Skip to content

Commit

Permalink
docs: Add docs for createContentScriptUi
Browse files Browse the repository at this point in the history
  • Loading branch information
aklinker1 committed Oct 1, 2023
1 parent 2d4983e commit 65fcfc0
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 21 deletions.
2 changes: 1 addition & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# API Reference

:::warning 🚧 Under construction
This documentation does not exist yet.
This documentation does not exist yet. All APIs are documented with JSDoc, so for now, you can view the documentation in your editor.
:::
1 change: 1 addition & 0 deletions docs/guide/auto-imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Some WXT APIs can be used without importing them:
- [`browser`](/config.md#browser) from `wxt/browser`, a small wrapper around `webextension-polyfill`
- [`defineContentScript`](/config.md#defiencontentscript) from `wxt/client`
- [`defineBackground`](/config.md#definebackgroundscript) from `wxt/client`
- [`createContentScriptUi`](/config.md#createcontentscriptui) from `wxt/client`

And more. All [`wxt/client`](/config.md#wxtclient) APIs can be used without imports.

Expand Down
207 changes: 187 additions & 20 deletions docs/guide/content-scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,43 @@ export default defineContentScript({
When defining multiple content scripts, content script entrypoints that have the same set of options will be merged into a single `content_script` item in the manifest.

## Context

Old content scripts are not automatically stopped when an extension updates and reloads. Often, this leads to "Invalidated context" errors in production when a content script from an old version of your extension tries to use a extension API.

WXT provides a utility for managing this process: `ContentScriptContext`. An instance of this class is provided to you automatically inside the `main` function of your content script.

```ts
export default defineContentScript({
// ...
main(ctx: ContentScriptContext) {
// Add custom listeners for stopping work
ctx.onInvalidated(() => {
// ...
});

// Stop fetch requests
fetch('...url', { signal: ctx.signal });

// Timeout utilities
ctx.setTimeout(() => {
// ...
}, 5e3);
ctx.setInterval(() => {
// ...
}, 60e3);
},
});
```

The class extends [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) and provides other utilities for stopping a content script's logic once it becomes invalidated.

:::tip
When working with content scripts, **you should always use the `ctx` object to stop any async or future work.**

This prevents old content scripts from interfering with new content scripts, and prevents error messages from the console in production.
:::

## CSS

To include CSS with your content script, import the CSS file at the top of your entrypoint.
Expand All @@ -66,6 +103,7 @@ import './style.css';

export default defineContentScript({
matches: ['*://google.com/*', '*://duckduckgo.com/*'],

main(ctx) {
// ...
},
Expand All @@ -87,39 +125,168 @@ Any styles imported in your content script will be added to that content script'
}
```

## Context
To disable this behavior, set `cssInjectionMode` to `"manual"` or `"ui"`.

Old content scripts are not automatically stopped when an extension updates and restarts. Often, this leads to "Invalidated context" errors in production when a content script from an old version of your extension tries to use a web extension API. Since it's not connected to the latest version of your extension, the browser decides to throw an error.
```ts
export default defineContentScript({
matches: ['*://google.com/*', '*://duckduckgo.com/*'],
cssInjectionMode: 'manual',

WXT provides a utility for managing this process: `ContentScriptContext`. An instance of this class is provided to you automatically inside the `main` function of your content script.
main(ctx) {
// ...
},
});
```

## UI

WXT provides a utility function, `createContentScriptUi` to simplify mounting a UI from a content script. Internally, it uses the `ShadowRoot` API to isolate your CSS from the webpages.

`createContentScriptUi` requires a `ContentScriptContext` so that when the context is invalidated, the UI is automatically removed from the webpage.

:::details When to use `createContentScriptUi`
You should only use `createContentScriptUi` if you want your UI's styles isolated from the webpages. If you want to create a more "integrated" UI that uses the page's styles, you can just use the regular JS API's to append your UI to the page.

```ts
const ui = document.createElement('div');
const anchor = document.querySelector('#anchor-selector');
anchor.append(ui);
```

:::

### Usage

To use `createContentScriptUi`, follow these steps:

1. Import your CSS file at the top of your content script
2. Set `cssInjectionMode: "ui"` inside `defineContentScript`
3. Call `createContentScriptUi`
4. Call `ui.mount()` to add the UI to the webpage

Here's a basic example:

```ts
// entrypoints/ui.content/index.ts
import './style.css';

export default defineContentScript({
// ...
main(ctx: ContentScriptContext) {
// Add custom listeners for stopping work
ctx.onInvalidated(() => {
// ...
cssInjectionMode: 'ui',

async main(ctx) {
const ui = await createContentScriptUi(ctx, {
name: 'example-ui',
type: 'inline',
anchor: '#some-element',
append: 'after',
mount(container) {
// Mount UI inside `container`...
},
});

// Stop fetch requests
fetch('...url', { signal: ctx.signal });
// Yoy must call `mount` to add the UI to the page.
ui.mount();
},
});
```

// Timeout utilities
ctx.setTimeout(() => {
// ...
}, 5e3);
ctx.setInterval(() => {
// ...
}, 60e3);
If you're using a frontend framework, you'll also need to include an `onRemoved` callback:

:::code-group

```ts [Vue]
import { createApp } from 'vue';

createContentScriptUi(ctx, {
// ...
mount(container) {
// Create a new app and mount it inside the container
const app = createApp(...);
app.mount(container);
return app;
},
onRemove(app) {
// When the UI is removed from the DOM, call unmount to stop the app
app.unmount();
},
});
```

The class extends [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) and provides other utilities for stopping a content script's logic once it becomes invalidated.
```ts [React]
import ReactDOM from 'react-dom/client';

:::tip
When working with content scripts, **you should always use the `ctx` object to stop any async or future work.**
createContentScriptUi(ctx, {
// ...
mount(container) {
// Create a root using the container and render your app
const root = ReactDOM.createRoot(container);
root.render(...);
return root;
},
onRemove(root) {
// When the UI is removed from the DOM, call unmount to stop the app
root.unmount();
},
});
```

```ts [Svelte]
import App from './App.svelte';

createContentScriptUi(ctx, {
// ...
mount(container) {
// Mount your app component inside the container
return new App({
target: container,
});
},
onRemove(app) {
// When the UI is removed from the DOM, call $destroy to stop the app
app.$destroy();
},
});
```

```ts [Solid]
import { render } from 'solid-js/web';

createContentScriptUi(ctx, {
// ...
mount(container) {
// Render your app component into the container
return render(() => ..., container)
},
onRemove(unmount) {
// When the UI is removed from the DOM, call unmount to stop the app
unmount();
},
});
```

This prevents old content scripts from interfering with new content scripts, and prevents error messages from the console in production.
:::

### `anchor`

The anchor dictates where the UI will be mounted.

### `append`

Customize where the UI get's appended to the DOM, relative to the `anchor` element.

### `type`

There are 3 types of UI's you can mount.

- `inline`: Shows up inline based on the `anchor` and `append` options
- `overlay`: Shows up inline, but styled to be 0px by 0px, with overflow visible. This causes the UI to overlay on top of the webpage's content
- `modal`: A fullscreen overlay that covers the entire screen, regardless of where it's anchored.

> TODO: Add visualization of the different UI types.
### Overlay `alignment`

Because the overlay UI type results in a 0px by 0px container being added to the webpage, the `alignment` option allows you to configure which corner of your UI is aligned with the 0x0 element.

> TODO: Add visualization of the different alignments.

0 comments on commit 65fcfc0

Please sign in to comment.