From c5a7993266676665cc4626cca05e6bfe80d7d229 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Mon, 6 Nov 2023 23:13:57 +0800 Subject: [PATCH 1/8] Handle Vite 5 changes (#9005) --- .../src/core/preview/static-preview-server.ts | 6 ++++-- .../core/preview/vite-plugin-astro-preview.ts | 20 ++++++++++++++++--- .../astro/src/vite-plugin-scripts/index.ts | 4 +++- packages/astro/test/preview-routing.test.js | 7 ++++++- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/packages/astro/src/core/preview/static-preview-server.ts b/packages/astro/src/core/preview/static-preview-server.ts index 3cb0e89e8f03..937ba1c99a1e 100644 --- a/packages/astro/src/core/preview/static-preview-server.ts +++ b/packages/astro/src/core/preview/static-preview-server.ts @@ -54,7 +54,7 @@ export default async function createStaticPreviewServer( null, msg.serverStart({ startupTime: performance.now() - startServerTime, - resolvedUrls: previewServer.resolvedUrls, + resolvedUrls: previewServer.resolvedUrls ?? { local: [], network: [] }, host: settings.config.server.host, base: settings.config.base, }) @@ -72,7 +72,9 @@ export default async function createStaticPreviewServer( host: getResolvedHostForHttpServer(settings.config.server.host), port: settings.config.server.port, closed, - server: previewServer.httpServer, + // In Vite 5, `httpServer` may be a `Http2SecureServer`, but we know we are only starting a HTTP server + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + server: previewServer.httpServer as http.Server, stop: async () => { await new Promise((resolve, reject) => { previewServer.httpServer.destroy((err) => (err ? reject(err) : resolve(undefined))); diff --git a/packages/astro/src/core/preview/vite-plugin-astro-preview.ts b/packages/astro/src/core/preview/vite-plugin-astro-preview.ts index 1f9f7c86f0e5..b5bac4ed48ba 100644 --- a/packages/astro/src/core/preview/vite-plugin-astro-preview.ts +++ b/packages/astro/src/core/preview/vite-plugin-astro-preview.ts @@ -1,11 +1,13 @@ import fs from 'node:fs'; import { fileURLToPath } from 'node:url'; -import type { Plugin } from 'vite'; +import { version } from 'vite'; +import type { Plugin, Connect } from 'vite'; import type { AstroSettings } from '../../@types/astro.js'; import { notFoundTemplate, subpathNotUsedTemplate } from '../../template/4xx.js'; import { stripBase } from './util.js'; const HAS_FILE_EXTENSION_REGEXP = /^.*\.[^\\]+$/; +const IS_VITE_5 = version.startsWith('5.'); export function vitePluginAstroPreview(settings: AstroSettings): Plugin { const { base, outDir, trailingSlash } = settings.config; @@ -50,7 +52,7 @@ export function vitePluginAstroPreview(settings: AstroSettings): Plugin { }); return () => { - server.middlewares.use((req, res) => { + const fourOhFourMiddleware: Connect.NextHandleFunction = (req, res) => { const errorPagePath = fileURLToPath(outDir + '/404.html'); if (fs.existsSync(errorPagePath)) { res.statusCode = 404; @@ -61,7 +63,19 @@ export function vitePluginAstroPreview(settings: AstroSettings): Plugin { res.statusCode = 404; res.end(notFoundTemplate(pathname, 'Not Found')); } - }); + }; + + // Vite 5 has its own 404 middleware, we replace it with ours instead. + if (IS_VITE_5) { + for (const middleware of server.middlewares.stack) { + // This hardcoded name will not break between Vite versions + if ((middleware.handle as Connect.HandleFunction).name === 'vite404Middleware') { + middleware.handle = fourOhFourMiddleware; + } + } + } else { + server.middlewares.use(fourOhFourMiddleware); + } }; }, }; diff --git a/packages/astro/src/vite-plugin-scripts/index.ts b/packages/astro/src/vite-plugin-scripts/index.ts index d49d0e22f909..0066b98f5e70 100644 --- a/packages/astro/src/vite-plugin-scripts/index.ts +++ b/packages/astro/src/vite-plugin-scripts/index.ts @@ -50,7 +50,9 @@ export default function astroScriptsPlugin({ settings }: { settings: AstroSettin }, buildStart() { const hasHydrationScripts = settings.scripts.some((s) => s.stage === 'before-hydration'); - if (hasHydrationScripts && env?.command === 'build' && !env?.ssrBuild) { + // @ts-expect-error Vite 5 renamed `ssrBuild` to `isSsrBuild` + const isSsrBuild = env?.ssrBuild || env?.isSsrBuild; + if (hasHydrationScripts && env?.command === 'build' && !isSsrBuild) { this.emitFile({ type: 'chunk', id: BEFORE_HYDRATION_SCRIPT_ID, diff --git a/packages/astro/test/preview-routing.test.js b/packages/astro/test/preview-routing.test.js index 4c99881b6417..8a4653a72787 100644 --- a/packages/astro/test/preview-routing.test.js +++ b/packages/astro/test/preview-routing.test.js @@ -1,7 +1,12 @@ import { expect } from 'chai'; +import { version } from 'vite'; import { loadFixture } from './test-utils.js'; -describe('Preview Routing', () => { +const IS_VITE_5 = version.startsWith('5.'); + +// Skip in Vite 5 as it changes how HTML files are served. We may want to review aligning +// trailingSlash and build.format to avoid potential footguns in Astro 4 +(IS_VITE_5 ? describe.skip : describe)('Preview Routing', function () { describe('build format: directory', () => { describe('Subpath without trailing slash and trailingSlash: never', () => { /** @type {import('./test-utils').Fixture} */ From 4983acea8c7a1b2747ceea6a5d0b31003567d05a Mon Sep 17 00:00:00 2001 From: bluwy Date: Mon, 6 Nov 2023 15:15:47 +0000 Subject: [PATCH 2/8] [ci] format --- packages/astro/src/core/preview/vite-plugin-astro-preview.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/core/preview/vite-plugin-astro-preview.ts b/packages/astro/src/core/preview/vite-plugin-astro-preview.ts index b5bac4ed48ba..9faabe29b21e 100644 --- a/packages/astro/src/core/preview/vite-plugin-astro-preview.ts +++ b/packages/astro/src/core/preview/vite-plugin-astro-preview.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import { fileURLToPath } from 'node:url'; +import type { Connect, Plugin } from 'vite'; import { version } from 'vite'; -import type { Plugin, Connect } from 'vite'; import type { AstroSettings } from '../../@types/astro.js'; import { notFoundTemplate, subpathNotUsedTemplate } from '../../template/4xx.js'; import { stripBase } from './util.js'; From 35739d01e9cc4fa31a8b85201feecf29c747eca9 Mon Sep 17 00:00:00 2001 From: Martin Trapp <94928215+martrapp@users.noreply.github.com> Date: Mon, 6 Nov 2023 18:48:34 +0100 Subject: [PATCH 3/8] Fix DEV mode on Safari when view transitioning to client:only components (#9000) * Fix DEV mode on Safari when view transitioning to client:only components * Update .changeset/eighty-ladybugs-shake.md Co-authored-by: Sarah Rainsberger --------- Co-authored-by: Sarah Rainsberger --- .changeset/eighty-ladybugs-shake.md | 5 +++++ packages/astro/src/transitions/router.ts | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 .changeset/eighty-ladybugs-shake.md diff --git a/.changeset/eighty-ladybugs-shake.md b/.changeset/eighty-ladybugs-shake.md new file mode 100644 index 000000000000..e34d66c971d4 --- /dev/null +++ b/.changeset/eighty-ladybugs-shake.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes an error in dev mode on Safari where view transitions prevented navigating to pages with `client:only` components diff --git a/packages/astro/src/transitions/router.ts b/packages/astro/src/transitions/router.ts index af8a09d97643..da5496f81ff9 100644 --- a/packages/astro/src/transitions/router.ts +++ b/packages/astro/src/transitions/router.ts @@ -529,9 +529,8 @@ async function prepareForClientOnlyComponents(newDocument: Document, toLocation: if (newDocument.body.querySelector(`astro-island[client='only']`)) { // Load the next page with an empty module loader cache const nextPage = document.createElement('iframe'); - // do not fetch the file from the server, but use the current newDocument - nextPage.srcdoc = - (newDocument.doctype ? '' : '') + newDocument.documentElement.outerHTML; + // with srcdoc resolving imports does not work on webkit browsers + nextPage.src = toLocation.href; nextPage.style.display = 'none'; document.body.append(nextPage); // silence the iframe's console From 754e4fd31ce49eadb2cf4951e941a48d11d10e73 Mon Sep 17 00:00:00 2001 From: Arsh <69170106+lilnasy@users.noreply.github.com> Date: Tue, 7 Nov 2023 14:01:04 +0000 Subject: [PATCH 4/8] feat(vercel): streaming (#8879) --- .changeset/tricky-clocks-end.md | 7 +++++++ .../vercel/src/serverless/adapter.ts | 3 ++- .../test/fixtures/streaming/astro.config.mjs | 7 +++++++ .../test/fixtures/streaming/package.json | 10 +++++++++ .../fixtures/streaming/src/pages/one.astro | 8 +++++++ .../fixtures/streaming/src/pages/two.astro | 8 +++++++ .../vercel/test/static-assets.test.js | 15 ++++++------- .../vercel/test/streaming.test.js | 21 +++++++++++++++++++ pnpm-lock.yaml | 9 ++++++++ 9 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 .changeset/tricky-clocks-end.md create mode 100644 packages/integrations/vercel/test/fixtures/streaming/astro.config.mjs create mode 100644 packages/integrations/vercel/test/fixtures/streaming/package.json create mode 100644 packages/integrations/vercel/test/fixtures/streaming/src/pages/one.astro create mode 100644 packages/integrations/vercel/test/fixtures/streaming/src/pages/two.astro create mode 100644 packages/integrations/vercel/test/streaming.test.js diff --git a/.changeset/tricky-clocks-end.md b/.changeset/tricky-clocks-end.md new file mode 100644 index 000000000000..8b57899881cb --- /dev/null +++ b/.changeset/tricky-clocks-end.md @@ -0,0 +1,7 @@ +--- +'@astrojs/vercel': minor +--- + +The Vercel adapter now streams responses! + +This brings better performance to your visitors by showing them content as it is rendered. The browser can also start loading the required stylesheets and scripts much sooner, which ultimately results in faster full page loads. diff --git a/packages/integrations/vercel/src/serverless/adapter.ts b/packages/integrations/vercel/src/serverless/adapter.ts index ea7cd7e533f8..621849688ff3 100644 --- a/packages/integrations/vercel/src/serverless/adapter.ts +++ b/packages/integrations/vercel/src/serverless/adapter.ts @@ -341,7 +341,7 @@ interface CreateFunctionFolderArgs { NTF_CACHE: any; includeFiles: URL[]; excludeFiles?: string[]; - maxDuration?: number; + maxDuration: number | undefined; } async function createFunctionFolder({ @@ -381,6 +381,7 @@ async function createFunctionFolder({ handler, launcherType: 'Nodejs', maxDuration, + supportsResponseStreaming: true, }); } diff --git a/packages/integrations/vercel/test/fixtures/streaming/astro.config.mjs b/packages/integrations/vercel/test/fixtures/streaming/astro.config.mjs new file mode 100644 index 000000000000..b1a48b07df94 --- /dev/null +++ b/packages/integrations/vercel/test/fixtures/streaming/astro.config.mjs @@ -0,0 +1,7 @@ +import { defineConfig } from 'astro/config'; +import vercel from '@astrojs/vercel/serverless'; + +export default defineConfig({ + output: "server", + adapter: vercel() +}); diff --git a/packages/integrations/vercel/test/fixtures/streaming/package.json b/packages/integrations/vercel/test/fixtures/streaming/package.json new file mode 100644 index 000000000000..80068581cc33 --- /dev/null +++ b/packages/integrations/vercel/test/fixtures/streaming/package.json @@ -0,0 +1,10 @@ +{ + "name": "@test/vercel-streaming", + "version": "0.0.0", + "private": true, + "dependencies": { + "@astrojs/vercel": "workspace:*", + "astro": "workspace:*" + } +} + \ No newline at end of file diff --git a/packages/integrations/vercel/test/fixtures/streaming/src/pages/one.astro b/packages/integrations/vercel/test/fixtures/streaming/src/pages/one.astro new file mode 100644 index 000000000000..0c7fb90a735e --- /dev/null +++ b/packages/integrations/vercel/test/fixtures/streaming/src/pages/one.astro @@ -0,0 +1,8 @@ + + + One + + +

One

+ + diff --git a/packages/integrations/vercel/test/fixtures/streaming/src/pages/two.astro b/packages/integrations/vercel/test/fixtures/streaming/src/pages/two.astro new file mode 100644 index 000000000000..e7ba9910e2a6 --- /dev/null +++ b/packages/integrations/vercel/test/fixtures/streaming/src/pages/two.astro @@ -0,0 +1,8 @@ + + + Two + + +

Two

+ + diff --git a/packages/integrations/vercel/test/static-assets.test.js b/packages/integrations/vercel/test/static-assets.test.js index 7f360aebc70c..92e37c0af9ce 100644 --- a/packages/integrations/vercel/test/static-assets.test.js +++ b/packages/integrations/vercel/test/static-assets.test.js @@ -7,9 +7,10 @@ describe('Static Assets', () => { const VALID_CACHE_CONTROL = 'public, max-age=31536000, immutable'; - async function build({ adapter, assets }) { + async function build({ adapter, assets, output }) { fixture = await loadFixture({ root: './fixtures/static-assets/', + output, adapter, build: { assets, @@ -38,31 +39,31 @@ describe('Static Assets', () => { } describe('static adapter', async () => { - const adapter = await import('@astrojs/vercel/static'); + const { default: vercel } = await import('@astrojs/vercel/static'); it('has cache control', async () => { - await build({ adapter }); + await build({ adapter: vercel() }); checkValidCacheControl(); }); it('has cache control other assets', async () => { const assets = '_foo'; - await build({ adapter, assets }); + await build({ adapter: vercel(), assets }); checkValidCacheControl(assets); }); }); describe('serverless adapter', async () => { - const adapter = await import('@astrojs/vercel/serverless'); + const { default: vercel } = await import('@astrojs/vercel/serverless'); it('has cache control', async () => { - await build({ adapter }); + await build({ output: "server", adapter: vercel() }); checkValidCacheControl(); }); it('has cache control other assets', async () => { const assets = '_foo'; - await build({ adapter, assets }); + await build({ output: "server", adapter: vercel(), assets }); checkValidCacheControl(assets); }); }); diff --git a/packages/integrations/vercel/test/streaming.test.js b/packages/integrations/vercel/test/streaming.test.js new file mode 100644 index 000000000000..93dc95c39508 --- /dev/null +++ b/packages/integrations/vercel/test/streaming.test.js @@ -0,0 +1,21 @@ +import { loadFixture } from './test-utils.js'; +import { expect } from 'chai'; + +describe('maxDuration', () => { + /** @type {import('./test-utils.js').Fixture} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/streaming/', + }); + await fixture.build(); + }); + + it('makes it to vercel function configuration', async () => { + const vcConfig = JSON.parse( + await fixture.readFile('../.vercel/output/functions/render.func/.vc-config.json') + ); + expect(vcConfig).to.deep.include({ supportsResponseStreaming: true }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0bfaef637100..525adb49ac9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4731,6 +4731,15 @@ importers: specifier: workspace:* version: link:../../../../../astro + packages/integrations/vercel/test/fixtures/streaming: + dependencies: + '@astrojs/vercel': + specifier: workspace:* + version: link:../../.. + astro: + specifier: workspace:* + version: link:../../../../../astro + packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server: dependencies: '@astrojs/vercel': From 6e30bdc941e397afe3cd789bc018c6b1dd2b6675 Mon Sep 17 00:00:00 2001 From: lilnasy Date: Tue, 7 Nov 2023 14:03:45 +0000 Subject: [PATCH 5/8] [ci] format --- packages/integrations/vercel/test/static-assets.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/integrations/vercel/test/static-assets.test.js b/packages/integrations/vercel/test/static-assets.test.js index 92e37c0af9ce..606addfb3f13 100644 --- a/packages/integrations/vercel/test/static-assets.test.js +++ b/packages/integrations/vercel/test/static-assets.test.js @@ -57,13 +57,13 @@ describe('Static Assets', () => { const { default: vercel } = await import('@astrojs/vercel/serverless'); it('has cache control', async () => { - await build({ output: "server", adapter: vercel() }); + await build({ output: 'server', adapter: vercel() }); checkValidCacheControl(); }); it('has cache control other assets', async () => { const assets = '_foo'; - await build({ output: "server", adapter: vercel(), assets }); + await build({ output: 'server', adapter: vercel(), assets }); checkValidCacheControl(assets); }); }); From d979b8f0a82c12f2a844c429982207c88fe13ae6 Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Wed, 8 Nov 2023 08:37:35 +0100 Subject: [PATCH 6/8] feat(overlay): Match the design's animations, shadows and general styling (#9014) --- .changeset/rude-eggs-hunt.md | 5 + packages/astro/src/@types/astro.ts | 1 + .../runtime/client/dev-overlay/entrypoint.ts | 4 +- .../src/runtime/client/dev-overlay/overlay.ts | 80 +++++++---- .../client/dev-overlay/plugins/astro.ts | 58 ++++++-- .../client/dev-overlay/plugins/audit.ts | 124 +++++++++++++++--- .../client/dev-overlay/plugins/xray.ts | 26 ++++ .../client/dev-overlay/ui-library/card.ts | 2 + .../client/dev-overlay/ui-library/icons.ts | 5 + .../client/dev-overlay/ui-library/tooltip.ts | 1 + .../client/dev-overlay/ui-library/window.ts | 1 + 11 files changed, 251 insertions(+), 56 deletions(-) create mode 100644 .changeset/rude-eggs-hunt.md diff --git a/.changeset/rude-eggs-hunt.md b/.changeset/rude-eggs-hunt.md new file mode 100644 index 000000000000..4bbe7c9c9004 --- /dev/null +++ b/.changeset/rude-eggs-hunt.md @@ -0,0 +1,5 @@ +--- +"astro": patch +--- + +Add animations, shadows and general styling tweaks to the Dev Overlay to better match the intended design. diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 4ae5e7138656..dfeb7ee80f68 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -2318,6 +2318,7 @@ export interface DevOverlayPlugin { name: string; icon: Icon; init?(canvas: ShadowRoot, eventTarget: EventTarget): void | Promise; + beforeTogglingOff?(canvas: ShadowRoot): boolean | Promise; } export type DevOverlayMetadata = Window & diff --git a/packages/astro/src/runtime/client/dev-overlay/entrypoint.ts b/packages/astro/src/runtime/client/dev-overlay/entrypoint.ts index 61c5e779bf6c..7336f9d06673 100644 --- a/packages/astro/src/runtime/client/dev-overlay/entrypoint.ts +++ b/packages/astro/src/runtime/client/dev-overlay/entrypoint.ts @@ -63,13 +63,13 @@ document.addEventListener('DOMContentLoaded', async () => { target.querySelector('.notification')?.toggleAttribute('data-active', newState); }); - eventTarget.addEventListener('toggle-plugin', (evt) => { + eventTarget.addEventListener('toggle-plugin', async (evt) => { let newState = undefined; if (evt instanceof CustomEvent) { newState = evt.detail.state ?? true; } - overlay.togglePluginStatus(plugin, newState); + await overlay.togglePluginStatus(plugin, newState); }); return plugin; diff --git a/packages/astro/src/runtime/client/dev-overlay/overlay.ts b/packages/astro/src/runtime/client/dev-overlay/overlay.ts index 50b015ca12bd..e0ab02e481a7 100644 --- a/packages/astro/src/runtime/client/dev-overlay/overlay.ts +++ b/packages/astro/src/runtime/client/dev-overlay/overlay.ts @@ -51,7 +51,7 @@ export class AstroDevOverlay extends HTMLElement { display: flex; gap: 8px; align-items: center; - transition: bottom 0.2s ease-in-out; + transition: bottom 0.35s cubic-bezier(0.485, -0.050, 0.285, 1.505); pointer-events: none; } @@ -72,11 +72,10 @@ export class AstroDevOverlay extends HTMLElement { height: 56px; overflow: hidden; pointer-events: auto; - background: linear-gradient(180deg, #13151A 0%, rgba(19, 21, 26, 0.88) 100%); - box-shadow: 0px 0px 0px 0px #13151A4D; border: 1px solid #343841; border-radius: 9999px; + box-shadow: 0px 0px 0px 0px rgba(19, 21, 26, 0.30), 0px 1px 2px 0px rgba(19, 21, 26, 0.29), 0px 4px 4px 0px rgba(19, 21, 26, 0.26), 0px 10px 6px 0px rgba(19, 21, 26, 0.15), 0px 17px 7px 0px rgba(19, 21, 26, 0.04), 0px 26px 7px 0px rgba(19, 21, 26, 0.01); } #dev-bar .item { @@ -187,16 +186,6 @@ export class AstroDevOverlay extends HTMLElement { width: 1px; } - astro-dev-overlay-plugin-canvas { - position: absolute; - top: 0; - left: 0; - } - - astro-dev-overlay-plugin-canvas:not([data-active]) { - display: none; - } - #minimize-button { width: 32px; height: 32px; @@ -263,7 +252,7 @@ export class AstroDevOverlay extends HTMLElement { } // Create plugin canvases - this.plugins.forEach((plugin) => { + this.plugins.forEach(async (plugin) => { if (!this.hasBeenInitialized) { console.log(`Creating plugin canvas for ${plugin.id}`); const pluginCanvas = document.createElement('astro-dev-overlay-plugin-canvas'); @@ -271,7 +260,7 @@ export class AstroDevOverlay extends HTMLElement { this.shadowRoot?.append(pluginCanvas); } - this.togglePluginStatus(plugin, plugin.active); + await this.togglePluginStatus(plugin, plugin.active); }); // Init plugin lazily - This is safe to do here because only plugins that are not initialized yet will be affected @@ -306,7 +295,7 @@ export class AstroDevOverlay extends HTMLElement { await this.initPlugin(plugin); } - this.togglePluginStatus(plugin); + await this.togglePluginStatus(plugin); }); }); @@ -418,30 +407,52 @@ export class AstroDevOverlay extends HTMLElement { } getPluginCanvasById(id: string) { - return this.shadowRoot.querySelector(`astro-dev-overlay-plugin-canvas[data-plugin-id="${id}"]`); + return this.shadowRoot.querySelector( + `astro-dev-overlay-plugin-canvas[data-plugin-id="${id}"]` + ); } - togglePluginStatus(plugin: DevOverlayPlugin, status?: boolean) { - plugin.active = status ?? !plugin.active; + /** + * @param plugin The plugin to toggle the status of + * @param newStatus Optionally, force the plugin into a specific state + */ + async togglePluginStatus(plugin: DevOverlayPlugin, newStatus?: boolean) { + const pluginCanvas = this.getPluginCanvasById(plugin.id); + if (!pluginCanvas) return; + + if (plugin.active && !newStatus && plugin.beforeTogglingOff) { + const shouldToggleOff = await plugin.beforeTogglingOff(pluginCanvas.shadowRoot!); + + // If the plugin returned false, don't toggle it off, maybe the plugin showed a confirmation dialog or similar + if (!shouldToggleOff) return; + } + + plugin.active = newStatus ?? !plugin.active; const target = this.shadowRoot.querySelector(`[data-plugin-id="${plugin.id}"]`); if (!target) return; target.classList.toggle('active', plugin.active); - this.getPluginCanvasById(plugin.id)?.toggleAttribute('data-active', plugin.active); - - plugin.eventTarget.dispatchEvent( - new CustomEvent('plugin-toggled', { - detail: { - state: plugin.active, - plugin, - }, - }) - ); + pluginCanvas.style.display = plugin.active ? 'block' : 'none'; + + window.requestAnimationFrame(() => { + pluginCanvas.toggleAttribute('data-active', plugin.active); + plugin.eventTarget.dispatchEvent( + new CustomEvent('plugin-toggled', { + detail: { + state: plugin.active, + plugin, + }, + }) + ); + }); if (import.meta.hot) { import.meta.hot.send(`${WS_EVENT_NAME}:${plugin.id}:toggled`, { state: plugin.active }); } } + /** + * @param newStatus Optionally, force the minimize button into a specific state + */ toggleMinimizeButton(newStatus?: boolean) { const minimizeButton = this.shadowRoot.querySelector('#minimize-button'); if (!minimizeButton) return; @@ -493,4 +504,15 @@ export class DevOverlayCanvas extends HTMLElement { super(); this.shadowRoot = this.attachShadow({ mode: 'open' }); } + + connectedCallback() { + this.shadowRoot.innerHTML = ` + `; + } } diff --git a/packages/astro/src/runtime/client/dev-overlay/plugins/astro.ts b/packages/astro/src/runtime/client/dev-overlay/plugins/astro.ts index cc83cbe83931..ea3b7f26fc1f 100644 --- a/packages/astro/src/runtime/client/dev-overlay/plugins/astro.ts +++ b/packages/astro/src/runtime/client/dev-overlay/plugins/astro.ts @@ -5,12 +5,42 @@ export default { name: 'Astro', icon: 'astro:logo', init(canvas) { - const astroWindow = document.createElement('astro-dev-overlay-window'); + createWindow(); - astroWindow.windowTitle = 'Astro'; - astroWindow.windowIcon = 'astro:logo'; + document.addEventListener('astro:after-swap', createWindow); - astroWindow.innerHTML = ` + function createWindow() { + const style = document.createElement('style'); + style.textContent = ` + :host { + opacity: 0; + transition: opacity 0.15s ease-in-out; + } + + :host([data-active]) { + opacity: 1; + } + + @media screen and (prefers-reduced-motion: no-preference) { + :host astro-dev-overlay-window { + transform: translateY(55px) translate(-50%, -50%); + transition: transform 0.15s ease-in-out; + transform-origin: center bottom; + } + + :host([data-active]) astro-dev-overlay-window { + transform: translateY(0) translate(-50%, -50%); + } + } + `; + canvas.append(style); + + const astroWindow = document.createElement('astro-dev-overlay-window'); + + astroWindow.windowTitle = 'Astro'; + astroWindow.windowIcon = 'astro:logo'; + + astroWindow.innerHTML = `