Compare commits
4 commits
main
...
feat/dev-o
Author | SHA1 | Date | |
---|---|---|---|
|
85d1d60cb5 | ||
|
4ba8b5c73f | ||
|
5bb1398b2a | ||
|
66f7b5aa3f |
26 changed files with 795 additions and 1 deletions
|
@ -1,5 +1,7 @@
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const { builtinModules } = require('module');
|
const { builtinModules } = require('module');
|
||||||
|
|
||||||
|
/** @type {import("@types/eslint").Linter.Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
extends: [
|
extends: [
|
||||||
'plugin:@typescript-eslint/recommended-type-checked',
|
'plugin:@typescript-eslint/recommended-type-checked',
|
||||||
|
@ -69,6 +71,12 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
files: ['packages/astro/src/runtime/client/**/*.ts'],
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
files: ['packages/**/test/*.js', 'packages/**/*.js'],
|
files: ['packages/**/test/*.js', 'packages/**/*.js'],
|
||||||
env: {
|
env: {
|
||||||
|
|
1
examples/dev-overlay/.codesandbox/Dockerfile
Normal file
1
examples/dev-overlay/.codesandbox/Dockerfile
Normal file
|
@ -0,0 +1 @@
|
||||||
|
FROM node:18-bullseye
|
21
examples/dev-overlay/.gitignore
vendored
Normal file
21
examples/dev-overlay/.gitignore
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# build output
|
||||||
|
dist/
|
||||||
|
# generated types
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# macOS-specific files
|
||||||
|
.DS_Store
|
4
examples/dev-overlay/.vscode/extensions.json
vendored
Normal file
4
examples/dev-overlay/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"recommendations": ["astro-build.astro-vscode"],
|
||||||
|
"unwantedRecommendations": []
|
||||||
|
}
|
11
examples/dev-overlay/.vscode/launch.json
vendored
Normal file
11
examples/dev-overlay/.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"command": "./node_modules/.bin/astro dev",
|
||||||
|
"name": "Development server",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
47
examples/dev-overlay/README.md
Normal file
47
examples/dev-overlay/README.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# Astro Starter Kit: Dev Overlay Showcase
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm create astro@latest -- --template dev-overlay
|
||||||
|
```
|
||||||
|
|
||||||
|
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal)
|
||||||
|
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/minimal)
|
||||||
|
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/minimal/devcontainer.json)
|
||||||
|
|
||||||
|
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||||
|
|
||||||
|
## 🚀 Project Structure
|
||||||
|
|
||||||
|
Inside of your Astro project, you'll see the following folders and files:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/
|
||||||
|
├── public/
|
||||||
|
├── src/
|
||||||
|
│ └── pages/
|
||||||
|
│ └── index.astro
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||||
|
|
||||||
|
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||||
|
|
||||||
|
Any static assets, like images, can be placed in the `public/` directory.
|
||||||
|
|
||||||
|
## 🧞 Commands
|
||||||
|
|
||||||
|
All commands are run from the root of the project, from a terminal:
|
||||||
|
|
||||||
|
| Command | Action |
|
||||||
|
| :------------------------ | :----------------------------------------------- |
|
||||||
|
| `npm install` | Installs dependencies |
|
||||||
|
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||||
|
| `npm run build` | Build your production site to `./dist/` |
|
||||||
|
| `npm run preview` | Preview your build locally, before deploying |
|
||||||
|
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||||
|
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||||
|
|
||||||
|
## 👀 Want to learn more?
|
||||||
|
|
||||||
|
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
5
examples/dev-overlay/astro-devtools-plugin.js
Normal file
5
examples/dev-overlay/astro-devtools-plugin.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default {
|
||||||
|
id: 'custom',
|
||||||
|
title: "I'm a plugin!",
|
||||||
|
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="#fff" d="M7.563 19.28a9.693 9.693 0 0 0 2.496-.217a8.798 8.798 0 0 1 2.98-.131a7.78 7.78 0 0 1 1.289.257c1.077.275 2.61.223 3.005-.41c.29-.468.253-.787-.026-1.199c-.06-.09-.126-.17-.188-.235l-.024-.025a25.109 25.109 0 0 1-.743-.618a25.578 25.578 0 0 1-1.753-1.66a16.153 16.153 0 0 1-1.577-1.893l-.036-.053c-.742-1.139-1.558-1.067-2.002-.318a9.593 9.593 0 0 1-.955 1.332c-.41.482-.83.89-1.306 1.297c-.122.105-.502.42-.411.344c-.004.003-.017.015.05-.071c-.098.12-.95.877-1.2 1.162c-.515.583-.722 1.08-.645 1.48c.073.376.22.587.45.745a1.433 1.433 0 0 0 .48.206c.033.003.072.005.116.007Zm7.099-7.276c1.375 1.97 3.731 3.793 3.731 3.793s2.064 1.748.638 4.038c-1.426 2.29-5.253 1.278-5.253 1.278s-1.52-.49-3.286-.098c-1.765.395-3.286.245-3.286.245S5 21.015 4.553 18.701c-.446-2.314 2.06-4.04 2.258-4.284c.196-.247 1.512-1.073 2.452-2.658c.94-1.586 3.584-2.54 5.399.245Zm5.538-1.42c0 .457.191 2.393-1.552 2.432c-1.743.038-1.816-1.178-1.816-2.05c0-.913.187-2.205 1.59-2.205c1.4 0 1.778 1.369 1.778 1.824Zm-5.429-2.777c-1.18-.152-1.447-1.222-1.333-2.293c.095-.875 1.142-2.219 1.981-2.026c.837.19 1.6 1.3 1.446 2.254c-.152.957-.912 2.218-2.094 2.065ZM9.755 7.44c-.861 0-1.56-.993-1.56-2.22c0-1.227.698-2.22 1.56-2.22c.863 0 1.56.993 1.56 2.22c0 1.227-.697 2.22-1.56 2.22Zm-3.793 4.566c-1.695.365-2.327-1.597-2.14-2.515c0 0 .2-1.987 1.576-2.11c1.093-.095 1.898 1.101 1.98 1.785c.052.444.283 2.475-1.416 2.84Z"/></svg>',
|
||||||
|
};
|
11
examples/dev-overlay/astro.config.mjs
Normal file
11
examples/dev-overlay/astro.config.mjs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
|
import solidJs from "@astrojs/solid-js";
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
devTools: {
|
||||||
|
plugins: ['./astro-devtools-plugin.js']
|
||||||
|
},
|
||||||
|
integrations: [solidJs()]
|
||||||
|
});
|
3
examples/dev-overlay/components/Component.tsx
Normal file
3
examples/dev-overlay/components/Component.tsx
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export function HelloWorld(props: { text: string }) {
|
||||||
|
return <><div>{props.text}</div><br/></>;
|
||||||
|
}
|
18
examples/dev-overlay/package.json
Normal file
18
examples/dev-overlay/package.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "@example/dev-overlay",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "astro dev",
|
||||||
|
"start": "astro dev",
|
||||||
|
"build": "astro build",
|
||||||
|
"preview": "astro preview",
|
||||||
|
"astro": "astro"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"astro": "^3.2.3",
|
||||||
|
"@astrojs/solid-js": "^3.0.1",
|
||||||
|
"solid-js": "^1.7.11"
|
||||||
|
}
|
||||||
|
}
|
BIN
examples/dev-overlay/public/astro-logo.png
Normal file
BIN
examples/dev-overlay/public/astro-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
9
examples/dev-overlay/public/favicon.svg
Normal file
9
examples/dev-overlay/public/favicon.svg
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||||
|
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||||
|
<style>
|
||||||
|
path { fill: #000; }
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
path { fill: #FFF; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 749 B |
23
examples/dev-overlay/src/pages/index.astro
Normal file
23
examples/dev-overlay/src/pages/index.astro
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
import { HelloWorld } from '../../components/Component';
|
||||||
|
---
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<meta name="generator" content={Astro.generator} />
|
||||||
|
<title>Astro</title>
|
||||||
|
</head>
|
||||||
|
<body style="background-color: sandybrown;">
|
||||||
|
<h1>Astro</h1>
|
||||||
|
|
||||||
|
<h2>Audit</h2>
|
||||||
|
<img src="/astro-logo.png" />
|
||||||
|
|
||||||
|
<h2>Components</h2>
|
||||||
|
<HelloWorld text="No hydration" />
|
||||||
|
<HelloWorld text="client:load Hydrated" client:load name="hello" />
|
||||||
|
</body>
|
||||||
|
</html>
|
7
examples/dev-overlay/tsconfig.json
Normal file
7
examples/dev-overlay/tsconfig.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"extends": "astro/tsconfigs/base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "preserve",
|
||||||
|
"jsxImportSource": "solid-js"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1084,6 +1084,10 @@ export interface AstroUserConfig {
|
||||||
remotePatterns?: Partial<RemotePattern>[];
|
remotePatterns?: Partial<RemotePattern>[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
devTools?: {
|
||||||
|
plugins: string[];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @docs
|
* @docs
|
||||||
* @kind heading
|
* @kind heading
|
||||||
|
@ -2263,3 +2267,10 @@ export interface ClientDirectiveConfig {
|
||||||
name: string;
|
name: string;
|
||||||
entrypoint: string;
|
entrypoint: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DevOverlayItem {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
init?(canvas: ShadowRoot, eventTarget: EventTarget): void | Promise<void>;
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,9 @@ const ASTRO_CONFIG_DEFAULTS = {
|
||||||
image: {
|
image: {
|
||||||
service: { entrypoint: 'astro/assets/services/sharp', config: {} },
|
service: { entrypoint: 'astro/assets/services/sharp', config: {} },
|
||||||
},
|
},
|
||||||
|
devTools: {
|
||||||
|
plugins: [],
|
||||||
|
},
|
||||||
compressHTML: true,
|
compressHTML: true,
|
||||||
server: {
|
server: {
|
||||||
host: false,
|
host: false,
|
||||||
|
@ -220,6 +223,9 @@ export const AstroConfigSchema = z.object({
|
||||||
.default([]),
|
.default([]),
|
||||||
})
|
})
|
||||||
.default(ASTRO_CONFIG_DEFAULTS.image),
|
.default(ASTRO_CONFIG_DEFAULTS.image),
|
||||||
|
devTools: z
|
||||||
|
.object({ plugins: z.array(z.string()).default([]) })
|
||||||
|
.default(ASTRO_CONFIG_DEFAULTS.devTools),
|
||||||
markdown: z
|
markdown: z
|
||||||
.object({
|
.object({
|
||||||
drafts: z.boolean().default(false),
|
drafts: z.boolean().default(false),
|
||||||
|
|
|
@ -16,6 +16,7 @@ import astroPostprocessVitePlugin from '../vite-plugin-astro-postprocess/index.j
|
||||||
import { vitePluginAstroServer } from '../vite-plugin-astro-server/index.js';
|
import { vitePluginAstroServer } from '../vite-plugin-astro-server/index.js';
|
||||||
import astroVitePlugin from '../vite-plugin-astro/index.js';
|
import astroVitePlugin from '../vite-plugin-astro/index.js';
|
||||||
import configAliasVitePlugin from '../vite-plugin-config-alias/index.js';
|
import configAliasVitePlugin from '../vite-plugin-config-alias/index.js';
|
||||||
|
import astroDevTools from '../vite-plugin-dev-tools/vite-plugin-dev-tools.js';
|
||||||
import envVitePlugin from '../vite-plugin-env/index.js';
|
import envVitePlugin from '../vite-plugin-env/index.js';
|
||||||
import astroHeadPlugin from '../vite-plugin-head/index.js';
|
import astroHeadPlugin from '../vite-plugin-head/index.js';
|
||||||
import htmlVitePlugin from '../vite-plugin-html/index.js';
|
import htmlVitePlugin from '../vite-plugin-html/index.js';
|
||||||
|
@ -134,6 +135,7 @@ export async function createVite(
|
||||||
vitePluginSSRManifest(),
|
vitePluginSSRManifest(),
|
||||||
astroAssetsPlugin({ settings, logger, mode }),
|
astroAssetsPlugin({ settings, logger, mode }),
|
||||||
astroTransitions(),
|
astroTransitions(),
|
||||||
|
astroDevTools({ settings, logger }),
|
||||||
],
|
],
|
||||||
publicDir: fileURLToPath(settings.config.publicDir),
|
publicDir: fileURLToPath(settings.config.publicDir),
|
||||||
root: fileURLToPath(settings.config.root),
|
root: fileURLToPath(settings.config.root),
|
||||||
|
|
189
packages/astro/src/runtime/client/dev-overlay/overlay.ts
Normal file
189
packages/astro/src/runtime/client/dev-overlay/overlay.ts
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
// @ts-expect-error
|
||||||
|
import { loadDevToolsPlugins } from 'astro:dev-tools';
|
||||||
|
import type { DevOverlayItem as DevOverlayItemDefinition } from '../../../@types/astro.js';
|
||||||
|
import astroDevToolPlugin from './plugins/astro.js';
|
||||||
|
import astroAuditPlugin from './plugins/audit.js';
|
||||||
|
import astroXrayPlugin from './plugins/xray.js';
|
||||||
|
import { DevOverlayHighlight, DevOverlayTooltip, DevOverlayWindow } from './ui-toolkit.js';
|
||||||
|
|
||||||
|
type DevOverlayItem = DevOverlayItemDefinition & {
|
||||||
|
active: boolean;
|
||||||
|
inited: boolean;
|
||||||
|
eventTarget: EventTarget;
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
const builtinPlugins: DevOverlayItem[] = [
|
||||||
|
astroDevToolPlugin,
|
||||||
|
astroXrayPlugin,
|
||||||
|
astroAuditPlugin,
|
||||||
|
].map((plugin) => ({ ...plugin, active: false, inited: false, eventTarget: new EventTarget() }));
|
||||||
|
const customPluginsImports = (await loadDevToolsPlugins()) as DevOverlayItem[];
|
||||||
|
const customPlugins: DevOverlayItem[] = [];
|
||||||
|
customPlugins.push(...customPluginsImports.map((plugin) => ({ ...plugin, active: false })));
|
||||||
|
|
||||||
|
const plugins: DevOverlayItem[] = [...builtinPlugins, ...customPlugins];
|
||||||
|
|
||||||
|
class AstroDevOverlay extends HTMLElement {
|
||||||
|
shadowRoot: ShadowRoot;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.shadowRoot = this.attachShadow({ mode: 'closed' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect component
|
||||||
|
async connectedCallback() {
|
||||||
|
this.shadowRoot.innerHTML = `
|
||||||
|
<style>
|
||||||
|
#dev-overlay {
|
||||||
|
display: inline-block;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 7.5%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 0%);
|
||||||
|
height: 56px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
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;
|
||||||
|
z-index: 999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev-overlay .item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev-overlay #bar-container .item:hover {
|
||||||
|
background: rgba(27, 30, 36, 1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev-overlay #bar-container .item.active {
|
||||||
|
background: rgba(71, 78, 94, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev-overlay .item svg {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev-overlay #bar-container {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev-overlay .separator {
|
||||||
|
background: rgba(52, 56, 65, 1);
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
astro-overlay-plugin-canvas:not([data-active]) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="dev-overlay">
|
||||||
|
<div id="bar-container">
|
||||||
|
${builtinPlugins.map((plugin) => this.getPluginTemplate(plugin)).join('')}
|
||||||
|
<div class="separator"></div>
|
||||||
|
${customPluginsImports.map((plugin) => this.getPluginTemplate(plugin)).join('')}
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
this.attachClickEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
attachClickEvents() {
|
||||||
|
const items = this.shadowRoot.querySelectorAll<HTMLDivElement>('.item');
|
||||||
|
if (!items) return;
|
||||||
|
items.forEach((item) => {
|
||||||
|
item.addEventListener('click', async (e) => {
|
||||||
|
const target = e.currentTarget;
|
||||||
|
if (!target || !(target instanceof HTMLElement)) return;
|
||||||
|
|
||||||
|
const id = target.dataset.pluginId;
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
const plugin = this.getPluginById(id);
|
||||||
|
if (!plugin) return;
|
||||||
|
const shadowRoot = this.getPluginCanvasById(plugin.id)!.shadowRoot!;
|
||||||
|
if (!plugin.inited) {
|
||||||
|
await plugin.init?.(shadowRoot, plugin.eventTarget);
|
||||||
|
plugin.inited = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.togglePluginStatus(plugin);
|
||||||
|
plugin.eventTarget.dispatchEvent(
|
||||||
|
new CustomEvent('plugin-toggle', {
|
||||||
|
detail: {
|
||||||
|
state: plugin.active,
|
||||||
|
plugin,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getPluginTemplate(plugin: DevOverlayItem) {
|
||||||
|
return `<div class="item" data-plugin-id="${plugin.id}">
|
||||||
|
<div class="icon">${plugin.icon}</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPluginById(id: string) {
|
||||||
|
return plugins.find((plugin) => plugin.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPluginCanvasById(id: string) {
|
||||||
|
return this.shadowRoot.querySelector(`astro-overlay-plugin-canvas[data-plugin-id="${id}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePluginStatus(plugin: DevOverlayItem, status?: boolean) {
|
||||||
|
plugin.active = status ?? !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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DevOverlayCanvas extends HTMLElement {
|
||||||
|
shadowRoot: ShadowRoot;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.shadowRoot = this.attachShadow({ mode: 'closed' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect component
|
||||||
|
async connectedCallback() {
|
||||||
|
this.shadowRoot.innerHTML = ``;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('astro-dev-overlay', AstroDevOverlay);
|
||||||
|
customElements.define('astro-dev-overlay-window', DevOverlayWindow);
|
||||||
|
customElements.define('astro-overlay-plugin-canvas', DevOverlayCanvas);
|
||||||
|
customElements.define('astro-overlay-tooltip', DevOverlayTooltip);
|
||||||
|
customElements.define('astro-overlay-highlight', DevOverlayHighlight);
|
||||||
|
|
||||||
|
const overlay = document.createElement('astro-dev-overlay');
|
||||||
|
overlay.style.zIndex = '999999';
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
|
// Create plugin canvases
|
||||||
|
plugins.forEach((plugin) => {
|
||||||
|
const pluginCanvas = document.createElement('astro-overlay-plugin-canvas');
|
||||||
|
pluginCanvas.dataset.pluginId = plugin.id;
|
||||||
|
overlay.shadowRoot?.appendChild(pluginCanvas);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,14 @@
|
||||||
|
import type { DevOverlayItem } from '../../../../@types/astro.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
id: 'astro',
|
||||||
|
name: 'Astro',
|
||||||
|
icon: '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 24"><path fill="#fff" d="M6.45385 20.9419c-1.08694-.9911-1.40425-3.0736-.9514-4.5823.78522.9512 1.8732 1.2525 3.00013 1.4226 1.73972.2625 3.44832.1643 5.06442-.6289.1849-.0908.3557-.2115.5578-.3338.1516.4388.1911.8819.1381 1.3328-.1288 1.0982-.6767 1.9465-1.5481 2.5896-.3485.2572-.7172.4871-1.0771.7297-1.1056.7454-1.4047 1.6194-.9893 2.8909.0099.0309.0187.0619.041.1375-.5645-.252-.9768-.6189-1.29099-1.1013-.33185-.5092-.48972-1.0725-.49803-1.682-.00416-.2966-.00416-.5958-.04414-.8882-.09764-.7129-.43312-1.032-1.06513-1.0504-.64864-.0189-1.16173.3811-1.29779 1.011-.01039.0483-.02545.0961-.04051.1523l.00104.0005Z"/><path fill="url(#a)" d="M6.45385 20.9419c-1.08694-.9911-1.40425-3.0736-.9514-4.5823.78522.9512 1.8732 1.2525 3.00013 1.4226 1.73972.2625 3.44832.1643 5.06442-.6289.1849-.0908.3557-.2115.5578-.3338.1516.4388.1911.8819.1381 1.3328-.1288 1.0982-.6767 1.9465-1.5481 2.5896-.3485.2572-.7172.4871-1.0771.7297-1.1056.7454-1.4047 1.6194-.9893 2.8909.0099.0309.0187.0619.041.1375-.5645-.252-.9768-.6189-1.29099-1.1013-.33185-.5092-.48972-1.0725-.49803-1.682-.00416-.2966-.00416-.5958-.04414-.8882-.09764-.7129-.43312-1.032-1.06513-1.0504-.64864-.0189-1.16173.3811-1.29779 1.011-.01039.0483-.02545.0961-.04051.1523l.00104.0005Z"/><path fill="#fff" d="M.25 16.1083s3.21861-1.5641 6.44622-1.5641l2.43351-7.51249c.0911-.36331.35712-.61021.65744-.61021.30033 0 .56633.2469.65743.61021l2.4335 7.51249c3.8226 0 6.4462 1.5641 6.4462 1.5641s-5.467-14.85637-5.4777-14.88618C13.6897.782887 13.4248.5 13.0676.5H6.50726c-.35713 0-.61133.282887-.77893.72212C5.71652 1.25137.25 16.1083.25 16.1083Z"/><defs><linearGradient id="a" x1="9.7873" x2="12.2634" y1="23.3025" y2="15.1217" gradientUnits="userSpaceOnUse"><stop stop-color="#D83333"/><stop offset="1" stop-color="#F041FF"/></linearGradient></defs></svg>',
|
||||||
|
init(canvas, eventTarget) {
|
||||||
|
eventTarget.addEventListener('plugin-toggle', (e) => {
|
||||||
|
if ((e as CustomEvent).detail.state === true) {
|
||||||
|
window.open('https://astro.build', '_blank');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
} satisfies DevOverlayItem;
|
121
packages/astro/src/runtime/client/dev-overlay/plugins/audit.ts
Normal file
121
packages/astro/src/runtime/client/dev-overlay/plugins/audit.ts
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
import type { DevOverlayItem } from '../../../../@types/astro.js';
|
||||||
|
import type { DevOverlayTooltip } from '../ui-toolkit.js';
|
||||||
|
|
||||||
|
interface AuditRule {
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectorBasedRules: (AuditRule & { selector: string })[] = [
|
||||||
|
{
|
||||||
|
title: 'Missing `alt` tag',
|
||||||
|
message: 'The alt attribute is important for accessibility.',
|
||||||
|
selector: 'img:not([alt])',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
id: 'astro:audit',
|
||||||
|
name: 'Audit',
|
||||||
|
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 16" fill="none"><path fill="#fff" d="M.625 2c0-.29837.118526-.58452.329505-.7955C1.16548.993526 1.45163.875 1.75.875h16.5c.2984 0 .5845.118526.7955.3295.211.21098.3295.49713.3295.7955 0 .29837-.1185.58452-.3295.7955-.211.21097-.4971.3295-.7955.3295H1.75c-.29837 0-.58452-.11853-.795495-.3295C.743526 2.58452.625 2.29837.625 2ZM1.75 9.125h6c.29837 0 .58452-.11853.7955-.32951.21097-.21097.3295-.49712.3295-.79549s-.11853-.58452-.3295-.7955c-.21098-.21097-.49713-.3295-.7955-.3295h-6c-.29837 0-.58452.11853-.795495.3295C.743526 7.41548.625 7.70163.625 8c0 .29837.118526.58452.329505.79549.210975.21098.497125.32951.795495.32951Zm7.5 3.75h-7.5c-.29837 0-.58452.1185-.795495.3295C.743526 13.4155.625 13.7016.625 14s.118526.5845.329505.7955c.210975.211.497125.3295.795495.3295h7.5c.29837 0 .58452-.1185.7955-.3295.211-.211.3295-.4971.3295-.7955s-.1185-.5845-.3295-.7955c-.21098-.211-.49713-.3295-.7955-.3295Zm11.2959 1.9209c-.1045.1049-.2287.1881-.3654.2449-.1368.0568-.2834.086-.4314.086-.1481 0-.2947-.0292-.4315-.086-.1367-.0568-.2609-.14-.3654-.2449l-1.695-1.695c-.8694.4849-1.8849.6389-2.859.4338s-1.8411-.7556-2.4412-1.5498c-.6001-.7943-.8927-1.7787-.8239-2.77183.0688-.99308.4944-1.92778 1.1983-2.63167.7039-.7039 1.6386-1.12951 2.6317-1.19832.9931-.06881 1.9775.22382 2.7718.82391.7942.60009 1.3447 1.46716 1.5498 2.44126.2051.9741.0511 1.98955-.4338 2.85895l1.695 1.6941c.1051.1045.1884.2287.2453.3656.0568.1368.0861.2835.0861.4317s-.0293.2949-.0861.4317c-.0569.1369-.1402.2611-.2453.3656ZM15.25 11.375c.3708 0 .7334-.11 1.0417-.316.3083-.206.5487-.4989.6906-.8415.1419-.34258.179-.71958.1067-1.0833-.0724-.36371-.251-.6978-.5132-.96003-.2622-.26222-.5963-.4408-.96-.51314-.3637-.07235-.7407-.03522-1.0833.1067-.3426.14191-.6355.38223-.8415.69058-.206.30834-.316.67085-.316 1.04169 0 .24623.0485.49005.1427.7175.0943.2275.2324.4342.4065.6083.1741.1741.3808.3122.6083.4065.2275.0942.4713.1427.7175.1427Z"/></svg>',
|
||||||
|
init(canvas) {
|
||||||
|
createBase();
|
||||||
|
|
||||||
|
selectorBasedRules.forEach((rule) => {
|
||||||
|
document.querySelectorAll(rule.selector).forEach((el) => {
|
||||||
|
canvas.appendChild(createAuditProblem(rule, el));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function createAuditProblem(rule: AuditRule, originalElement: Element) {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.className = 'astro-audit-problem';
|
||||||
|
el.style.position = 'absolute';
|
||||||
|
|
||||||
|
const rect = originalElement.getBoundingClientRect();
|
||||||
|
el.style.top = `${Math.max(rect.top - 10, 0)}px`;
|
||||||
|
el.style.left = `${Math.max(rect.left - 10, 0)}px`;
|
||||||
|
el.style.width = `${rect.width + 15}px`;
|
||||||
|
el.style.height = `${rect.height + 15}px`;
|
||||||
|
|
||||||
|
el.innerHTML = `
|
||||||
|
<div class="icon" style="left: ${rect.width}px;">
|
||||||
|
<svg width="16px" height="16px">
|
||||||
|
<use xlink:href="#astro:audit:warning" width="16px" height="16px"></use>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<astro-overlay-highlight></astro-overlay-highlight>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const tooltip = document.createElement('astro-overlay-tooltip') as DevOverlayTooltip;
|
||||||
|
tooltip.sections = [
|
||||||
|
{
|
||||||
|
icon: '<svg width="16px" height="16px"><use xlink:href="#astro:audit:warning" width="16px" height="16px"></use></svg>',
|
||||||
|
title: rule.title,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: rule.message,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '/src/somewhere/component.astro',
|
||||||
|
clickDescription: 'Click to go to file',
|
||||||
|
clickAction() {
|
||||||
|
// TODO: Implement this
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
tooltip.style.position = 'absolute';
|
||||||
|
tooltip.style.top = `${rect.height}px`;
|
||||||
|
tooltip.style.left = `${Math.max(rect.left - 10, 5)}px`;
|
||||||
|
tooltip.style.margin = '0';
|
||||||
|
|
||||||
|
el.appendChild(tooltip);
|
||||||
|
|
||||||
|
el.addEventListener('mouseover', () => {
|
||||||
|
tooltip.dialog.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
el.addEventListener('mouseout', () => {
|
||||||
|
tooltip.dialog.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBase() {
|
||||||
|
// Create style
|
||||||
|
const style = document.createElement('style');
|
||||||
|
// TODO: Should this be in the astro-overlay-highlight component?
|
||||||
|
style.innerHTML = `
|
||||||
|
.astro-audit-problem .icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background: linear-gradient(0deg, #B33E66, #B33E66), linear-gradient(0deg, #351722, #351722);
|
||||||
|
border: 1px solid rgba(53, 23, 34, 1);
|
||||||
|
border-radius: 9999px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
top: -15px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const svgSymbols = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||||
|
svgSymbols.setAttribute('aria-hidden', 'true');
|
||||||
|
svgSymbols.setAttribute(
|
||||||
|
'style',
|
||||||
|
'position: absolute; width: 0; height: 0; overflow: hidden;'
|
||||||
|
);
|
||||||
|
svgSymbols.innerHTML = `
|
||||||
|
<symbol viewBox="0 0 16 16" id="astro:audit:warning">
|
||||||
|
<path fill="#fff" d="M8 .40625c-1.5019 0-2.97007.445366-4.21886 1.27978C2.53236 2.52044 1.55905 3.70642.984293 5.094.40954 6.48157.259159 8.00842.552165 9.48147.845172 10.9545 1.56841 12.3076 2.63041 13.3696c1.06201 1.062 2.41508 1.7852 3.88813 2.0782 1.47304.293 2.99989.1427 4.38746-.4321 1.3876-.5747 2.5736-1.5481 3.408-2.7968.8344-1.2488 1.2798-2.717 1.2798-4.2189-.0023-2.0133-.8031-3.9435-2.2267-5.36713C11.9435 1.20925 10.0133.408483 8 .40625ZM8 13.9062c-1.16814 0-2.31006-.3463-3.28133-.9953-.97128-.649-1.7283-1.5715-2.17533-2.6507-.44703-1.0792-.56399-2.26675-.3361-3.41245.22789-1.1457.79041-2.1981 1.61641-3.0241.82601-.826 1.8784-1.38852 3.0241-1.61641 1.1457-.2279 2.33325-.11093 3.41245.3361 1.0793.44703 2.0017 1.20405 2.6507 2.17532.649.97128.9954 2.11319.9954 3.28134-.0017 1.56592-.6245 3.0672-1.7318 4.1745S9.56592 13.9046 8 13.9062Zm-.84375-5.62495V4.625c0-.22378.0889-.43839.24713-.59662.15824-.15824.37285-.24713.59662-.24713.22378 0 .43839.08889.59662.24713.15824.15823.24713.37284.24713.59662v3.65625c0 .22378-.08889.43839-.24713.59662C8.43839 9.03611 8.22378 9.125 8 9.125c-.22377 0-.43838-.08889-.59662-.24713-.15823-.15823-.24713-.37284-.24713-.59662ZM9.125 11.0938c0 .2225-.06598.44-.18959.625-.12362.185-.29932.3292-.50489.4143-.20556.0852-.43176.1074-.64999.064-.21823-.0434-.41869-.1505-.57602-.3079-.15734-.1573-.26448-.3577-.30789-.576-.04341-.2182-.02113-.4444.06402-.65.08515-.2055.22934-.3812.41435-.5049.185-.1236.40251-.18955.62501-.18955.29837 0 .58452.11855.7955.32955.21098.2109.3295.4971.3295.7955Z"/>
|
||||||
|
</symbol>
|
||||||
|
`;
|
||||||
|
|
||||||
|
canvas.appendChild(style);
|
||||||
|
canvas.appendChild(svgSymbols);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
} satisfies DevOverlayItem;
|
|
@ -0,0 +1,84 @@
|
||||||
|
import type { DevOverlayItem } from '../../../../@types/astro.js';
|
||||||
|
import type { DevOverlayTooltip } from '../ui-toolkit.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
id: 'astro:xray',
|
||||||
|
name: 'Xray',
|
||||||
|
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#fff" d="M7.875 1.5v-.375c0-.298369.11853-.584517.3295-.795495C8.41548.118526 8.70163 0 9 0c.29837 0 .58452.118526.7955.329505.211.210978.3295.497126.3295.795495V1.5c0 .29837-.1185.58452-.3295.7955-.21098.21097-.49713.3295-.7955.3295-.29837 0-.58452-.11853-.7955-.3295-.21097-.21098-.3295-.49713-.3295-.7955ZM1.5 10.125c.29837 0 .58452-.1185.7955-.3295.21097-.21098.3295-.49713.3295-.7955 0-.29837-.11853-.58452-.3295-.7955-.21098-.21097-.49713-.3295-.7955-.3295h-.375c-.298369 0-.584517.11853-.795495.3295C.118526 8.41548 0 8.70163 0 9c0 .29837.118526.58452.329505.7955.210978.211.497126.3295.795495.3295H1.5Zm10.5187-6.43313c.1402.04674.2882.0654.4356.05493.1474-.01046.2912-.04986.4234-.11594.1321-.06607.25-.15753.3468-.26916.0968-.11162.1707-.24122.2174-.38139l.375-1.125c.084-.28016.0555-.58202-.0793-.84158-.1348-.259563-.3654-.45642-.6429-.548838-.2775-.092417-.58-.07313-.8436.053773-.2635.126903-.4672.351445-.568.626025l-.375 1.125c-.0941.28283-.0721.59146.0611.85812.1332.26665.3669.46952.6495.56406ZM2.26875 11.3081l-1.125.375c-.144256.0433-.27836.115-.394363.2111-.116003.096-.211543.2144-.280956.348-.069412.1337-.111285.2799-.123135.43-.01185.1502.006564.3011.054149.444.047586.1429.123375.2748.222875.3878.099499.1131.220683.205.356368.2703.135682.0654.283112.1028.433532.1101.15042.0073.30078-.0156.44216-.0675l1.125-.375c.14425-.0433.27836-.115.39436-.2111.116-.096.21154-.2144.28095-.348.06942-.1337.11129-.2799.12314-.43.01185-.1502-.00656-.3011-.05415-.444-.04759-.1429-.12338-.2748-.22287-.3878-.0995-.1131-.22069-.205-.35637-.2703-.13569-.0654-.28311-.1028-.43353-.1101-.15042-.0073-.30078.0156-.44216.0675Zm18.55595 5.6766c.1742.1741.3124.3808.4066.6084.0943.2275.1428.4714.1428.7177 0 .2463-.0485.4902-.1428.7177-.0942.2275-.2324.4343-.4066.6084l-1.1888 1.1887c-.1741.1742-.3808.3124-.6084.4067-.2275.0942-.4714.1428-.7177.1428-.2462 0-.4901-.0486-.7177-.1428-.2275-.0943-.4342-.2325-.6084-.4067l-4.2665-4.2665-1.6041 3.6909c-.1436.3349-.3826.6201-.6872.8201-.3045.2001-.66121.3061-1.02559.3049h-.09375c-.3792-.0165-.74423-.1489-1.04594-.3792-.30172-.2303-.52562-.5475-.64156-.9089L2.71875 5.0775c-.10548-.32823-.11842-.6792-.0374-1.01431s.25287-.64139.49666-.88518c.24379-.24379.55007-.41564.88518-.49666.33511-.08102.68608-.06808 1.01431.0374L20.085 7.61906c.3591.11962.6736.34511.9021.64684.2286.30172.3604.66555.3783 1.04363.0178.37808-.0792.75267-.2782 1.07467-.1991.3219-.491.576-.8372.7289l-3.6909 1.605 4.2656 4.2666Zm-1.8563 1.3256-4.3903-4.3912c-.2158-.2161-.3755-.4817-.4653-.7736-.0898-.2919-.1069-.6013-.0499-.9013.057-.3001.1864-.5816.377-.8202.1906-.2387.4366-.4271.7167-.549l3.2812-1.42594L5.08969 5.08969 9.44812 18.4369l1.42598-3.2813c.122-.28.3105-.5259.5492-.7164.2388-.1905.5204-.3198.8205-.3767.1155-.0224.2329-.0337.3506-.0338.4969.0004.9733.198 1.3247.5494l4.3912 4.3913.6581-.6591Z"/></svg>',
|
||||||
|
init(canvas) {
|
||||||
|
const islands = document.querySelectorAll<HTMLElement>('astro-island');
|
||||||
|
|
||||||
|
islands.forEach((island) => {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.style.position = 'absolute';
|
||||||
|
|
||||||
|
const rect = island.children[0]
|
||||||
|
? island.children[0].getBoundingClientRect()
|
||||||
|
: island.getBoundingClientRect();
|
||||||
|
|
||||||
|
el.style.top = `${Math.max(rect.top - 10, 0)}px`;
|
||||||
|
el.style.left = `${Math.max(rect.left - 10, 0)}px`;
|
||||||
|
el.style.width = `${rect.width + 15}px`;
|
||||||
|
el.style.height = `${rect.height + 15}px`;
|
||||||
|
|
||||||
|
el.innerHTML = `
|
||||||
|
<astro-overlay-highlight></astro-overlay-highlight>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const islandProps = island.getAttribute('props')
|
||||||
|
? JSON.parse(island.getAttribute('props')!)
|
||||||
|
: {};
|
||||||
|
const islandClientDirective = island.getAttribute('client');
|
||||||
|
|
||||||
|
const tooltip = document.createElement('astro-overlay-tooltip') as DevOverlayTooltip;
|
||||||
|
tooltip.sections = [];
|
||||||
|
|
||||||
|
if (islandClientDirective) {
|
||||||
|
tooltip.sections.push({
|
||||||
|
title: 'Client directive',
|
||||||
|
content: `client:${islandClientDirective}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(islandProps).length > 0) {
|
||||||
|
tooltip.sections.push({
|
||||||
|
title: 'Props',
|
||||||
|
content: `${Object.entries(islandProps)
|
||||||
|
.map((prop) => `<code>${prop[0]}=${getPropValue(prop[1] as any)}</code>`)
|
||||||
|
.join(', ')}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltip.sections.push({
|
||||||
|
content: '/src/somewhere/component.astro',
|
||||||
|
clickDescription: 'Click to go to file',
|
||||||
|
clickAction() {
|
||||||
|
// TODO: Implement this
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
tooltip.style.position = 'absolute';
|
||||||
|
tooltip.style.top = `${rect.height}px`;
|
||||||
|
tooltip.style.left = `${Math.max(rect.left - 10, 5)}px`;
|
||||||
|
tooltip.style.margin = '0';
|
||||||
|
|
||||||
|
el.appendChild(tooltip);
|
||||||
|
|
||||||
|
el.addEventListener('mouseover', () => {
|
||||||
|
tooltip.dialog.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
el.addEventListener('mouseout', () => {
|
||||||
|
tooltip.dialog.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.appendChild(el);
|
||||||
|
});
|
||||||
|
|
||||||
|
function getPropValue(prop: [number, any]) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const [_, value] = prop;
|
||||||
|
return JSON.stringify(value, null, 2);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
} satisfies DevOverlayItem;
|
148
packages/astro/src/runtime/client/dev-overlay/ui-toolkit.ts
Normal file
148
packages/astro/src/runtime/client/dev-overlay/ui-toolkit.ts
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
export class DevOverlayWindow extends HTMLElement {
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.title = 'World';
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectedCallback() {
|
||||||
|
const shadow = this.attachShadow({ mode: 'closed' });
|
||||||
|
shadow.innerHTML = `
|
||||||
|
<style>
|
||||||
|
#astro-dev-window {
|
||||||
|
background: linear-gradient(0deg, #13151A, #13151A), linear-gradient(0deg, #343841, #343841);
|
||||||
|
}
|
||||||
|
|
||||||
|
#astro-dev-window h1 {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="astro-dev-window">
|
||||||
|
<h1>${this.title}</h1>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DevOverlayTooltipSection {
|
||||||
|
title?: string;
|
||||||
|
icon?: string;
|
||||||
|
content?: string;
|
||||||
|
clickAction?: () => void;
|
||||||
|
clickDescription?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DevOverlayTooltip extends HTMLElement {
|
||||||
|
sections: DevOverlayTooltipSection[] = [];
|
||||||
|
dialog: HTMLDialogElement;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.dialog = document.createElement('dialog');
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.style.width = '100%';
|
||||||
|
this.innerHTML = `
|
||||||
|
<style>
|
||||||
|
dialog {
|
||||||
|
color: white;
|
||||||
|
background: linear-gradient(0deg, #310A65, #310A65), linear-gradient(0deg, #7118E2, #7118E2);
|
||||||
|
border: 1px solid rgba(113, 24, 226, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0;
|
||||||
|
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog svg {
|
||||||
|
vertical-align: bottom;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog hr {
|
||||||
|
border: 1px solid rgba(136, 58, 234, 0.33);
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog section {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-weight: bold;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title + div {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-cta {
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable-section {
|
||||||
|
background: rgba(113, 24, 226, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: rgba(136, 58, 234, 0.33);
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.sections.forEach((section, index) => {
|
||||||
|
const el = document.createElement('section');
|
||||||
|
if (section.clickAction) {
|
||||||
|
el.classList.add('clickable-section');
|
||||||
|
el.addEventListener('click', section.clickAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.innerHTML = `
|
||||||
|
${section.icon ? `${section.icon}` : ''}
|
||||||
|
${section.title ? `<span class="modal-title">${section.title}</span>` : ''}
|
||||||
|
${section.content ? `<div>${section.content}</div>` : ''}
|
||||||
|
${section.clickDescription ? `<span class="modal-cta">${section.clickDescription}</span>` : ''}
|
||||||
|
`;
|
||||||
|
this.dialog.appendChild(el);
|
||||||
|
|
||||||
|
if (index < this.sections.length - 1) {
|
||||||
|
this.dialog.appendChild(document.createElement('hr'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.appendChild(this.dialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DevOverlayHighlight extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({ mode: 'open' });
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.innerHTML = `
|
||||||
|
<style>
|
||||||
|
astro-overlay-highlight {
|
||||||
|
background: linear-gradient(180deg, rgba(224, 204, 250, 0.33) 0%, rgba(224, 204, 250, 0.0825) 100%);
|
||||||
|
border: 1px solid rgba(113, 24, 226, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ import { loadMiddleware } from '../core/middleware/loadMiddleware.js';
|
||||||
import { createRenderContext, getParamsAndProps, type SSROptions } from '../core/render/index.js';
|
import { createRenderContext, getParamsAndProps, type SSROptions } from '../core/render/index.js';
|
||||||
import { createRequest } from '../core/request.js';
|
import { createRequest } from '../core/request.js';
|
||||||
import { matchAllRoutes } from '../core/routing/index.js';
|
import { matchAllRoutes } from '../core/routing/index.js';
|
||||||
import { isPage } from '../core/util.js';
|
import { isPage, resolveIdToUrl } from '../core/util.js';
|
||||||
import { getSortedPreloadedMatches } from '../prerender/routing.js';
|
import { getSortedPreloadedMatches } from '../prerender/routing.js';
|
||||||
import { isServerLikeOutput } from '../prerender/utils.js';
|
import { isServerLikeOutput } from '../prerender/utils.js';
|
||||||
import { PAGE_SCRIPT_ID } from '../vite-plugin-scripts/index.js';
|
import { PAGE_SCRIPT_ID } from '../vite-plugin-scripts/index.js';
|
||||||
|
@ -275,6 +275,13 @@ async function getScriptsAndStyles({ pipeline, filePath }: GetScriptsAndStylesPa
|
||||||
props: { type: 'module', src: '/@vite/client' },
|
props: { type: 'module', src: '/@vite/client' },
|
||||||
children: '',
|
children: '',
|
||||||
});
|
});
|
||||||
|
scripts.add({
|
||||||
|
props: {
|
||||||
|
type: 'module',
|
||||||
|
src: await resolveIdToUrl(moduleLoader, 'astro/runtime/client/dev-overlay/overlay.js'),
|
||||||
|
},
|
||||||
|
children: '',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We should allow adding generic HTML elements to the head, not just scripts
|
// TODO: We should allow adding generic HTML elements to the head, not just scripts
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import type * as vite from 'vite';
|
||||||
|
import type { AstroPluginOptions } from '../@types/astro.js';
|
||||||
|
|
||||||
|
const VIRTUAL_MODULE_ID = 'astro:dev-tools';
|
||||||
|
const resolvedVirtualModuleId = '\0' + VIRTUAL_MODULE_ID;
|
||||||
|
|
||||||
|
export default function astroDevTools({ settings }: AstroPluginOptions): vite.Plugin {
|
||||||
|
return {
|
||||||
|
name: 'astro:dev-tools',
|
||||||
|
resolveId(id) {
|
||||||
|
if (id === VIRTUAL_MODULE_ID) {
|
||||||
|
return resolvedVirtualModuleId;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
load(id) {
|
||||||
|
if (id === resolvedVirtualModuleId) {
|
||||||
|
return `
|
||||||
|
export const loadDevToolsPlugins = async () => {
|
||||||
|
return [${settings.config.devTools.plugins.map((p) => `(await import('${p}')).default`).join(',')}];
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
configureServer(server) {
|
||||||
|
// Example: wait for a client to connect before sending a message
|
||||||
|
server.ws.on('connection', () => {
|
||||||
|
server.ws.send('astro-dev-tools', { msg: 'hello' });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -149,6 +149,18 @@ importers:
|
||||||
specifier: ^3.2.3
|
specifier: ^3.2.3
|
||||||
version: link:../../packages/astro
|
version: link:../../packages/astro
|
||||||
|
|
||||||
|
examples/dev-overlay:
|
||||||
|
dependencies:
|
||||||
|
'@astrojs/solid-js':
|
||||||
|
specifier: ^3.0.1
|
||||||
|
version: link:../../packages/integrations/solid
|
||||||
|
astro:
|
||||||
|
specifier: ^3.2.3
|
||||||
|
version: link:../../packages/astro
|
||||||
|
solid-js:
|
||||||
|
specifier: ^1.7.11
|
||||||
|
version: 1.7.11
|
||||||
|
|
||||||
examples/framework-alpine:
|
examples/framework-alpine:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/alpinejs':
|
'@astrojs/alpinejs':
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowJs": true
|
"allowJs": true
|
||||||
},
|
},
|
||||||
|
"include": [".eslintrc.cjs"],
|
||||||
"extends": "./tsconfig.base.json"
|
"extends": "./tsconfig.base.json"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue