mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-11 00:06:06 +00:00
* fix(frontend): stop swallowing Ctrl/Cmd+Shift+S in the editors Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(frontend): make Ctrl/Cmd+S from a focused Monaco flush the draft Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(frontend): broadcast the Monaco save shortcut after the effect flush Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
348 lines
9.8 KiB
TypeScript
348 lines
9.8 KiB
TypeScript
import '@codingame/monaco-vscode-standalone-typescript-language-features'
|
|
|
|
import { editor as meditor, KeyCode, KeyMod, Uri as mUri } from 'monaco-editor'
|
|
import { getAppliedDarkModeVariant } from '$lib/darkModeVariant'
|
|
|
|
export let isInitialized = false
|
|
export let isInitializing = false
|
|
|
|
import {
|
|
getEnhancedMonacoEnvironment,
|
|
MonacoVscodeApiWrapper
|
|
} from 'monaco-languageclient/vscodeApiWrapper'
|
|
import getLanguagesServiceOverride from '@codingame/monaco-vscode-languages-service-override'
|
|
import { getCssColor } from '$lib/utils'
|
|
|
|
// Monaco theme name matching the current document theme (light / dark / GitHub dark variant).
|
|
export function getEditorTheme(): string {
|
|
const classes = document.documentElement.classList
|
|
if (!classes.contains('dark')) {
|
|
return 'myTheme'
|
|
}
|
|
return getAppliedDarkModeVariant() === 'github' ? 'github-dark' : 'nord'
|
|
}
|
|
|
|
export function buildWorkerDefinition() {
|
|
const envEnhanced = getEnhancedMonacoEnvironment()
|
|
const getWorker = (moduleId: string, label: string) => {
|
|
console.log(`getWorker: moduleId: ${moduleId} label: ${label}`)
|
|
|
|
let selector = label
|
|
|
|
// const defaultTextEditorWorker = () => new Worker(
|
|
// new URL('@codingame/monaco-vscode-editor-api/esm/vs/editor/editor.worker.js', import.meta.url),
|
|
// { type: 'module' }
|
|
// );
|
|
// const defaultTextMateWorker = () => new Worker(
|
|
// new URL('@codingame/monaco-vscode-textmate-service-override/worker', import.meta.url),
|
|
// { type: 'module' }
|
|
// );
|
|
|
|
let workerLoaders = {
|
|
TextEditorWorker: () => {
|
|
return new Worker(
|
|
new URL(
|
|
'@codingame/monaco-vscode-editor-api/esm/vs/editor/editor.worker.js',
|
|
import.meta.url
|
|
),
|
|
{
|
|
type: 'module'
|
|
}
|
|
)
|
|
},
|
|
// javascript: () => {
|
|
// return new Worker(new URL('monaco-editor-wrapper/workers/module/ts', import.meta.url), {
|
|
// type: 'module'
|
|
// })
|
|
// },
|
|
javascript: () => {
|
|
return new Worker(
|
|
new URL(
|
|
'@codingame/monaco-vscode-standalone-typescript-language-features/worker',
|
|
import.meta.url
|
|
),
|
|
{
|
|
type: 'module'
|
|
}
|
|
)
|
|
},
|
|
typescript: () => {
|
|
// Use our custom TypeScript worker with SQL type inference plugin
|
|
return new Worker(new URL('../monaco_workers/sqlTypePlugin.worker.js', import.meta.url), {
|
|
type: 'module',
|
|
name: 'typescript-sql-aware'
|
|
})
|
|
},
|
|
json: () => {
|
|
return new Worker(
|
|
new URL(
|
|
'@codingame/monaco-vscode-standalone-json-language-features/worker',
|
|
import.meta.url
|
|
),
|
|
{
|
|
type: 'module'
|
|
}
|
|
)
|
|
},
|
|
html: () => {
|
|
return new Worker(
|
|
new URL(
|
|
'@codingame/monaco-vscode-standalone-html-language-features/worker',
|
|
import.meta.url
|
|
),
|
|
{
|
|
type: 'module'
|
|
}
|
|
)
|
|
},
|
|
css: () => {
|
|
return new Worker(
|
|
new URL(
|
|
'@codingame/monaco-vscode-standalone-css-language-features/worker',
|
|
import.meta.url
|
|
),
|
|
{
|
|
type: 'module'
|
|
}
|
|
)
|
|
}
|
|
// graphql: () => {
|
|
// console.log('Creating graphql worker')
|
|
// return new Worker(new URL(`../monaco_workers/graphql.worker.bundle.js`, import.meta.url), {
|
|
// name: 'graphql'
|
|
// })
|
|
// }
|
|
}
|
|
const workerFunc = workerLoaders[selector]
|
|
if (workerFunc !== undefined) {
|
|
return workerFunc()
|
|
} else {
|
|
throw new Error(`Unimplemented worker ${label} (${moduleId})`)
|
|
}
|
|
}
|
|
envEnhanced.getWorker = getWorker
|
|
}
|
|
|
|
export async function initializeVscode(caller?: string, htmlContainer?: HTMLElement) {
|
|
if (!isInitialized && !isInitializing) {
|
|
console.log(`Initializing vscode-api from ${caller ?? 'unknown'}`)
|
|
isInitializing = true
|
|
|
|
try {
|
|
// init vscode-api
|
|
const apiWrapper = new MonacoVscodeApiWrapper({
|
|
$type: 'classic',
|
|
viewsConfig: {
|
|
$type: 'EditorService'
|
|
},
|
|
|
|
serviceOverrides: {
|
|
// ...getLogServiceOverride()
|
|
// ...getThemeServiceOverride(),
|
|
// ...getTextmateServiceOverride()
|
|
// ...getConfigurationServiceOverride(),
|
|
// ...getKeybindingsServiceOverride()
|
|
...getLanguagesServiceOverride()
|
|
},
|
|
userConfiguration: {
|
|
json: JSON.stringify({
|
|
'editor.experimental.asyncTokenization': true
|
|
})
|
|
},
|
|
advanced: {
|
|
enableExtHostWorker: true
|
|
},
|
|
monacoWorkerFactory: buildWorkerDefinition
|
|
})
|
|
await apiWrapper.start()
|
|
|
|
isInitialized = true
|
|
|
|
// vscode-api ships VS Code's keybindings, including Ctrl/Cmd+Shift+S
|
|
// (Save As) which has no meaning here. Left bound, every Monaco
|
|
// instance swallows the shortcut and the browser/OS one never fires.
|
|
meditor.addKeybindingRule({
|
|
keybinding: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyS,
|
|
command: null
|
|
})
|
|
|
|
meditor.defineTheme('nord', {
|
|
base: 'vs-dark',
|
|
inherit: true,
|
|
rules: [
|
|
{
|
|
background: '2E3440',
|
|
token: ''
|
|
},
|
|
{
|
|
foreground: '98A7C1',
|
|
token: 'comment'
|
|
},
|
|
{
|
|
foreground: 'b3deac',
|
|
token: 'string'
|
|
},
|
|
{
|
|
foreground: 'b48ead',
|
|
token: 'constant.numeric'
|
|
},
|
|
{
|
|
foreground: '81a1c1',
|
|
token: 'constant.language'
|
|
},
|
|
{
|
|
foreground: '92bae6',
|
|
token: 'keyword'
|
|
},
|
|
{
|
|
foreground: '81a1c1',
|
|
token: 'storage'
|
|
},
|
|
{
|
|
foreground: '81a1c1',
|
|
token: 'storage.type'
|
|
},
|
|
{
|
|
foreground: '8fbcbb',
|
|
token: 'entity.name.class'
|
|
},
|
|
{
|
|
foreground: '8fbcbb',
|
|
fontStyle: ' bold',
|
|
token: 'entity.other.inherited-class'
|
|
},
|
|
{
|
|
foreground: '88c0d0',
|
|
token: 'entity.name.function'
|
|
},
|
|
{
|
|
foreground: '81a1c1',
|
|
token: 'entity.name.tag'
|
|
},
|
|
{
|
|
foreground: '8fbcbb',
|
|
token: 'entity.other.attribute-name'
|
|
},
|
|
{
|
|
foreground: '88c0d0',
|
|
token: 'support.function'
|
|
},
|
|
{
|
|
foreground: 'f8f8f0',
|
|
background: 'f92672',
|
|
token: 'invalid'
|
|
},
|
|
{
|
|
foreground: 'f8f8f0',
|
|
background: 'ae81ff',
|
|
token: 'invalid.deprecated'
|
|
},
|
|
{
|
|
foreground: 'b48ead',
|
|
token: 'constant.color.other.rgb-value'
|
|
},
|
|
{
|
|
foreground: 'ebcb8b',
|
|
token: 'constant.character.escape'
|
|
},
|
|
{
|
|
foreground: '8fbcbb',
|
|
token: 'variable.other.constant'
|
|
},
|
|
{ token: 'string.value.json', foreground: 'e8b886' }, // string values in JSON
|
|
{ token: 'keyword.json', foreground: 'e8b886' } // true, false, null in JSON
|
|
],
|
|
colors: {
|
|
'editor.foreground': '#D8DEE9',
|
|
'editor.background': getCssColor('surface-input', { format: 'hex-dark' }),
|
|
'editor.selectionBackground': '#515A6D',
|
|
'editor.inactiveSelectionBackground': '#515A6DB0',
|
|
'editor.lineHighlightBackground': '#3B4252',
|
|
'editorCursor.foreground': '#D8DEE9',
|
|
'editorWhitespace.foreground': '#515A6D',
|
|
'editorIndentGuide.background1': '#5A647860',
|
|
'editorIndentGuide.activeBackground1': '#6A7488',
|
|
'editorLineNumber.foreground': '#515A6D',
|
|
'editorLineNumber.activeForeground': '#D8DEE980'
|
|
}
|
|
})
|
|
|
|
meditor.defineTheme('github-dark', {
|
|
base: 'vs-dark',
|
|
inherit: true,
|
|
rules: [
|
|
{ background: '0D1117', token: '' },
|
|
{ foreground: '8b949e', token: 'comment' },
|
|
{ foreground: 'a5d6ff', token: 'string' },
|
|
{ foreground: '79c0ff', token: 'constant.numeric' },
|
|
{ foreground: '79c0ff', token: 'constant.language' },
|
|
{ foreground: 'ff7b72', token: 'keyword' },
|
|
{ foreground: 'ff7b72', token: 'storage' },
|
|
{ foreground: 'ff7b72', token: 'storage.type' },
|
|
{ foreground: 'ffa657', token: 'entity.name.class' },
|
|
{ foreground: 'ffa657', fontStyle: 'bold', token: 'entity.other.inherited-class' },
|
|
{ foreground: 'd2a8ff', token: 'entity.name.function' },
|
|
{ foreground: '7ee787', token: 'entity.name.tag' },
|
|
{ foreground: '79c0ff', token: 'entity.other.attribute-name' },
|
|
{ foreground: 'd2a8ff', token: 'support.function' },
|
|
{ foreground: 'f0f6fc', background: 'f85149', token: 'invalid' },
|
|
{ foreground: 'f0f6fc', background: 'bd561d', token: 'invalid.deprecated' },
|
|
{ foreground: '79c0ff', token: 'constant.color.other.rgb-value' },
|
|
{ foreground: '79c0ff', token: 'constant.character.escape' },
|
|
{ foreground: 'ffa657', token: 'variable.other.constant' },
|
|
{ token: 'string.value.json', foreground: 'a5d6ff' }, // string values in JSON
|
|
{ token: 'keyword.json', foreground: '79c0ff' } // true, false, null in JSON
|
|
],
|
|
colors: {
|
|
'editor.foreground': '#e6edf3',
|
|
'editor.background': getCssColor('surface-input', { format: 'hex-github-dark' }),
|
|
'editor.selectionBackground': '#3392FF44',
|
|
'editor.inactiveSelectionBackground': '#3392FF22',
|
|
'editor.lineHighlightBackground': '#161b22',
|
|
'editorCursor.foreground': '#e6edf3',
|
|
'editorWhitespace.foreground': '#484f58',
|
|
'editorIndentGuide.background1': '#21262d',
|
|
'editorIndentGuide.activeBackground1': '#30363d',
|
|
'editorLineNumber.foreground': '#6e7681',
|
|
'editorLineNumber.activeForeground': '#e6edf3'
|
|
}
|
|
})
|
|
|
|
meditor.defineTheme('myTheme', {
|
|
base: 'vs',
|
|
inherit: true,
|
|
rules: [],
|
|
colors: {
|
|
'editor.background': '#FFFFFF',
|
|
'editor.foreground': '#2d3748',
|
|
'editorCursor.foreground': '#2d3748',
|
|
'editorLineNumber.foreground': '#C2C9D1',
|
|
'editorLineNumber.activeForeground': '#989DA5',
|
|
'editorGutter.background': '#FFFFFF00'
|
|
}
|
|
})
|
|
|
|
meditor.setTheme(getEditorTheme())
|
|
} catch (e) {
|
|
console.error('Failed to initialize monaco services', e)
|
|
} finally {
|
|
isInitialized = true
|
|
isInitializing = false
|
|
}
|
|
} else {
|
|
while (isInitializing && !isInitialized) {
|
|
console.log('Waiting for initialization of monaco services')
|
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
}
|
|
}
|
|
}
|
|
|
|
export function keepModelAroundToAvoidDisposalOfWorkers() {
|
|
const keepEditorUri = mUri.parse('file:///avoidDisposalOfWorkers')
|
|
if (!meditor?.getModel(keepEditorUri)) {
|
|
meditor.createModel('', 'typescript', keepEditorUri)
|
|
}
|
|
}
|
|
|
|
export let MONACO_Y_PADDING = 6.5
|