Skip to content
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
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,32 @@ Add this plugin in your configuration:
}
```

## Sandbox mode (⚠️ beta ⚠️)

One issue with VSCode is it's only designed to be initialized once. So the initialization options (workbench options, remote server authority...) can't be updated/reloaded. Also it's not possible to "unload" the services.

To still be able to do it, a possibility is to run all VSCode code inside an iframe instead of in the main page. But then VSCode will render in the iframe only and won't be well integrated in the page.

To better integrate it, it's also possible to run the code in the iframe, but make the code interact with the main page dom.

This library supports that mode. To enable that, you should
- have a secondary html entrypoint, that initialize the services
- load that secondary html in an iframe
- in the iframe, set `window.vscodeWindow` to the parent window, also initialize the service with a container mounted in that window
- do not import any monaco-vscode-library from the top window, but you can declare functions on the iframe window to get objects to the top window

To "unload" the workbench, you should:
- remove the iframe element from the top frame
- remove or empty the workbench container
- cleanup the elements that VSCode has injected in the page head: `document.querySelectorAll('[data-vscode]').forEach((el) => el.remove())`

⚠️ `window.vscodeWindow` should be set BEFORE any VSCode code is loaded


Note: it can be used in combination with shadow dom

It's demonstrated in the demo, by adding `?sandbox` query parameter to the demo url

## 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
2 changes: 1 addition & 1 deletion demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>monaco-vscode-api demo</title>
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<script type="module" src="/src/loader.ts"></script>
<script type="module" src="/src/entry.ts"></script>
</head>
<body>
</body>
Expand Down
6 changes: 3 additions & 3 deletions demo/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,22 @@
},
"devDependencies": {
"@codingame/esbuild-import-meta-url-plugin": "^1.0.3",
"@types/dockerode": "^3.3.41",
"@types/dockerode": "^3.3.42",
"@types/express": "^5.0.3",
"@types/node": "^20.11.4",
"@types/wicg-file-system-access": "^2023.10.6",
"@types/ws": "^8.18.1",
"patch-package": "^8.0.0",
"typescript": "~5.8.3",
"vite": "~7.0.0",
"vite": "~7.0.6",
"@codingame/monaco-vscode-rollup-extension-directory-plugin": "file:../dist/packages/monaco-vscode-rollup-extension-directory-plugin",
"@codingame/monaco-vscode-rollup-vsix-plugin": "file:../dist/packages/monaco-vscode-rollup-vsix-plugin"
},
"dependencies": {
"ansi-colors": "^4.1.3",
"dockerode": "^4.0.7",
"express": "^5.1.0",
"ws": "^8.18.2",
"ws": "^8.18.3",
"@codingame/monaco-vscode-07eaa805-9dea-5ec6-a422-a4f04872424d-common": "file:../dist/packages/monaco-vscode-07eaa805-9dea-5ec6-a422-a4f04872424d-common",
"@codingame/monaco-vscode-0b087f42-a5a3-5eb9-9bfd-1eebc1bba163-common": "file:../dist/packages/monaco-vscode-0b087f42-a5a3-5eb9-9bfd-1eebc1bba163-common",
"@codingame/monaco-vscode-0c06bfba-d24d-5c4d-90cd-b40cefb7f811-common": "file:../dist/packages/monaco-vscode-0c06bfba-d24d-5c4d-90cd-b40cefb7f811-common",
Expand Down
12 changes: 12 additions & 0 deletions demo/src/entry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const searchParams = new URLSearchParams(window.location.search)

const sandbox = searchParams.has('sandbox')
if (sandbox) {
;(async () => {
await import('./sandbox')
})()
} else {
;(async () => {
await import('./loader')
})()
}
23 changes: 19 additions & 4 deletions demo/src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,26 @@ if (locale != null) {
}

const mode = searchParams.get('mode')
const sandboxed = searchParams.has('sandboxed')

if (mode === 'full-workbench') {
void import('./main.workbench')
} else {
void import('./main.views')
;(async () => {
if (sandboxed) {
window.vscodeContainer = await new Promise<HTMLElement>((resolve) => {
window.start = resolve
window.parent.postMessage('WAITING')
})
window.vscodeWindow = window.vscodeContainer.ownerDocument.defaultView!
}
if (mode === 'full-workbench') {
await import('./main.workbench')
} else {
await import('./main.views')
}
})()

declare global {
var vscodeContainer: HTMLElement
var start: (container: HTMLElement) => void
}

export {}
101 changes: 101 additions & 0 deletions demo/src/sandbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import './style.css'

const searchParams = new URLSearchParams()
searchParams.set('mode', 'full-workbench')
searchParams.set('sandboxed', '')

const container = document.createElement('div')
container.style.height = '100vh'
container.style.display = 'flex'
container.style.flexDirection = 'column'

document.body.appendChild(container)

function load(): Disposable {
const wrapper = document.createElement('div')
wrapper.style.flex = '1'
wrapper.style.display = 'flex'
container.append(wrapper)
const shadowRoot = wrapper.attachShadow({
mode: 'open'
})

const workbenchElement = document.createElement('div')
workbenchElement.style.position = 'relative'
workbenchElement.style.flex = '1'
workbenchElement.style.maxWidth = '100%'
shadowRoot.appendChild(workbenchElement)

const loader = document.createElement('div')
loader.style.position = 'absolute'
loader.style.left = '0'
loader.style.right = '0'
loader.style.bottom = '0'
loader.style.top = '0'
loader.style.display = 'flex'
loader.style.alignItems = 'center'
loader.style.justifyContent = 'center'
loader.style.border = '1px solid red'
loader.textContent = 'Loading...'
workbenchElement.appendChild(loader)

const iframe = document.createElement('iframe')
iframe.src = window.location.origin + '?' + searchParams?.toString()
iframe.loading = 'eager'
iframe.style.display = 'none'
document.body.appendChild(iframe)

window.addEventListener('message', (event) => {
if (event.data === 'WAITING' && event.source === iframe.contentWindow) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(iframe.contentWindow as any)?.start(workbenchElement)
}
})

return {
[Symbol.dispose]() {
iframe.remove()
wrapper.remove()
document.querySelectorAll('[data-vscode]').forEach((el) => el.remove())
}
}
}

let disposable = load()

function reload() {
console.log('reloading...')
disposable[Symbol.dispose]()
disposable = load()
}

const buttons = document.createElement('div')

const serverUrlInput = document.createElement('input')
serverUrlInput.style.width = '350px'
serverUrlInput.type = 'text'
serverUrlInput.placeholder = 'remoteAuthority/remotePath?'
serverUrlInput.addEventListener('change', () => {
searchParams.delete('remotePath')
searchParams.delete('remoteAuthority')
if (serverUrlInput.value.trim().length > 0) {
const url = new URL('ws://' + serverUrlInput.value)
searchParams.append('remoteAuthority', url.host)
if (url.pathname.length > 0) {
searchParams.append('remotePath', url.pathname)
}
}
reload()
})
buttons.appendChild(serverUrlInput)

const reinitializeButton = document.createElement('button')
reinitializeButton.textContent = 'Reinitialize the workbench'
reinitializeButton.addEventListener('click', reload)
buttons.appendChild(reinitializeButton)

container.prepend(buttons)

const header = document.createElement('h1')
header.textContent = 'Sandbox mode: reinitialize the workbench without reloading the page'
container.prepend(header)
35 changes: 23 additions & 12 deletions demo/src/setup.workbench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,24 @@ import {
disableShadowDom
} from './setup.common'

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

document.body.replaceChildren(container)
if (container == null) {
container = document.createElement('div')
container.style.height = '100vh'

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

const workbenchElement = document.createElement('div')
workbenchElement.style.height = '100vh'
shadowRoot.appendChild(workbenchElement)
container = workbenchElement
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')
Expand All @@ -48,7 +52,7 @@ buttons.innerHTML = `
<br />
<button id="togglePanel">Toggle Panel</button>
<button id="toggleAuxiliary">Toggle Secondary Panel</button>
</div>
<button id="toggleSandbox">Switch to sandbox rendering mode</button>
`
document.body.append(buttons)

Expand Down Expand Up @@ -79,6 +83,13 @@ document.querySelector('#toggleAuxiliary')!.addEventListener('click', async () =
)
})

document.querySelector('#toggleSandbox')!.addEventListener('click', async () => {
const url = new URL(window.location.href)
url.search = ''
url.searchParams.append('sandbox', '')
window.location.href = url.toString()
})

export async function clearStorage(): Promise<void> {
await userDataProvider.reset()
await ((await getService(IStorageService)) as BrowserStorageService).clear()
Expand Down
1 change: 1 addition & 0 deletions docs/vscode_monaco_upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- Shadow dom mode or not
- Using VSCode server
- Using HTML file system provider
- Sandbox mode
- ...
- Make the commit that updates the vscode version a breaking change commit: by adding `!` before the `:` in the commit message

Expand Down
27 changes: 12 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading