0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-03-31 23:31:30 -05:00

refactor(toolbar): Rename every internal reference of overlay/plugins to toolbar/apps (#9647)

* refactor(toolbar): Rename every internal reference of overlay/plugins to toolbar/apps

* refactor: rename vite plugin

* fix: update import

* nit: add setting fallback
This commit is contained in:
Erika 2024-01-11 14:03:45 -05:00 committed by GitHub
parent 74008cc238
commit 37e1018b0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 373 additions and 386 deletions

View file

@ -2,7 +2,7 @@ import { expect } from '@playwright/test';
import { testFactory } from './test-utils.js';
const test = testFactory({
root: './fixtures/dev-overlay/',
root: './fixtures/dev-toolbar/',
});
let devServer;
@ -15,54 +15,50 @@ test.afterAll(async () => {
await devServer.stop();
});
test.describe('Dev Overlay', () => {
test('dev overlay exists in the page', async ({ page, astro }) => {
test.describe('Dev Toolbar', () => {
test('dev toolbar exists in the page', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/'));
const devToolbar = page.locator('astro-dev-toolbar');
await expect(devToolbar).toHaveCount(1);
});
test('shows plugin name on hover', async ({ page, astro }) => {
test('shows app name on hover', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/'));
const overlay = page.locator('astro-dev-toolbar');
const pluginButton = overlay.locator('button[data-plugin-id="astro"]');
const pluginButtonTooltip = pluginButton.locator('.item-tooltip');
await pluginButton.hover();
const toolbar = page.locator('astro-dev-toolbar');
const appButton = toolbar.locator('button[data-app-id="astro"]');
const appButtonTooltip = appButton.locator('.item-tooltip');
await appButton.hover();
await expect(pluginButtonTooltip).toBeVisible();
await expect(appButtonTooltip).toBeVisible();
});
test('can open Astro plugin', async ({ page, astro }) => {
test('can open Astro app', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/'));
const overlay = page.locator('astro-dev-toolbar');
const pluginButton = overlay.locator('button[data-plugin-id="astro"]');
await pluginButton.click();
const toolbar = page.locator('astro-dev-toolbar');
const appButton = toolbar.locator('button[data-app-id="astro"]');
await appButton.click();
const astroPluginCanvas = overlay.locator(
'astro-dev-toolbar-plugin-canvas[data-plugin-id="astro"]'
);
const astroWindow = astroPluginCanvas.locator('astro-dev-toolbar-window');
const astroAppCanvas = toolbar.locator('astro-dev-toolbar-app-canvas[data-app-id="astro"]');
const astroWindow = astroAppCanvas.locator('astro-dev-toolbar-window');
await expect(astroWindow).toHaveCount(1);
await expect(astroWindow).toBeVisible();
// Toggle plugin off
await pluginButton.click();
// Toggle app off
await appButton.click();
await expect(astroWindow).not.toBeVisible();
});
test('xray shows highlights and tooltips', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/'));
const overlay = page.locator('astro-dev-toolbar');
const pluginButton = overlay.locator('button[data-plugin-id="astro:xray"]');
await pluginButton.click();
const toolbar = page.locator('astro-dev-toolbar');
const appButton = toolbar.locator('button[data-app-id="astro:xray"]');
await appButton.click();
const xrayCanvas = overlay.locator(
'astro-dev-toolbar-plugin-canvas[data-plugin-id="astro:xray"]'
);
const xrayCanvas = toolbar.locator('astro-dev-toolbar-app-canvas[data-app-id="astro:xray"]');
const xrayHighlight = xrayCanvas.locator('astro-dev-toolbar-highlight');
await expect(xrayHighlight).toBeVisible();
@ -70,8 +66,8 @@ test.describe('Dev Overlay', () => {
const xrayHighlightTooltip = xrayHighlight.locator('astro-dev-toolbar-tooltip');
await expect(xrayHighlightTooltip).toBeVisible();
// Toggle plugin off
await pluginButton.click();
// Toggle app off
await appButton.click();
await expect(xrayHighlight).not.toBeVisible();
await expect(xrayHighlightTooltip).not.toBeVisible();
});
@ -79,13 +75,11 @@ test.describe('Dev Overlay', () => {
test('xray shows no islands message when there are none', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/xray-no-islands'));
const overlay = page.locator('astro-dev-toolbar');
const pluginButton = overlay.locator('button[data-plugin-id="astro:xray"]');
await pluginButton.click();
const toolbar = page.locator('astro-dev-toolbar');
const appButton = toolbar.locator('button[data-app-id="astro:xray"]');
await appButton.click();
const xrayCanvas = overlay.locator(
'astro-dev-toolbar-plugin-canvas[data-plugin-id="astro:xray"]'
);
const xrayCanvas = toolbar.locator('astro-dev-toolbar-app-canvas[data-app-id="astro:xray"]');
const auditHighlight = xrayCanvas.locator('astro-dev-toolbar-highlight');
await expect(auditHighlight).not.toBeVisible();
@ -99,13 +93,11 @@ test.describe('Dev Overlay', () => {
test('audit shows higlights and tooltips', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/'));
const overlay = page.locator('astro-dev-toolbar');
const pluginButton = overlay.locator('button[data-plugin-id="astro:audit"]');
await pluginButton.click();
const toolbar = page.locator('astro-dev-toolbar');
const appButton = toolbar.locator('button[data-app-id="astro:audit"]');
await appButton.click();
const auditCanvas = overlay.locator(
'astro-dev-toolbar-plugin-canvas[data-plugin-id="astro:audit"]'
);
const auditCanvas = toolbar.locator('astro-dev-toolbar-app-canvas[data-app-id="astro:audit"]');
const auditHighlight = auditCanvas.locator('astro-dev-toolbar-highlight');
await expect(auditHighlight).toBeVisible();
@ -113,8 +105,8 @@ test.describe('Dev Overlay', () => {
const auditHighlightTooltip = auditHighlight.locator('astro-dev-toolbar-tooltip');
await expect(auditHighlightTooltip).toBeVisible();
// Toggle plugin off
await pluginButton.click();
// Toggle app off
await appButton.click();
await expect(auditHighlight).not.toBeVisible();
await expect(auditHighlightTooltip).not.toBeVisible();
});
@ -122,13 +114,11 @@ test.describe('Dev Overlay', () => {
test('audit shows no issues message when there are no issues', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/audit-no-warning'));
const overlay = page.locator('astro-dev-toolbar');
const pluginButton = overlay.locator('button[data-plugin-id="astro:audit"]');
await pluginButton.click();
const toolbar = page.locator('astro-dev-toolbar');
const appButton = toolbar.locator('button[data-app-id="astro:audit"]');
await appButton.click();
const auditCanvas = overlay.locator(
'astro-dev-toolbar-plugin-canvas[data-plugin-id="astro:audit"]'
);
const auditCanvas = toolbar.locator('astro-dev-toolbar-app-canvas[data-app-id="astro:audit"]');
const auditHighlight = auditCanvas.locator('astro-dev-toolbar-highlight');
await expect(auditHighlight).not.toBeVisible();
@ -142,13 +132,11 @@ test.describe('Dev Overlay', () => {
test('adjusts tooltip position if off-screen', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/tooltip-position'));
const overlay = page.locator('astro-dev-toolbar');
const pluginButton = overlay.locator('button[data-plugin-id="astro:audit"]');
await pluginButton.click();
const toolbar = page.locator('astro-dev-toolbar');
const appButton = toolbar.locator('button[data-app-id="astro:audit"]');
await appButton.click();
const auditCanvas = overlay.locator(
'astro-dev-toolbar-plugin-canvas[data-plugin-id="astro:audit"]'
);
const auditCanvas = toolbar.locator('astro-dev-toolbar-app-canvas[data-app-id="astro:audit"]');
const auditHighlights = auditCanvas.locator('astro-dev-toolbar-highlight');
for (const highlight of await auditHighlights.all()) {
await expect(highlight).toBeVisible();
@ -165,68 +153,66 @@ test.describe('Dev Overlay', () => {
}
});
test('can open Settings plugin', async ({ page, astro }) => {
test('can open Settings app', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/'));
const overlay = page.locator('astro-dev-toolbar');
const pluginButton = overlay.locator('button[data-plugin-id="astro:settings"]');
await pluginButton.click();
const toolbar = page.locator('astro-dev-toolbar');
const appButton = toolbar.locator('button[data-app-id="astro:settings"]');
await appButton.click();
const settingsPluginCanvas = overlay.locator(
'astro-dev-toolbar-plugin-canvas[data-plugin-id="astro:settings"]'
const settingsAppCanvas = toolbar.locator(
'astro-dev-toolbar-app-canvas[data-app-id="astro:settings"]'
);
const settingsWindow = settingsPluginCanvas.locator('astro-dev-toolbar-window');
const settingsWindow = settingsAppCanvas.locator('astro-dev-toolbar-window');
await expect(settingsWindow).toHaveCount(1);
await expect(settingsWindow).toBeVisible();
// Toggle plugin off
await pluginButton.click();
// Toggle app off
await appButton.click();
await expect(settingsWindow).not.toBeVisible();
});
test('Opening a plugin closes the currently opened plugin', async ({ page, astro }) => {
test('Opening a app closes the currently opened app', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/'));
const overlay = page.locator('astro-dev-toolbar');
let pluginButton = overlay.locator('button[data-plugin-id="astro:settings"]');
await pluginButton.click();
const toolbar = page.locator('astro-dev-toolbar');
let appButton = toolbar.locator('button[data-app-id="astro:settings"]');
await appButton.click();
const settingsPluginCanvas = overlay.locator(
'astro-dev-toolbar-plugin-canvas[data-plugin-id="astro:settings"]'
const settingsAppCanvas = toolbar.locator(
'astro-dev-toolbar-app-canvas[data-app-id="astro:settings"]'
);
const settingsWindow = settingsPluginCanvas.locator('astro-dev-toolbar-window');
const settingsWindow = settingsAppCanvas.locator('astro-dev-toolbar-window');
await expect(settingsWindow).toHaveCount(1);
await expect(settingsWindow).toBeVisible();
// Click the astro plugin
pluginButton = overlay.locator('button[data-plugin-id="astro"]');
await pluginButton.click();
// Click the astro app
appButton = toolbar.locator('button[data-app-id="astro"]');
await appButton.click();
const astroPluginCanvas = overlay.locator(
'astro-dev-toolbar-plugin-canvas[data-plugin-id="astro"]'
);
const astroWindow = astroPluginCanvas.locator('astro-dev-toolbar-window');
const astroAppCanvas = toolbar.locator('astro-dev-toolbar-app-canvas[data-app-id="astro"]');
const astroWindow = astroAppCanvas.locator('astro-dev-toolbar-window');
await expect(astroWindow).toHaveCount(1);
await expect(astroWindow).toBeVisible();
await expect(settingsWindow).not.toBeVisible();
});
test('Settings plugin contains message on disabling the overlay', async ({ page, astro }) => {
test('Settings app contains message on disabling the toolbar', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/'));
const overlay = page.locator('astro-dev-toolbar');
let pluginButton = overlay.locator('button[data-plugin-id="astro:settings"]');
await pluginButton.click();
const toolbar = page.locator('astro-dev-toolbar');
let appButton = toolbar.locator('button[data-app-id="astro:settings"]');
await appButton.click();
const settingsPluginCanvas = overlay.locator(
'astro-dev-toolbar-plugin-canvas[data-plugin-id="astro:settings"]'
const settingsAppCanvas = toolbar.locator(
'astro-dev-toolbar-app-canvas[data-app-id="astro:settings"]'
);
const settingsWindow = settingsPluginCanvas.locator('astro-dev-toolbar-window');
const settingsWindow = settingsAppCanvas.locator('astro-dev-toolbar-window');
await expect(settingsWindow).toHaveCount(1);
await expect(settingsWindow).toBeVisible();
const hideOverlay = settingsWindow.getByRole('heading', { name: 'Hide toolbar' });
await expect(hideOverlay).toBeVisible();
const hideToolbar = settingsWindow.getByRole('heading', { name: 'Hide toolbar' });
await expect(hideToolbar).toBeVisible();
});
});

View file

@ -1,5 +1,5 @@
{
"name": "@e2e/dev-overlay",
"name": "@e2e/dev-toolbar",
"version": "0.0.0",
"private": true,
"dependencies": {

View file

@ -21,18 +21,18 @@ import type { TSConfig } from '../core/config/tsconfig.js';
import type { AstroCookies } from '../core/cookies/index.js';
import type { AstroIntegrationLogger, Logger, LoggerLevel } from '../core/logger/core.js';
import type { AstroPreferences } from '../preferences/index.js';
import type { AstroDevOverlay, DevOverlayCanvas } from '../runtime/client/dev-overlay/overlay.js';
import type { Icon } from '../runtime/client/dev-overlay/ui-library/icons.js';
import type { AstroDevToolbar, DevToolbarCanvas } from '../runtime/client/dev-toolbar/toolbar.js';
import type { Icon } from '../runtime/client/dev-toolbar/ui-library/icons.js';
import type {
DevOverlayBadge,
DevOverlayButton,
DevOverlayCard,
DevOverlayHighlight,
DevOverlayIcon,
DevOverlayToggle,
DevOverlayTooltip,
DevOverlayWindow,
} from '../runtime/client/dev-overlay/ui-library/index.js';
DevToolbarBadge,
DevToolbarButton,
DevToolbarCard,
DevToolbarHighlight,
DevToolbarIcon,
DevToolbarToggle,
DevToolbarTooltip,
DevToolbarWindow,
} from '../runtime/client/dev-toolbar/ui-library/index.js';
import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server/index.js';
import type { DeepPartial, OmitIndexSignature, Simplify } from '../type-utils.js';
import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js';
@ -2331,6 +2331,7 @@ export interface AstroIntegration {
addClientDirective: (directive: ClientDirectiveConfig) => void;
/**
* @deprecated Use `addDevToolbarApp` instead.
* TODO: Fully remove in Astro 5.0
*/
addDevOverlayPlugin: (entrypoint: string) => void;
addDevToolbarApp: (entrypoint: string) => void;
@ -2598,11 +2599,12 @@ export interface DevToolbarApp {
beforeTogglingOff?(canvas: ShadowRoot): boolean | Promise<boolean>;
}
// TODO: Remove in Astro 5.0
export type DevOverlayPlugin = DevToolbarApp;
export type DevOverlayMetadata = Window &
export type DevToolbarMetadata = Window &
typeof globalThis & {
__astro_dev_overlay__: {
__astro_dev_toolbar__: {
root: string;
version: string;
debugInfo: string;
@ -2611,27 +2613,28 @@ export type DevOverlayMetadata = Window &
declare global {
interface HTMLElementTagNameMap {
'astro-dev-toolbar': AstroDevOverlay;
'astro-dev-toolbar-window': DevOverlayWindow;
'astro-dev-toolbar-plugin-canvas': DevOverlayCanvas;
'astro-dev-toolbar-tooltip': DevOverlayTooltip;
'astro-dev-toolbar-highlight': DevOverlayHighlight;
'astro-dev-toolbar-toggle': DevOverlayToggle;
'astro-dev-toolbar-badge': DevOverlayBadge;
'astro-dev-toolbar-button': DevOverlayButton;
'astro-dev-toolbar-icon': DevOverlayIcon;
'astro-dev-toolbar-card': DevOverlayCard;
'astro-dev-toolbar': AstroDevToolbar;
'astro-dev-toolbar-window': DevToolbarWindow;
'astro-dev-toolbar-app-canvas': DevToolbarCanvas;
'astro-dev-toolbar-tooltip': DevToolbarTooltip;
'astro-dev-toolbar-highlight': DevToolbarHighlight;
'astro-dev-toolbar-toggle': DevToolbarToggle;
'astro-dev-toolbar-badge': DevToolbarBadge;
'astro-dev-toolbar-button': DevToolbarButton;
'astro-dev-toolbar-icon': DevToolbarIcon;
'astro-dev-toolbar-card': DevToolbarCard;
// Deprecated names
'astro-dev-overlay': AstroDevOverlay;
'astro-dev-overlay-window': DevOverlayWindow;
'astro-dev-overlay-plugin-canvas': DevOverlayCanvas;
'astro-dev-overlay-tooltip': DevOverlayTooltip;
'astro-dev-overlay-highlight': DevOverlayHighlight;
'astro-dev-overlay-toggle': DevOverlayToggle;
'astro-dev-overlay-badge': DevOverlayBadge;
'astro-dev-overlay-button': DevOverlayButton;
'astro-dev-overlay-icon': DevOverlayIcon;
'astro-dev-overlay-card': DevOverlayCard;
// TODO: Remove in Astro 5.0
'astro-dev-overlay': AstroDevToolbar;
'astro-dev-overlay-window': DevToolbarWindow;
'astro-dev-overlay-plugin-canvas': DevToolbarCanvas;
'astro-dev-overlay-tooltip': DevToolbarTooltip;
'astro-dev-overlay-highlight': DevToolbarHighlight;
'astro-dev-overlay-toggle': DevToolbarToggle;
'astro-dev-overlay-badge': DevToolbarBadge;
'astro-dev-overlay-button': DevToolbarButton;
'astro-dev-overlay-icon': DevToolbarIcon;
'astro-dev-overlay-card': DevToolbarCard;
}
}

View file

@ -16,8 +16,9 @@ import astroPostprocessVitePlugin from '../vite-plugin-astro-postprocess/index.j
import { vitePluginAstroServer } from '../vite-plugin-astro-server/index.js';
import astroVitePlugin from '../vite-plugin-astro/index.js';
import configAliasVitePlugin from '../vite-plugin-config-alias/index.js';
import astroDevOverlay from '../vite-plugin-dev-overlay/vite-plugin-dev-overlay.js';
import astroDevToolbar from '../vite-plugin-dev-toolbar/vite-plugin-dev-toolbar.js';
import envVitePlugin from '../vite-plugin-env/index.js';
import vitePluginFileURL from '../vite-plugin-fileurl/index.js';
import astroHeadPlugin from '../vite-plugin-head/index.js';
import htmlVitePlugin from '../vite-plugin-html/index.js';
import { astroInjectEnvTsPlugin } from '../vite-plugin-inject-env-ts/index.js';
@ -33,7 +34,6 @@ import type { Logger } from './logger/core.js';
import { createViteLogger } from './logger/vite.js';
import { vitePluginMiddleware } from './middleware/vite-plugin.js';
import { joinPaths } from './path.js';
import vitePluginFileURL from '../vite-plugin-fileurl/index.js';
interface CreateViteOptions {
settings: AstroSettings;
@ -141,7 +141,7 @@ export async function createVite(
astroAssetsPlugin({ settings, logger, mode }),
astroPrefetch({ settings }),
astroTransitions({ settings }),
astroDevOverlay({ settings, logger }),
astroDevToolbar({ settings, logger }),
vitePluginFileURL({}),
!!settings.config.i18n && astroInternationalization({ settings }),
],

View file

@ -1,8 +0,0 @@
export { DevOverlayBadge } from './badge.js';
export { DevOverlayButton } from './button.js';
export { DevOverlayCard } from './card.js';
export { DevOverlayHighlight } from './highlight.js';
export { DevOverlayIcon } from './icon.js';
export { DevOverlayToggle } from './toggle.js';
export { DevOverlayTooltip } from './tooltip.js';
export { DevOverlayWindow } from './window.js';

View file

@ -1,4 +1,4 @@
import type { DevOverlayMetadata, DevOverlayPlugin } from '../../../../@types/astro.js';
import type { DevToolbarApp, DevToolbarMetadata } from '../../../../@types/astro.js';
import { isDefinedIcon, type Icon } from '../ui-library/icons.js';
import { colorForIntegration, iconForIntegration } from './utils/icons.js';
import { createWindowElement } from './utils/window.js';
@ -321,7 +321,7 @@ export default {
<section>
${astroLogo}
<astro-dev-toolbar-badge badge-style="gray" size="large">${
(window as DevOverlayMetadata).__astro_dev_overlay__.version
(window as DevToolbarMetadata).__astro_dev_toolbar__.version
}</astro-dev-toolbar-badge>
</section>
<astro-dev-toolbar-button id="copy-debug-button">Copy debug info <astro-dev-toolbar-icon icon="copy" /></astro-dev-toolbar-button>
@ -360,7 +360,7 @@ export default {
copyDebugButton?.addEventListener('click', () => {
navigator.clipboard.writeText(
'```\n' + (window as DevOverlayMetadata).__astro_dev_overlay__.debugInfo + '\n```'
'```\n' + (window as DevToolbarMetadata).__astro_dev_toolbar__.debugInfo + '\n```'
);
copyDebugButton.textContent = 'Copied to clipboard!';
@ -436,4 +436,4 @@ export default {
integrationList.append(fragment);
}
},
} satisfies DevOverlayPlugin;
} satisfies DevToolbarApp;

View file

@ -1,5 +1,5 @@
import type { DevOverlayMetadata, DevOverlayPlugin } from '../../../../../@types/astro.js';
import type { DevOverlayHighlight } from '../../ui-library/highlight.js';
import type { DevToolbarApp, DevToolbarMetadata } from '../../../../../@types/astro.js';
import type { DevToolbarHighlight } from '../../ui-library/highlight.js';
import {
attachTooltipToHighlight,
createHighlight,
@ -49,7 +49,7 @@ export default {
name: 'Audit',
icon: icon,
async init(canvas, eventTarget) {
let audits: { highlightElement: DevOverlayHighlight; auditedElement: HTMLElement }[] = [];
let audits: { highlightElement: DevToolbarHighlight; auditedElement: HTMLElement }[] = [];
await lint();
@ -62,7 +62,7 @@ export default {
if (!target.closest) return;
if (target.closest('astro-dev-toolbar')) return;
eventTarget.dispatchEvent(
new CustomEvent('toggle-plugin', {
new CustomEvent('toggle-app', {
detail: {
state: false,
},
@ -167,7 +167,7 @@ export default {
if (noAuditBlock) {
const devOverlayRect = document
.querySelector('astro-dev-toolbar')
?.shadowRoot.querySelector('#dev-overlay')
?.shadowRoot.querySelector('#dev-toolbar-root')
?.getBoundingClientRect();
noAuditBlock.style.top = `${
@ -240,7 +240,7 @@ export default {
tooltip.sections.push({
content: elementFileWithPosition.slice(
(window as DevOverlayMetadata).__astro_dev_overlay__.root.length - 1 // We want to keep the final slash, so minus one.
(window as DevToolbarMetadata).__astro_dev_toolbar__.root.length - 1 // We want to keep the final slash, so minus one.
),
clickDescription: 'Click to go to file',
async clickAction() {
@ -263,4 +263,4 @@ export default {
.replace(/'/g, '&#039;');
}
},
} satisfies DevOverlayPlugin;
} satisfies DevToolbarApp;

View file

@ -1,4 +1,4 @@
import type { DevOverlayPlugin } from '../../../../@types/astro.js';
import type { DevToolbarApp } from '../../../../@types/astro.js';
import { settings, type Settings } from '../settings.js';
import { createWindowElement } from './utils/window.js';
@ -15,24 +15,24 @@ const settingsRows = [
name: 'Disable notifications',
description: 'Hide notification badges in the toolbar.',
input: 'checkbox',
settingKey: 'disablePluginNotification',
settingKey: 'disableAppNotification',
changeEvent: (evt: Event) => {
if (evt.currentTarget instanceof HTMLInputElement) {
const devOverlay = document.querySelector('astro-dev-toolbar');
const devToolbar = document.querySelector('astro-dev-toolbar');
if (devOverlay) {
devOverlay.setNotificationVisible(!evt.currentTarget.checked);
if (devToolbar) {
devToolbar.setNotificationVisible(!evt.currentTarget.checked);
}
settings.updateSetting('disablePluginNotification', evt.currentTarget.checked);
settings.updateSetting('disableAppNotification', evt.currentTarget.checked);
const action = evt.currentTarget.checked ? 'disabled' : 'enabled';
settings.log(`Plugin notification badges ${action}`);
settings.log(`App notification badges ${action}`);
}
},
},
{
name: 'Verbose logging',
description: 'Logs dev overlay events in the browser console.',
description: 'Logs dev toolbar events in the browser console.',
input: 'checkbox',
settingKey: 'verbose',
changeEvent: (evt: Event) => {
@ -168,4 +168,4 @@ export default {
}
}
},
} satisfies DevOverlayPlugin;
} satisfies DevToolbarApp;

View file

@ -1,4 +1,4 @@
import type { DevOverlayHighlight } from '../../ui-library/highlight.js';
import type { DevToolbarHighlight } from '../../ui-library/highlight.js';
import type { Icon } from '../../ui-library/icons.js';
export function createHighlight(rect: DOMRect, icon?: Icon) {
@ -33,7 +33,7 @@ export function getElementsPositionInDocument(el: Element) {
};
}
export function positionHighlight(highlight: DevOverlayHighlight, rect: DOMRect) {
export function positionHighlight(highlight: DevToolbarHighlight, rect: DOMRect) {
highlight.style.display = 'block';
// If the highlight is fixed, don't position based on scroll
const scrollY = highlight.style.position === 'fixed' ? 0 : window.scrollY;
@ -45,7 +45,7 @@ export function positionHighlight(highlight: DevOverlayHighlight, rect: DOMRect)
}
export function attachTooltipToHighlight(
highlight: DevOverlayHighlight,
highlight: DevToolbarHighlight,
tooltip: HTMLElement,
originalElement: Element
) {

View file

@ -1,5 +1,5 @@
import type { DevOverlayMetadata, DevOverlayPlugin } from '../../../../@types/astro.js';
import type { DevOverlayHighlight } from '../ui-library/highlight.js';
import type { DevToolbarApp, DevToolbarMetadata } from '../../../../@types/astro.js';
import type { DevToolbarHighlight } from '../ui-library/highlight.js';
import {
attachTooltipToHighlight,
createHighlight,
@ -16,7 +16,7 @@ export default {
name: 'Inspect',
icon: icon,
init(canvas, eventTarget) {
let islandsOverlays: { highlightElement: DevOverlayHighlight; island: HTMLElement }[] = [];
let islandsOverlays: { highlightElement: DevToolbarHighlight; island: HTMLElement }[] = [];
addIslandsOverlay();
@ -31,7 +31,7 @@ export default {
event.preventDefault();
event.stopPropagation();
eventTarget.dispatchEvent(
new CustomEvent('toggle-plugin', {
new CustomEvent('toggle-app', {
detail: {
state: false,
},
@ -61,7 +61,7 @@ export default {
header {
display: flex;
}
h1 {
display: flex;
align-items: center;
@ -71,7 +71,7 @@ export default {
margin: 0;
font-size: 22px;
}
astro-dev-toolbar-icon {
width: 1em;
height: 1em;
@ -181,7 +181,7 @@ export default {
await fetch(
'/__open-in-editor?file=' +
encodeURIComponent(
(window as DevOverlayMetadata).__astro_dev_overlay__.root +
(window as DevToolbarMetadata).__astro_dev_toolbar__.root +
islandComponentPath.slice(1)
)
);
@ -192,4 +192,4 @@ export default {
return tooltip;
}
},
} satisfies DevOverlayPlugin;
} satisfies DevToolbarApp;

View file

@ -1,73 +1,71 @@
import type { DevOverlayPlugin as DevOverlayPluginDefinition } from '../../../@types/astro.js';
import type { AstroDevOverlay, DevOverlayPlugin } from './overlay.js';
import type { DevToolbarApp as DevToolbarAppDefinition } from '../../../@types/astro.js';
import { settings } from './settings.js';
import type { AstroDevToolbar, DevToolbarApp } from './toolbar.js';
// @ts-expect-error
import { loadDevOverlayPlugins } from 'astro:dev-overlay';
import { loadDevToolbarApps } from 'astro:dev-toolbar';
let overlay: AstroDevOverlay;
let overlay: AstroDevToolbar;
document.addEventListener('DOMContentLoaded', async () => {
const [
customPluginsDefinitions,
{ default: astroDevToolPlugin },
{ default: astroAuditPlugin },
{ default: astroXrayPlugin },
{ default: astroSettingsPlugin },
{ AstroDevOverlay, DevOverlayCanvas, getPluginIcon },
customAppsDefinitions,
{ default: astroDevToolApp },
{ default: astroAuditApp },
{ default: astroXrayApp },
{ default: astroSettingsApp },
{ AstroDevToolbar, DevToolbarCanvas, getAppIcon },
{
DevOverlayCard,
DevOverlayHighlight,
DevOverlayTooltip,
DevOverlayWindow,
DevOverlayToggle,
DevOverlayButton,
DevOverlayBadge,
DevOverlayIcon,
DevToolbarCard,
DevToolbarHighlight,
DevToolbarTooltip,
DevToolbarWindow,
DevToolbarToggle,
DevToolbarButton,
DevToolbarBadge,
DevToolbarIcon,
},
] = await Promise.all([
loadDevOverlayPlugins() as DevOverlayPluginDefinition[],
import('./plugins/astro.js'),
import('./plugins/audit/index.js'),
import('./plugins/xray.js'),
import('./plugins/settings.js'),
import('./overlay.js'),
loadDevToolbarApps() as DevToolbarAppDefinition[],
import('./apps/astro.js'),
import('./apps/audit/index.js'),
import('./apps/xray.js'),
import('./apps/settings.js'),
import('./toolbar.js'),
import('./ui-library/index.js'),
]);
// Register custom elements
customElements.define('astro-dev-toolbar', AstroDevOverlay);
customElements.define('astro-dev-toolbar-window', DevOverlayWindow);
customElements.define('astro-dev-toolbar-plugin-canvas', DevOverlayCanvas);
customElements.define('astro-dev-toolbar-tooltip', DevOverlayTooltip);
customElements.define('astro-dev-toolbar-highlight', DevOverlayHighlight);
customElements.define('astro-dev-toolbar-card', DevOverlayCard);
customElements.define('astro-dev-toolbar-toggle', DevOverlayToggle);
customElements.define('astro-dev-toolbar-button', DevOverlayButton);
customElements.define('astro-dev-toolbar-badge', DevOverlayBadge);
customElements.define('astro-dev-toolbar-icon', DevOverlayIcon);
customElements.define('astro-dev-toolbar', AstroDevToolbar);
customElements.define('astro-dev-toolbar-window', DevToolbarWindow);
customElements.define('astro-dev-toolbar-app-canvas', DevToolbarCanvas);
customElements.define('astro-dev-toolbar-tooltip', DevToolbarTooltip);
customElements.define('astro-dev-toolbar-highlight', DevToolbarHighlight);
customElements.define('astro-dev-toolbar-card', DevToolbarCard);
customElements.define('astro-dev-toolbar-toggle', DevToolbarToggle);
customElements.define('astro-dev-toolbar-button', DevToolbarButton);
customElements.define('astro-dev-toolbar-badge', DevToolbarBadge);
customElements.define('astro-dev-toolbar-icon', DevToolbarIcon);
// Add deprecated names
// TODO: Remove in Astro 5.0
const deprecated = (Parent: any) => class extends Parent {};
customElements.define('astro-dev-overlay', deprecated(AstroDevOverlay));
customElements.define('astro-dev-overlay-window', deprecated(DevOverlayWindow));
customElements.define('astro-dev-overlay-plugin-canvas', deprecated(DevOverlayCanvas));
customElements.define('astro-dev-overlay-tooltip', deprecated(DevOverlayTooltip));
customElements.define('astro-dev-overlay-highlight', deprecated(DevOverlayHighlight));
customElements.define('astro-dev-overlay-card', deprecated(DevOverlayCard));
customElements.define('astro-dev-overlay-toggle', deprecated(DevOverlayToggle));
customElements.define('astro-dev-overlay-button', deprecated(DevOverlayButton));
customElements.define('astro-dev-overlay-badge', deprecated(DevOverlayBadge));
customElements.define('astro-dev-overlay-icon', deprecated(DevOverlayIcon));
customElements.define('astro-dev-overlay', deprecated(AstroDevToolbar));
customElements.define('astro-dev-overlay-window', deprecated(DevToolbarWindow));
customElements.define('astro-dev-overlay-plugin-canvas', deprecated(DevToolbarCanvas));
customElements.define('astro-dev-overlay-tooltip', deprecated(DevToolbarTooltip));
customElements.define('astro-dev-overlay-highlight', deprecated(DevToolbarHighlight));
customElements.define('astro-dev-overlay-card', deprecated(DevToolbarCard));
customElements.define('astro-dev-overlay-toggle', deprecated(DevToolbarToggle));
customElements.define('astro-dev-overlay-button', deprecated(DevToolbarButton));
customElements.define('astro-dev-overlay-badge', deprecated(DevToolbarBadge));
customElements.define('astro-dev-overlay-icon', deprecated(DevToolbarIcon));
overlay = document.createElement('astro-dev-toolbar');
const preparePlugin = (
pluginDefinition: DevOverlayPluginDefinition,
builtIn: boolean
): DevOverlayPlugin => {
const prepareApp = (appDefinition: DevToolbarAppDefinition, builtIn: boolean): DevToolbarApp => {
const eventTarget = new EventTarget();
const plugin = {
...pluginDefinition,
const app = {
...appDefinition,
builtIn: builtIn,
active: false,
status: 'loading' as const,
@ -75,9 +73,9 @@ document.addEventListener('DOMContentLoaded', async () => {
eventTarget: eventTarget,
};
// Events plugins can send to the overlay to update their status
// Events apps can send to the overlay to update their status
eventTarget.addEventListener('toggle-notification', (evt) => {
const target = overlay.shadowRoot?.querySelector(`[data-plugin-id="${plugin.id}"]`);
const target = overlay.shadowRoot?.querySelector(`[data-app-id="${app.id}"]`);
if (!target) return;
let newState = true;
@ -85,7 +83,7 @@ document.addEventListener('DOMContentLoaded', async () => {
newState = evt.detail.state ?? true;
}
plugin.notification.state = newState;
app.notification.state = newState;
target.querySelector('.notification')?.toggleAttribute('data-active', newState);
});
@ -96,22 +94,23 @@ document.addEventListener('DOMContentLoaded', async () => {
newState = evt.detail.state ?? true;
}
await overlay.setPluginStatus(plugin, newState);
await overlay.setAppStatus(app, newState);
};
eventTarget.addEventListener('toggle-app', onToggleApp);
// Deprecated
// TODO: Remove in Astro 5.0
eventTarget.addEventListener('toggle-plugin', onToggleApp);
return plugin;
return app;
};
const astromorePlugin = {
const astroMoreApp = {
id: 'astro:more',
name: 'More',
icon: 'dots-three',
init(canvas, eventTarget) {
const hiddenPlugins = plugins.filter((p) => !p.builtIn).slice(overlay.customPluginsToShow);
const hiddenApps = apps.filter((p) => !p.builtIn).slice(overlay.customAppsToShow);
createDropdown();
@ -192,17 +191,17 @@ document.addEventListener('DOMContentLoaded', async () => {
const dropdown = document.createElement('div');
dropdown.id = 'dropdown';
dropdown.toggleAttribute('data-no-notification', settings.config.disablePluginNotification);
dropdown.toggleAttribute('data-no-notification', settings.config.disableAppNotification);
for (const plugin of hiddenPlugins) {
for (const app of hiddenApps) {
const buttonContainer = document.createElement('div');
buttonContainer.classList.add('item');
const button = document.createElement('button');
button.setAttribute('data-plugin-id', plugin.id);
button.setAttribute('data-app-id', app.id);
const iconContainer = document.createElement('div');
const iconElement = document.createElement('template');
iconElement.innerHTML = getPluginIcon(plugin.icon);
iconElement.innerHTML = getAppIcon(app.icon);
iconContainer.append(iconElement.content.cloneNode(true));
const notification = document.createElement('div');
@ -211,16 +210,16 @@ document.addEventListener('DOMContentLoaded', async () => {
iconContainer.classList.add('icon');
button.append(iconContainer);
button.append(document.createTextNode(plugin.name));
button.append(document.createTextNode(app.name));
button.addEventListener('click', () => {
overlay.togglePluginStatus(plugin);
overlay.toggleAppStatus(app);
});
buttonContainer.append(button);
dropdown.append(buttonContainer);
plugin.eventTarget.addEventListener('toggle-notification', (evt) => {
app.eventTarget.addEventListener('toggle-notification', (evt) => {
if (!(evt instanceof CustomEvent)) return;
notification.toggleAttribute('data-active', evt.detail.state ?? true);
@ -228,7 +227,7 @@ document.addEventListener('DOMContentLoaded', async () => {
eventTarget.dispatchEvent(
new CustomEvent('toggle-notification', {
detail: {
state: hiddenPlugins.some((p) => p.notification.state === true),
state: hiddenApps.some((p) => p.notification.state === true),
},
})
);
@ -238,20 +237,16 @@ document.addEventListener('DOMContentLoaded', async () => {
canvas.append(dropdown);
}
},
} satisfies DevOverlayPluginDefinition;
} satisfies DevToolbarAppDefinition;
const plugins: DevOverlayPlugin[] = [
...[
astroDevToolPlugin,
astroXrayPlugin,
astroAuditPlugin,
astroSettingsPlugin,
astromorePlugin,
].map((pluginDef) => preparePlugin(pluginDef, true)),
...customPluginsDefinitions.map((pluginDef) => preparePlugin(pluginDef, false)),
const apps: DevToolbarApp[] = [
...[astroDevToolApp, astroXrayApp, astroAuditApp, astroSettingsApp, astroMoreApp].map(
(appDef) => prepareApp(appDef, true)
),
...customAppsDefinitions.map((appDef) => prepareApp(appDef, false)),
];
overlay.plugins = plugins;
overlay.apps = apps;
document.body.append(overlay);

View file

@ -1,10 +1,10 @@
export interface Settings {
disablePluginNotification: boolean;
disableAppNotification: boolean;
verbose: boolean;
}
export const defaultSettings = {
disablePluginNotification: false,
disableAppNotification: false,
verbose: false,
} satisfies Settings;
@ -12,15 +12,22 @@ export const settings = getSettings();
function getSettings() {
let _settings: Settings = { ...defaultSettings };
const overlaySettings = localStorage.getItem('astro:dev-overlay:settings');
const toolbarSettings = localStorage.getItem('astro:dev-toolbar:settings');
if (overlaySettings) {
_settings = { ..._settings, ...JSON.parse(overlaySettings) };
// TODO: Remove in Astro 5.0
const oldSettings = localStorage.getItem('astro:dev-overlay:settings');
if (oldSettings && !toolbarSettings) {
localStorage.setItem('astro:dev-toolbar:settings', oldSettings);
localStorage.removeItem('astro:dev-overlay:settings');
}
if (toolbarSettings) {
_settings = { ..._settings, ...JSON.parse(toolbarSettings) };
}
function updateSetting(key: keyof Settings, value: Settings[typeof key]) {
_settings[key] = value;
localStorage.setItem('astro:dev-overlay:settings', JSON.stringify(_settings));
localStorage.setItem('astro:dev-toolbar:settings', JSON.stringify(_settings));
}
function log(message: string) {

View file

@ -1,12 +1,9 @@
/* eslint-disable no-console */
import type {
DevOverlayMetadata,
DevOverlayPlugin as DevOverlayPluginDefinition,
} from '../../../@types/astro.js';
import type { DevToolbarApp as DevToolbarAppDefinition } from '../../../@types/astro.js';
import { settings } from './settings.js';
import { getIconElement, isDefinedIcon, type Icon } from './ui-library/icons.js';
export type DevOverlayPlugin = DevOverlayPluginDefinition & {
export type DevToolbarApp = DevToolbarAppDefinition & {
builtIn: boolean;
active: boolean;
status: 'ready' | 'loading' | 'error';
@ -16,18 +13,20 @@ export type DevOverlayPlugin = DevOverlayPluginDefinition & {
eventTarget: EventTarget;
};
const WS_EVENT_NAME = 'astro-dev-toolbar';
// TODO: Remove in Astro 5.0
const WS_EVENT_NAME_DEPRECATED = 'astro-dev-overlay';
const HOVER_DELAY = 2 * 1000;
const DEVBAR_HITBOX_ABOVE = 42;
export class AstroDevOverlay extends HTMLElement {
export class AstroDevToolbar extends HTMLElement {
shadowRoot: ShadowRoot;
delayedHideTimeout: number | undefined;
devOverlay: HTMLDivElement | undefined;
plugins: DevOverlayPlugin[] = [];
devToolbarContainer: HTMLDivElement | undefined;
apps: DevToolbarApp[] = [];
hasBeenInitialized = false;
// TODO: This should be dynamic based on the screen size or at least configurable, erika - 2023-11-29
customPluginsToShow = 3;
customAppsToShow = 3;
constructor() {
super();
@ -54,7 +53,7 @@ export class AstroDevOverlay extends HTMLElement {
animation: none;
}
#dev-overlay {
#dev-toolbar-root {
position: fixed;
bottom: 0px;
left: 50%;
@ -67,11 +66,11 @@ export class AstroDevOverlay extends HTMLElement {
pointer-events: none;
}
#dev-overlay[data-hidden] {
#dev-toolbar-root[data-hidden] {
bottom: -40px;
}
#dev-overlay[data-hidden] #dev-bar .item {
#dev-toolbar-root[data-hidden] #dev-bar .item {
opacity: 0.2;
}
@ -215,7 +214,7 @@ export class AstroDevOverlay extends HTMLElement {
background: #B33E66;
}
#dev-overlay:not([data-no-notification]) #dev-bar .item .notification[data-active] {
#dev-toolbar-root:not([data-no-notification]) #dev-bar .item .notification[data-active] {
display: block;
}
@ -229,66 +228,62 @@ export class AstroDevOverlay extends HTMLElement {
width: 1px;
}
</style>
<div id="dev-overlay" data-hidden ${
settings.config.disablePluginNotification ? 'data-no-notification' : ''
<div id="dev-toolbar-root" data-hidden ${
settings.config.disableAppNotification ? 'data-no-notification' : ''
}>
<div id="dev-bar-hitbox-above"></div>
<div id="dev-bar">
<div id="bar-container">
${this.plugins
.filter(
(plugin) => plugin.builtIn && !['astro:settings', 'astro:more'].includes(plugin.id)
)
.map((plugin) => this.getPluginTemplate(plugin))
${this.apps
.filter((app) => app.builtIn && !['astro:settings', 'astro:more'].includes(app.id))
.map((app) => this.getAppTemplate(app))
.join('')}
${
this.plugins.filter((plugin) => !plugin.builtIn).length > 0
? `<div class="separator"></div>${this.plugins
.filter((plugin) => !plugin.builtIn)
.slice(0, this.customPluginsToShow)
.map((plugin) => this.getPluginTemplate(plugin))
this.apps.filter((app) => !app.builtIn).length > 0
? `<div class="separator"></div>${this.apps
.filter((app) => !app.builtIn)
.slice(0, this.customAppsToShow)
.map((app) => this.getAppTemplate(app))
.join('')}`
: ''
}
${
this.plugins.filter((plugin) => !plugin.builtIn).length > this.customPluginsToShow
? this.getPluginTemplate(
this.plugins.find((plugin) => plugin.builtIn && plugin.id === 'astro:more')!
this.apps.filter((app) => !app.builtIn).length > this.customAppsToShow
? this.getAppTemplate(
this.apps.find((app) => app.builtIn && app.id === 'astro:more')!
)
: ''
}
<div class="separator"></div>
${this.getPluginTemplate(
this.plugins.find((plugin) => plugin.builtIn && plugin.id === 'astro:settings')!
)}
${this.getAppTemplate(this.apps.find((app) => app.builtIn && app.id === 'astro:settings')!)}
</div>
</div>
<div id="dev-bar-hitbox-below"></div>
</div>`;
this.devOverlay = this.shadowRoot.querySelector<HTMLDivElement>('#dev-overlay')!;
this.devToolbarContainer = this.shadowRoot.querySelector<HTMLDivElement>('#dev-toolbar-root')!;
this.attachEvents();
// Create plugin canvases
this.plugins.forEach(async (plugin) => {
if (settings.config.verbose) console.log(`Creating plugin canvas for ${plugin.id}`);
const pluginCanvas = document.createElement('astro-dev-toolbar-plugin-canvas');
pluginCanvas.dataset.pluginId = plugin.id;
this.shadowRoot?.append(pluginCanvas);
// Create app canvases
this.apps.forEach(async (app) => {
if (settings.config.verbose) console.log(`Creating app canvas for ${app.id}`);
const appCanvas = document.createElement('astro-dev-toolbar-app-canvas');
appCanvas.dataset.appId = app.id;
this.shadowRoot?.append(appCanvas);
});
// Init plugin lazily, so that the page can load faster.
// Init app lazily, so that the page can load faster.
// Fallback to setTimeout for Safari (sad!)
if ('requestIdleCallback' in window) {
window.requestIdleCallback(
async () => {
this.plugins.map((plugin) => this.initPlugin(plugin));
this.apps.map((app) => this.initApp(app));
},
{ timeout: 300 }
);
} else {
setTimeout(async () => {
this.plugins.map((plugin) => this.initPlugin(plugin));
this.apps.map((app) => this.initApp(app));
}, 300);
}
}
@ -302,9 +297,9 @@ export class AstroDevOverlay extends HTMLElement {
this.hasBeenInitialized = true;
}
// Run this every time to make sure the correct plugin is open.
this.plugins.forEach(async (plugin) => {
await this.setPluginStatus(plugin, plugin.active);
// Run this every time to make sure the correct app is open.
this.apps.forEach(async (app) => {
await this.setAppStatus(app, app.active);
});
}
@ -314,28 +309,28 @@ export class AstroDevOverlay extends HTMLElement {
item.addEventListener('click', async (event) => {
const target = event.currentTarget;
if (!target || !(target instanceof HTMLElement)) return;
const id = target.dataset.pluginId;
const id = target.dataset.appId;
if (!id) return;
const plugin = this.getPluginById(id);
if (!plugin) return;
const app = this.getAppById(id);
if (!app) return;
event.stopPropagation();
await this.togglePluginStatus(plugin);
await this.toggleAppStatus(app);
});
});
(['mouseenter', 'focusin'] as const).forEach((event) => {
this.devOverlay!.addEventListener(event, () => {
this.devToolbarContainer!.addEventListener(event, () => {
this.clearDelayedHide();
if (this.isHidden()) {
this.setOverlayVisible(true);
this.setToolbarVisible(true);
}
});
});
(['mouseleave', 'focusout'] as const).forEach((event) => {
this.devOverlay!.addEventListener(event, () => {
this.devToolbarContainer!.addEventListener(event, () => {
this.clearDelayedHide();
if (this.getActivePlugin() || this.isHidden()) {
if (this.getActiveApp() || this.isHidden()) {
return;
}
this.triggerDelayedHide();
@ -345,124 +340,125 @@ export class AstroDevOverlay extends HTMLElement {
document.addEventListener('keyup', (event) => {
if (event.key !== 'Escape') return;
if (this.isHidden()) return;
const activePlugin = this.getActivePlugin();
if (activePlugin) {
this.togglePluginStatus(activePlugin);
const activeApp = this.getActiveApp();
if (activeApp) {
this.toggleAppStatus(activeApp);
} else {
this.setOverlayVisible(false);
this.setToolbarVisible(false);
}
});
}
async initPlugin(plugin: DevOverlayPlugin) {
const shadowRoot = this.getPluginCanvasById(plugin.id)!.shadowRoot!;
plugin.status = 'loading';
async initApp(app: DevToolbarApp) {
const shadowRoot = this.getAppCanvasById(app.id)!.shadowRoot!;
app.status = 'loading';
try {
if (settings.config.verbose) console.info(`Initializing plugin ${plugin.id}`);
if (settings.config.verbose) console.info(`Initializing app ${app.id}`);
await plugin.init?.(shadowRoot, plugin.eventTarget);
plugin.status = 'ready';
await app.init?.(shadowRoot, app.eventTarget);
app.status = 'ready';
if (import.meta.hot) {
import.meta.hot.send(`${WS_EVENT_NAME}:${plugin.id}:initialized`);
import.meta.hot.send(`${WS_EVENT_NAME_DEPRECATED}:${plugin.id}:initialized`);
import.meta.hot.send(`${WS_EVENT_NAME}:${app.id}:initialized`);
import.meta.hot.send(`${WS_EVENT_NAME_DEPRECATED}:${app.id}:initialized`);
}
} catch (e) {
console.error(`Failed to init plugin ${plugin.id}, error: ${e}`);
plugin.status = 'error';
console.error(`Failed to init app ${app.id}, error: ${e}`);
app.status = 'error';
}
}
getPluginTemplate(plugin: DevOverlayPlugin) {
return `<button class="item" data-plugin-id="${plugin.id}">
<div class="icon">${getPluginIcon(plugin.icon)}<div class="notification"></div></div>
<span class="item-tooltip">${plugin.name}</span>
getAppTemplate(app: DevToolbarApp) {
return `<button class="item" data-app-id="${app.id}">
<div class="icon">${getAppIcon(app.icon)}<div class="notification"></div></div>
<span class="item-tooltip">${app.name}</span>
</button>`;
}
getPluginById(id: string) {
return this.plugins.find((plugin) => plugin.id === id);
getAppById(id: string) {
return this.apps.find((app) => app.id === id);
}
getPluginCanvasById(id: string) {
getAppCanvasById(id: string) {
return this.shadowRoot.querySelector<HTMLElement>(
`astro-dev-toolbar-plugin-canvas[data-plugin-id="${id}"]`
`astro-dev-toolbar-app-canvas[data-app-id="${id}"]`
);
}
async togglePluginStatus(plugin: DevOverlayPlugin) {
const activePlugin = this.getActivePlugin();
if (activePlugin) {
const closePlugin = await this.setPluginStatus(activePlugin, false);
async toggleAppStatus(app: DevToolbarApp) {
const activeApp = this.getActiveApp();
if (activeApp) {
const closeApp = await this.setAppStatus(activeApp, false);
// If the plugin returned false, don't open the new plugin, the old plugin didn't want to close
if (!closePlugin) return;
// If the app returned false, don't open the new app, the old app didn't want to close
if (!closeApp) return;
}
// TODO(fks): Handle a plugin that hasn't loaded yet.
// TODO(fks): Handle a app that hasn't loaded yet.
// Currently, this will just do nothing.
if (plugin.status !== 'ready') return;
if (app.status !== 'ready') return;
// Open the selected plugin. If the selected plugin was
// already the active plugin then the desired outcome
// was to close that plugin, so no action needed.
if (plugin !== activePlugin) {
await this.setPluginStatus(plugin, true);
// Open the selected app. If the selected app was
// already the active app then the desired outcome
// was to close that app, so no action needed.
if (app !== activeApp) {
await this.setAppStatus(app, true);
}
}
async setPluginStatus(plugin: DevOverlayPlugin, newStatus: boolean) {
const pluginCanvas = this.getPluginCanvasById(plugin.id);
if (!pluginCanvas) return false;
async setAppStatus(app: DevToolbarApp, newStatus: boolean) {
const appCanvas = this.getAppCanvasById(app.id);
if (!appCanvas) return false;
if (plugin.active && !newStatus && plugin.beforeTogglingOff) {
const shouldToggleOff = await plugin.beforeTogglingOff(pluginCanvas.shadowRoot!);
if (app.active && !newStatus && app.beforeTogglingOff) {
const shouldToggleOff = await app.beforeTogglingOff(appCanvas.shadowRoot!);
// If the plugin returned false, don't toggle it off, maybe the plugin showed a confirmation dialog or similar
// If the app returned false, don't toggle it off, maybe the app showed a confirmation dialog or similar
if (!shouldToggleOff) return false;
}
plugin.active = newStatus ?? !plugin.active;
const mainBarButton = this.shadowRoot.querySelector(`[data-plugin-id="${plugin.id}"]`);
const moreBarButton = this.getPluginCanvasById('astro:more')?.shadowRoot?.querySelector(
`[data-plugin-id="${plugin.id}"]`
app.active = newStatus ?? !app.active;
const mainBarButton = this.shadowRoot.querySelector(`[data-app-id="${app.id}"]`);
const moreBarButton = this.getAppCanvasById('astro:more')?.shadowRoot?.querySelector(
`[data-app-id="${app.id}"]`
);
if (mainBarButton) {
mainBarButton.classList.toggle('active', plugin.active);
mainBarButton.classList.toggle('active', app.active);
}
if (moreBarButton) {
moreBarButton.classList.toggle('active', plugin.active);
moreBarButton.classList.toggle('active', app.active);
}
if (plugin.active) {
pluginCanvas.style.display = 'block';
pluginCanvas.setAttribute('data-active', '');
if (app.active) {
appCanvas.style.display = 'block';
appCanvas.setAttribute('data-active', '');
} else {
pluginCanvas.style.display = 'none';
pluginCanvas.removeAttribute('data-active');
appCanvas.style.display = 'none';
appCanvas.removeAttribute('data-active');
}
[
'app-toggled',
// Deprecated
// TODO: Remove in Astro 5.0
'plugin-toggled',
].forEach((eventName) => {
plugin.eventTarget.dispatchEvent(
app.eventTarget.dispatchEvent(
new CustomEvent(eventName, {
detail: {
state: plugin.active,
plugin,
state: app.active,
app,
},
})
);
});
if (import.meta.hot) {
import.meta.hot.send(`${WS_EVENT_NAME}:${plugin.id}:toggled`, { state: plugin.active });
import.meta.hot.send(`${WS_EVENT_NAME_DEPRECATED}:${plugin.id}:toggled`, {
state: plugin.active,
import.meta.hot.send(`${WS_EVENT_NAME}:${app.id}:toggled`, { state: app.active });
import.meta.hot.send(`${WS_EVENT_NAME_DEPRECATED}:${app.id}:toggled`, {
state: app.active,
});
}
@ -470,11 +466,11 @@ export class AstroDevOverlay extends HTMLElement {
}
isHidden(): boolean {
return this.devOverlay?.hasAttribute('data-hidden') ?? true;
return this.devToolbarContainer?.hasAttribute('data-hidden') ?? true;
}
getActivePlugin(): DevOverlayPlugin | undefined {
return this.plugins.find((plugin) => plugin.active);
getActiveApp(): DevToolbarApp | undefined {
return this.apps.find((app) => app.active);
}
clearDelayedHide() {
@ -485,25 +481,25 @@ export class AstroDevOverlay extends HTMLElement {
triggerDelayedHide() {
this.clearDelayedHide();
this.delayedHideTimeout = window.setTimeout(() => {
this.setOverlayVisible(false);
this.setToolbarVisible(false);
this.delayedHideTimeout = undefined;
}, HOVER_DELAY);
}
setOverlayVisible(newStatus: boolean) {
setToolbarVisible(newStatus: boolean) {
const barContainer = this.shadowRoot.querySelector<HTMLDivElement>('#bar-container');
const devBar = this.shadowRoot.querySelector<HTMLDivElement>('#dev-bar');
const devBarHitboxAbove =
this.shadowRoot.querySelector<HTMLDivElement>('#dev-bar-hitbox-above');
if (newStatus === true) {
this.devOverlay?.removeAttribute('data-hidden');
this.devToolbarContainer?.removeAttribute('data-hidden');
barContainer?.removeAttribute('inert');
devBar?.removeAttribute('tabindex');
if (devBarHitboxAbove) devBarHitboxAbove.style.height = '0';
return;
}
if (newStatus === false) {
this.devOverlay?.setAttribute('data-hidden', '');
this.devToolbarContainer?.setAttribute('data-hidden', '');
barContainer?.setAttribute('inert', '');
devBar?.setAttribute('tabindex', '0');
if (devBarHitboxAbove) devBarHitboxAbove.style.height = `${DEVBAR_HITBOX_ABOVE}px`;
@ -512,17 +508,16 @@ export class AstroDevOverlay extends HTMLElement {
}
setNotificationVisible(newStatus: boolean) {
const devOverlayElement = this.shadowRoot.querySelector<HTMLDivElement>('#dev-overlay');
devOverlayElement?.toggleAttribute('data-no-notification', !newStatus);
this.devToolbarContainer?.toggleAttribute('data-no-notification', !newStatus);
const moreCanvas = this.getPluginCanvasById('astro:more');
const moreCanvas = this.getAppCanvasById('astro:more');
moreCanvas?.shadowRoot
?.querySelector('#dropdown')
?.toggleAttribute('data-no-notification', !newStatus);
}
}
export class DevOverlayCanvas extends HTMLElement {
export class DevToolbarCanvas extends HTMLElement {
shadowRoot: ShadowRoot;
constructor() {
@ -542,7 +537,7 @@ export class DevOverlayCanvas extends HTMLElement {
}
}
export function getPluginIcon(icon: Icon) {
export function getAppIcon(icon: Icon) {
if (isDefinedIcon(icon)) {
return getIconElement(icon).outerHTML;
}

View file

@ -1,7 +1,7 @@
type BadgeSize = 'small' | 'large';
type BadgeStyle = 'purple' | 'gray' | 'red' | 'green' | 'yellow';
export class DevOverlayBadge extends HTMLElement {
export class DevToolbarBadge extends HTMLElement {
size: BadgeSize = 'small';
badgeStyle: BadgeStyle = 'purple';

View file

@ -1,7 +1,7 @@
type ButtonSize = 'small' | 'medium' | 'large';
type ButtonStyle = 'ghost' | 'outline' | 'purple' | 'gray' | 'red';
export class DevOverlayButton extends HTMLElement {
export class DevToolbarButton extends HTMLElement {
size: ButtonSize = 'small';
buttonStyle: ButtonStyle = 'purple';
@ -72,6 +72,7 @@ export class DevOverlayButton extends HTMLElement {
border-color: rgba(249, 196, 215, 0.33);
}
/* TODO: Remove "astro-dev-overlay-icon" in Astro 5.0 */
::slotted(astro-dev-overlay-icon),
::slotted(astro-dev-toolbar-icon) {
display: inline-block;

View file

@ -1,4 +1,4 @@
export class DevOverlayCard extends HTMLElement {
export class DevToolbarCard extends HTMLElement {
link?: string | undefined | null;
clickAction?: () => void | (() => Promise<void>);
shadowRoot: ShadowRoot;

View file

@ -1,6 +1,6 @@
import { getIconElement, isDefinedIcon, type Icon } from './icons.js';
export class DevOverlayHighlight extends HTMLElement {
export class DevToolbarHighlight extends HTMLElement {
icon?: Icon | undefined | null;
shadowRoot: ShadowRoot;

View file

@ -1,6 +1,6 @@
import { getIconElement, isDefinedIcon, type Icon } from './icons.js';
export class DevOverlayIcon extends HTMLElement {
export class DevToolbarIcon extends HTMLElement {
_icon: Icon | undefined = undefined;
shadowRoot: ShadowRoot;

View file

@ -0,0 +1,8 @@
export { DevToolbarBadge } from './badge.js';
export { DevToolbarButton } from './button.js';
export { DevToolbarCard } from './card.js';
export { DevToolbarHighlight } from './highlight.js';
export { DevToolbarIcon } from './icon.js';
export { DevToolbarToggle } from './toggle.js';
export { DevToolbarTooltip } from './tooltip.js';
export { DevToolbarWindow } from './window.js';

View file

@ -1,4 +1,4 @@
export class DevOverlayToggle extends HTMLElement {
export class DevToolbarToggle extends HTMLElement {
shadowRoot: ShadowRoot;
input: HTMLInputElement;

View file

@ -1,6 +1,6 @@
import { getIconElement, isDefinedIcon, type Icon } from './icons.js';
export interface DevOverlayTooltipSection {
export interface DevToolbarTooltipSection {
title?: string;
inlineTitle?: string;
icon?: Icon;
@ -9,8 +9,8 @@ export interface DevOverlayTooltipSection {
clickDescription?: string;
}
export class DevOverlayTooltip extends HTMLElement {
sections: DevOverlayTooltipSection[] = [];
export class DevToolbarTooltip extends HTMLElement {
sections: DevToolbarTooltipSection[] = [];
shadowRoot: ShadowRoot;
constructor() {

View file

@ -1,4 +1,4 @@
export class DevOverlayWindow extends HTMLElement {
export class DevToolbarWindow extends HTMLElement {
shadowRoot: ShadowRoot;
constructor() {
@ -34,7 +34,7 @@ export class DevOverlayWindow extends HTMLElement {
background: white;
}
}
@media (max-width: 640px) {
:host {
border-radius: 0;

View file

@ -2,7 +2,7 @@ import type http from 'node:http';
import { fileURLToPath } from 'node:url';
import type {
ComponentInstance,
DevOverlayMetadata,
DevToolbarMetadata,
ManifestData,
MiddlewareHandler,
RouteData,
@ -24,6 +24,7 @@ import {
import { createRequest } from '../core/request.js';
import { matchAllRoutes } from '../core/routing/index.js';
import { isPage, resolveIdToUrl } from '../core/util.js';
import { normalizeTheLocale } from '../i18n/index.js';
import { createI18nMiddleware, i18nPipelineHook } from '../i18n/middleware.js';
import { getSortedPreloadedMatches } from '../prerender/routing.js';
import { isServerLikeOutput } from '../prerender/utils.js';
@ -34,7 +35,6 @@ import { preload } from './index.js';
import { getComponentMetadata } from './metadata.js';
import { handle404Response, writeSSRResult, writeWebResponse } from './response.js';
import { getScriptsForURL } from './scripts.js';
import { normalizeTheLocale } from '../i18n/index.js';
const clientLocalsSymbol = Symbol.for('astro.locals');
@ -404,12 +404,12 @@ async function getScriptsAndStyles({ pipeline, filePath }: GetScriptsAndStylesPa
scripts.add({
props: {
type: 'module',
src: await resolveIdToUrl(moduleLoader, 'astro/runtime/client/dev-overlay/entrypoint.js'),
src: await resolveIdToUrl(moduleLoader, 'astro/runtime/client/dev-toolbar/entrypoint.js'),
},
children: '',
});
const additionalMetadata: DevOverlayMetadata['__astro_dev_overlay__'] = {
const additionalMetadata: DevToolbarMetadata['__astro_dev_toolbar__'] = {
root: fileURLToPath(settings.config.root),
version: ASTRO_VERSION,
debugInfo: await getInfoOutput({ userConfig: settings.config, print: false }),
@ -418,7 +418,7 @@ async function getScriptsAndStyles({ pipeline, filePath }: GetScriptsAndStylesPa
// Additional data for the dev overlay
scripts.add({
props: {},
children: `window.__astro_dev_overlay__ = ${JSON.stringify(additionalMetadata)}`,
children: `window.__astro_dev_toolbar__ = ${JSON.stringify(additionalMetadata)}`,
});
}
}

View file

@ -1,12 +1,12 @@
import type * as vite from 'vite';
import type { AstroPluginOptions } from '../@types/astro.js';
const VIRTUAL_MODULE_ID = 'astro:dev-overlay';
const VIRTUAL_MODULE_ID = 'astro:dev-toolbar';
const resolvedVirtualModuleId = '\0' + VIRTUAL_MODULE_ID;
export default function astroDevOverlay({ settings }: AstroPluginOptions): vite.Plugin {
export default function astroDevToolbar({ settings }: AstroPluginOptions): vite.Plugin {
return {
name: 'astro:dev-overlay',
name: 'astro:dev-toolbar',
config() {
return {
optimizeDeps: {
@ -23,7 +23,7 @@ export default function astroDevOverlay({ settings }: AstroPluginOptions): vite.
async load(id) {
if (id === resolvedVirtualModuleId) {
return `
export const loadDevOverlayPlugins = async () => {
export const loadDevToolbarApps = async () => {
return [${settings.devToolbarApps
.map((plugin) => `(await import(${JSON.stringify(plugin)})).default`)
.join(',')}];

2
pnpm-lock.yaml generated
View file

@ -959,7 +959,7 @@ importers:
specifier: ^18.0.0
version: 18.2.0(react@18.2.0)
packages/astro/e2e/fixtures/dev-overlay:
packages/astro/e2e/fixtures/dev-toolbar:
dependencies:
'@astrojs/preact':
specifier: workspace:*