Skip to content

Support shadow root #617

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,80 @@ npm run start:debugServer

See the [VSCode Server](https://github.com/CodinGame/monaco-vscode-api/wiki/How-to-install-and-use-VSCode-server-with-monaco‐vscode‐api) wiki page.

## Shadow dom (⚠️ beta ⚠️)

The library supports shadow-dom.

⚠️ VSCode itself doesn't support shadow dom, and there are multiple parts that needed to be patched in order for it to work.

There are multiple benefits of using it:
- Your custom global style won't impact the VSCode workbench style (for instance if you did override the default [box-sizing](https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing))
- The VSCode styles won't impact other parts of your app
- You page head won't be polluted with dozen of css files from VSCode

### How to use it

If the provided container element is a child of a shadow dom element, the styles will be injected in both the main page and the shadow root. That's it.

### ⚠️ Xterm notice ⚠️

The webgl renderer of xterm, which VSCode is using, doesn't support being used inside a shadom dom (see https://github.com/xtermjs/xterm.js/pull/5334).
Until it's fixed and deployed, either do not use the terminal service override, or apply the patch on your side (using [patch-package](https://www.npmjs.com/package/patch-package) for instance).

### Prerequisites

In order to be able to load the static css files in the shadow dom as well. Your bundler configuration needs to be adapted so that importing css files doesn't load their content in the page head, but instead just returns the file content as default (either as a string or a `CSSStyleSheet`). It can be achieved with most bundlers with some configurations.

Note that the bundler should still resolve referenced assets in the css files, so you can't just use the `raw` loader, or the `assets/source` webpack module type.

#### Webpack

Add this rule in your configuration:

```typescript
{
test: /node_modules\/(@codingame\/monaco-vscode|vscode|monaco-editor).*\.css$/,
use: [
{
loader: 'css-loader',
options: {
esModule: false,
exportType: 'css-style-sheet', // or 'string', both are working
url: true,
import: true
}
}
]
}
```

You should also make sure that no other loader is interfering with it, by either use a `oneOf` or exclusing those files in the other css loaders.

#### Vite

Add this plugin in your configuration:

```typescript
{
name: 'load-vscode-css-as-string',
enforce: 'pre',
async resolveId(source, importer, options) {
const resolved = (await this.resolve(source, importer, options))!
if (
resolved.id.match(
/node_modules\/(@codingame\/monaco-vscode|vscode|monaco-editor).*\.css$/
)
) {
return {
...resolved,
id: resolved.id + '?inline'
}
}
return undefined
}
}
```

## Troubleshooting

If something doesn't work, make sure to check out the [Troubleshooting](https://github.com/CodinGame/monaco-vscode-api/wiki/Troubleshooting) wiki page.
Expand Down
1,441 changes: 1,133 additions & 308 deletions demo/package-lock.json

Large diffs are not rendered by default.

51 changes: 32 additions & 19 deletions demo/package.json

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions demo/patches/@xterm+addon-webgl+0.19.0-beta.99.patch

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions demo/src/main.workbench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,13 @@ document.querySelector('#customEditorPanel')!.addEventListener('click', async ()
document.querySelector('#clearStorage')!.addEventListener('click', async () => {
await clearStorage()
})

document.querySelector('#toggleShadowDom')!.addEventListener('click', async () => {
const url = new URL(window.location.href)
if (url.searchParams.has('disableShadowDom')) {
url.searchParams.delete('disableShadowDom')
} else {
url.searchParams.set('disableShadowDom', 'true')
}
window.location.href = url.toString()
})
1 change: 1 addition & 0 deletions demo/src/setup.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export const remotePath =
remoteAuthority != null ? (params.get('remotePath') ?? undefined) : undefined
export const resetLayout = params.has('resetLayout')
export const useHtmlFileSystemProvider = params.has('htmlFileSystemProvider')
export const disableShadowDom = params.has('disableShadowDom')
params.delete('resetLayout')

window.history.replaceState({}, document.title, url.href)
Expand Down
17 changes: 15 additions & 2 deletions demo/src/setup.workbench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,30 @@ import {
constructOptions,
envOptions,
remoteAuthority,
userDataProvider
userDataProvider,
disableShadowDom
} from './setup.common'

const container = document.createElement('div')
let container = document.createElement('div')
container.style.height = '100vh'

document.body.replaceChildren(container)

if (!disableShadowDom) {
const shadowRoot = container.attachShadow({
mode: 'open'
})

const workbenchElement = document.createElement('div')
workbenchElement.style.height = '100vh'
shadowRoot.appendChild(workbenchElement)
container = workbenchElement
}

const buttons = document.createElement('div')
buttons.innerHTML = `
<button id="toggleHTMLFileSystemProvider">Toggle HTML filesystem provider</button>
<button id="toggleShadowDom">Toggle Shadow Dom usage</button>
<button id="customEditorPanel">Open custom editor panel</button>
<button id="clearStorage">Clear user data</button>
<button id="resetLayout">Reset layout</button>
Expand Down
18 changes: 18 additions & 0 deletions demo/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ export default defineConfig({
format: 'es'
},
plugins: [
{
name: 'load-vscode-css-as-string',
enforce: 'pre',
async resolveId(source, importer, options) {
const resolved = (await this.resolve(source, importer, options))!
if (
resolved.id.match(
/node_modules\/(@codingame\/monaco-vscode|vscode|monaco-editor).*\.css$/
)
) {
return {
...resolved,
id: resolved.id + '?inline'
}
}
return undefined
}
},
{
// For the *-language-features extensions which use SharedArrayBuffer
name: 'configure-response-headers',
Expand Down
20 changes: 20 additions & 0 deletions demo/vite.netlify.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@ export default defineConfig({
target: 'esnext',
assetsInlineLimit: 0
},
plugins: [
{
name: 'load-vscode-css-as-string',
enforce: 'pre',
async resolveId(source, importer, options) {
const resolved = (await this.resolve(source, importer, options))!
if (
resolved.id.match(
/node_modules\/(@codingame\/monaco-vscode|vscode|monaco-editor).*\.css$/
)
) {
return {
...resolved,
id: resolved.id + '?inline'
}
}
return undefined
}
}
],
worker: {
format: 'es'
},
Expand Down
6 changes: 6 additions & 0 deletions docs/vscode_monaco_upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
- Update dependencies
- Implement improvements dependening on the new features available from vscode (optional)
- Don't forget to check the `Window` output (in the `OUTPUT` panel tab) to check for errors
- Check all possible combinations
- Full workbench mode or not
- Shadow dom mode or not
- Using VSCode server
- Using HTML file system provider
- ...

## monaco-vscode-api demo

Expand Down
Loading