Skip to content

[pull] main from TryGhost:main#1189

Merged
pull[bot] merged 4 commits into
code:mainfrom
TryGhost:main
May 28, 2026
Merged

[pull] main from TryGhost:main#1189
pull[bot] merged 4 commits into
code:mainfrom
TryGhost:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 28, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

tryghost-renovate Bot and others added 4 commits May 28, 2026 04:22
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
|
[@types/jsdom](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/jsdom)
([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jsdom))
| [`28.0.1` →
`28.0.3`](https://renovatebot.com/diffs/npm/@types%2fjsdom/28.0.1/28.0.3)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fjsdom/28.0.3?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fjsdom/28.0.1/28.0.3?slim=true)
|
|
[@types/node](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node)
([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node))
| [`22.19.18` →
`22.19.19`](https://renovatebot.com/diffs/npm/@types%2fnode/22.19.18/22.19.19)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fnode/22.19.19?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fnode/22.19.18/22.19.19?slim=true)
|
|
[@types/react](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react)
([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react))
| [`18.3.28` →
`18.3.29`](https://renovatebot.com/diffs/npm/@types%2freact/18.3.28/18.3.29)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2freact/18.3.29?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2freact/18.3.28/18.3.29?slim=true)
|

---

### Configuration

📅 **Schedule**: (in timezone Etc/UTC)

- Branch creation
  - Only on Sunday and Saturday (`* * * * 0,6`)
  - Between 12:00 AM and 12:59 PM, only on Monday (`* 0-12 * * 1`)
- Between 09:00 PM and 11:59 PM, Monday through Friday (`* 21-23 * *
1-5`)
- Between 12:00 AM and 04:59 AM, Tuesday through Saturday (`* 0-4 * *
2-6`)
- Automerge
  - Only on Sunday and Saturday (`* * * * 0,6`)
  - Between 12:00 AM and 12:59 PM, only on Monday (`* 0-12 * * 1`)
- Between 10:00 PM and 11:59 PM, Monday through Friday (`* 22-23 * *
1-5`)
- Between 12:00 AM and 04:59 AM, Tuesday through Saturday (`* 0-4 * *
2-6`)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTUuOCIsInVwZGF0ZWRJblZlciI6IjQzLjE5NS44IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: tryghost-renovate[bot] <269725441+tryghost-renovate[bot]@users.noreply.github.com>
closes [ONC-1765](https://linear.app/ghost/issue/ONC-1765/oss-issue-update-button-incorrectly-enabled-on-load-when-feature-image]
closes #28094

The feature image caption editor was writing Lexical hydration markup back to `featureImageCaption` when API-created posts with plain text captions were opened, which marked the post as dirty before any user edit. Normalizing generated plain-text spans before comparing caption HTML keeps the loaded record clean while preserving real caption edits, and the regression test verifies the `Update` button remains disabled on load.
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@playwright/test](https://playwright.dev)
([source](https://redirect.github.com/microsoft/playwright)) | [`1.59.1`
→
`1.60.0`](https://renovatebot.com/diffs/npm/@playwright%2ftest/1.59.1/1.60.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@playwright%2ftest/1.60.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@playwright%2ftest/1.59.1/1.60.0?slim=true)
|

---

### Release Notes

<details>
<summary>microsoft/playwright (@&#8203;playwright/test)</summary>

###
[`v1.60.0`](https://redirect.github.com/microsoft/playwright/releases/tag/v1.60.0)

[Compare
Source](https://redirect.github.com/microsoft/playwright/compare/v1.59.1...v1.60.0)

#### 🌐 HAR recording on Tracing


[tracing.startHar()](https://playwright.dev/docs/api/class-tracing#tracing-start-har)
/
[tracing.stopHar()](https://playwright.dev/docs/api/class-tracing#tracing-stop-har)
expose HAR recording as a first-class tracing API, with the same
`content`, `mode` and `urlFilter` options as `recordHar`. The returned
[Disposable](https://playwright.dev/docs/api/class-disposable) makes it
easy to scope a recording with `await using`:

```js
await using har = await context.tracing.startHar('trace.har');
const page = await context.newPage();
await page.goto('https://playwright.dev');
// HAR is finalized when `har` goes out of scope.
```

#### 🪝 Drop API

New
[locator.drop()](https://playwright.dev/docs/api/class-locator#locator-drop)
simulates an external drag-and-drop of files or clipboard-like data onto
an element. Playwright dispatches `dragenter`, `dragover`, and `drop`
with a synthetic \[DataTransfer] in the page context — works
cross-browser and is great for testing upload zones:

```js
await page.locator('#dropzone').drop({
  files: { name: 'note.txt', mimeType: 'text/plain', buffer: Buffer.from('hello') },
});

await page.locator('#dropzone').drop({
  data: {
    'text/plain': 'hello world',
    'text/uri-list': 'https://example.com',
  },
});
```

#### 🎯 Aria snapshots

-
[expect(page).toMatchAriaSnapshot()](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-match-aria-snapshot)
now works on a [Page](https://playwright.dev/docs/api/class-page), in
addition to a [Locator](https://playwright.dev/docs/api/class-locator) —
equivalent to asserting against `page.locator('body')`.
- New `boxes` option on
[locator.ariaSnapshot()](https://playwright.dev/docs/api/class-locator#locator-aria-snapshot)
/
[page.ariaSnapshot()](https://playwright.dev/docs/api/class-page#page-aria-snapshot)
appends each element's bounding box as `[box=x,y,width,height]`, useful
for AI consumption.

#### 🛑 test.abort()

New
[test.abort()](https://playwright.dev/docs/api/class-test#test-abort)
aborts the currently running test from a fixture, hook, or route handler
with an optional message. Use it when you have detected an unrecoverable
misuse and want to fail the test right away:

```js
test('does not publish to the shared page', async ({ page }) => {
  await page.route('**/publish', route => {
    test.abort('Tests must not publish to the shared page. Use the `clone` option.');
    return route.abort();
  });
  // ...
});
```

#### New APIs

##### Browser, Context and Page

- Event
[browser.on('context')](https://playwright.dev/docs/api/class-browser#browser-event-context)
— fired when a new context is created on the browser.
- [BrowserContext](https://playwright.dev/docs/api/class-browsercontext)
now mirrors lifecycle events from its pages:
[browserContext.on('download')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-download),
[browserContext.on('frameattached')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-frame-attached),
[browserContext.on('framedetached')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-frame-detached),
[browserContext.on('framenavigated')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-frame-navigated),
[browserContext.on('pageclose')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-page-close),
[browserContext.on('pageload')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-page-load).

##### Locators and Assertions

- New option `description` in
[page.getByRole()](https://playwright.dev/docs/api/class-page#page-get-by-role)
/
[locator.getByRole()](https://playwright.dev/docs/api/class-locator#locator-get-by-role)
/
[frame.getByRole()](https://playwright.dev/docs/api/class-frame#frame-get-by-role)
/
[frameLocator.getByRole()](https://playwright.dev/docs/api/class-framelocator#frame-locator-get-by-role)
for matching the [accessible
description](https://www.w3.org/TR/wai-aria-1.2/#dfn-accessible-description).
- New option `pseudo` in
[expect(locator).toHaveCSS()](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-css)
reads computed styles from `::before` or `::after`.
- New option `style` in
[locator.highlight()](https://playwright.dev/docs/api/class-locator#locator-highlight)
applies extra inline CSS to the highlight overlay, plus new
[page.hideHighlight()](https://playwright.dev/docs/api/class-page#page-hide-highlight)
to clear all highlights.

##### Network

-
[webSocketRoute.protocols()](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-protocols)
returns the WebSocket subprotocols requested by the page.
- New option `noDefaults` in
[browserType.connectOverCDP()](https://playwright.dev/docs/api/class-browsertype#browser-type-connect-over-cdp)
disables Playwright's default overrides on the default context (download
behavior, focus emulation, media emulation), so attaching to a user's
daily-driver browser doesn't disturb its state.

##### Errors and Reporting

- New
[webError.location()](https://playwright.dev/docs/api/class-weberror#web-error-location)
mirrors
[consoleMessage.location()](https://playwright.dev/docs/api/class-consolemessage#console-message-location).
-
[consoleMessage.location()](https://playwright.dev/docs/api/class-consolemessage#console-message-location)
now exposes `line` / `column` properties (`lineNumber` / `columnNumber`
are deprecated).
- New
[testInfoError.errorContext](https://playwright.dev/docs/api/class-testinfoerror#test-info-error-error-context)
surfaces additional diagnostic context, such as the aria snapshot of the
receiver at the time of an `expect(...)` matcher failure.
-
[reporter.onError()](https://playwright.dev/docs/api/class-reporter#reporter-on-error)
now receives a `workerInfo` argument with details about the worker for
fixture teardown errors.

##### Test runner

- New `{testFileBaseName}` token in
[testProject.snapshotPathTemplate](https://playwright.dev/docs/api/class-testproject#test-project-snapshot-path-template)
— file name without extension.
- Test runner now errors when a config tries to override a non-option
fixture, and rejects `workers: 0` or negative values.

#### 🛠️ Other improvements

- HTML reporter:
- `npx playwright show-report` accepts `.zip` files directly — no need
to unzip first.
- Steps that contain attachments inside nested children show an
indicator on the parent step.
  - The `repeatEachIndex` is shown in the test header when non-zero.
- Trace Viewer adds a pretty-print toggle for JSON / form request and
response bodies in the network details panel.

#### Breaking Changes ⚠️

- Removed long-deprecated APIs:
- `Locator.ariaRef()` — use the standard
[locator.ariaSnapshot()](https://playwright.dev/docs/api/class-locator#locator-aria-snapshot)
pipeline.
- `handle` option on `BrowserContext.exposeBinding` and
`Page.exposeBinding`.
- `logger` option on `BrowserType.connect` and
`BrowserType.connectOverCDP` — use
[tracing](https://playwright.dev/docs/trace-viewer) instead.
- Context options `videosPath` / `videoSize` — use `recordVideo`
instead.

#### Browser Versions

- Chromium 148.0.7778.96
- Mozilla Firefox 150.0.2
- WebKit 26.4

This version was also tested against the following stable channels:

- Google Chrome 147
- Microsoft Edge 147

</details>

---

### Configuration

📅 **Schedule**: (in timezone Etc/UTC)

- Branch creation
  - Only on Sunday and Saturday (`* * * * 0,6`)
  - Between 12:00 AM and 12:59 PM, only on Monday (`* 0-12 * * 1`)
- Between 09:00 PM and 11:59 PM, Monday through Friday (`* 21-23 * *
1-5`)
- Between 12:00 AM and 04:59 AM, Tuesday through Saturday (`* 0-4 * *
2-6`)
- Automerge
  - Only on Sunday and Saturday (`* * * * 0,6`)
  - Between 12:00 AM and 12:59 PM, only on Monday (`* 0-12 * * 1`)
- Between 10:00 PM and 11:59 PM, Monday through Friday (`* 22-23 * *
1-5`)
- Between 12:00 AM and 04:59 AM, Tuesday through Saturday (`* 0-4 * *
2-6`)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTUuOCIsInVwZGF0ZWRJblZlciI6IjQzLjE5NS44IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: tryghost-renovate[bot] <269725441+tryghost-renovate[bot]@users.noreply.github.com>
Staff members viewing the frontend of their Ghost site now see a
floating toolbar with quick links back to the editor, settings, and
analytics for the current page. The toolbar is staff-only and invisible
to normal visitors — public pages remain fully cacheable.

**Activation model**

Admin "view site" links append `?admin=1`. Frontend middleware creates a
short-lived HMAC-signed marker cookie on the site domain and redirects
to the clean URL. The cookie contains no session data or PII — it only
signals that staff tooling should load. On subsequent requests the
middleware validates the marker and sets a response local that
`ghost_head` checks before emitting the script tag.

The browser bundle independently verifies the real staff session through
the existing `/ghost/auth-frame/` bridge before rendering anything. This
two-layer approach means the server never embeds staff-specific data in
HTML, so CDN and theme caching are unaffected.

**Suppression inside Admin**

Admin's "view site" iframe passes `?admin=1&admin_toolbar=0`. The
middleware seeds the marker cookie (so the toolbar works on normal
frontend visits) but suppresses injection for that response. Suppression
triggers on the explicit query parameter and on `Sec-Fetch-Dest: iframe`.
Theme and announcement-bar previews, which use `fetch()` rather than
iframes, append `?admin_toolbar=0` to their request URLs. The toolbar
script also checks the parameter client-side as a safety net.

**Package boundary**

The toolbar lives in `apps/admin-toolbar` as a self-contained package
with its own source, Vite build, tests, and UMD artifact. It uses Preact 
(~3 KB) instead of React (~40 KB) since it is a lightweight public-facing
widget that only needs basic rendering — a rationale that could apply 
to other small public scripts where bundle size matters more than 
ecosystem compatibility. It renders inside Shadow DOM so theme CSS 
cannot affect it.

In production the script is served from jsDelivr via the `adminToolbar`
config in `defaults.json`, following the same CDN pattern as portal,
comments-ui, search, and announcement-bar. In development the Docker
setup proxies through Caddy to a local Vite preview server.

---

Co-authored-by: Hannah Wolfe <github.erisds@gmail.com>
@pull pull Bot locked and limited conversation to collaborators May 28, 2026
@pull pull Bot added the ⤵️ pull label May 28, 2026
@pull pull Bot merged commit 8bdc9fe into code:main May 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants