2025-01-12 20:20:36 +03:00
|
|
|
import { editor as mEditor, Uri } from 'monaco-editor'
|
|
|
|
import { createEffect, on, onMount } from 'solid-js'
|
|
|
|
import { useColorScheme } from '../../lib/use-color-scheme'
|
|
|
|
|
|
|
|
import { $activeTab, $tabs, type EditorTab } from '../../store/tabs.ts'
|
|
|
|
import { useStore } from '../../store/use-store.ts'
|
|
|
|
import { setupMonaco } from './utils/setup.ts'
|
|
|
|
import './Editor.css'
|
|
|
|
|
|
|
|
export interface EditorProps {
|
|
|
|
class?: string
|
|
|
|
}
|
|
|
|
|
|
|
|
const DEFAULT_CODE = `
|
|
|
|
/**
|
|
|
|
* This playground comes pre-loaded with all @mtcute/* libraries,
|
|
|
|
* as well as a pre-configured Telegram client (available as \`tg\` global variable).
|
|
|
|
*
|
|
|
|
* Exports from this file will become available in the REPL.
|
|
|
|
*/
|
|
|
|
export const self = await tg.getMe()
|
|
|
|
console.log(self)
|
|
|
|
`.trimStart()
|
|
|
|
|
|
|
|
function findChangedTab(a: EditorTab[], b: EditorTab[]) {
|
|
|
|
const set = new Set(a.map(tab => tab.id))
|
|
|
|
for (const tab of b) {
|
|
|
|
if (!set.has(tab.id)) return tab
|
|
|
|
}
|
|
|
|
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
export default function Editor(props: EditorProps) {
|
|
|
|
const tabs = useStore($tabs)
|
|
|
|
const activeTab = useStore($activeTab)
|
|
|
|
|
|
|
|
let ref!: HTMLDivElement
|
|
|
|
let editor: mEditor.IStandaloneCodeEditor | undefined
|
|
|
|
|
|
|
|
const scheme = useColorScheme()
|
|
|
|
// const monacoTheme = () => scheme() === 'dark' ? 'ayu-dark' : 'ayu-light'
|
|
|
|
const modelsByTab = new Map<string, mEditor.ITextModel>()
|
|
|
|
|
|
|
|
onMount(async () => {
|
|
|
|
editor = mEditor.create(ref, {
|
|
|
|
model: null,
|
|
|
|
automaticLayout: true,
|
|
|
|
minimap: {
|
|
|
|
enabled: false,
|
|
|
|
},
|
|
|
|
scrollbar: {
|
|
|
|
verticalScrollbarSize: 8,
|
|
|
|
},
|
|
|
|
lightbulb: {
|
|
|
|
enabled: 'onCode' as any,
|
|
|
|
},
|
|
|
|
quickSuggestions: {
|
|
|
|
other: true,
|
|
|
|
comments: true,
|
|
|
|
strings: true,
|
|
|
|
},
|
|
|
|
padding: { top: 8 },
|
|
|
|
acceptSuggestionOnCommitCharacter: true,
|
|
|
|
acceptSuggestionOnEnter: 'on',
|
|
|
|
accessibilitySupport: 'on',
|
|
|
|
inlayHints: {
|
|
|
|
enabled: 'on',
|
|
|
|
},
|
|
|
|
lineNumbersMinChars: 3,
|
|
|
|
// theme: monacoTheme(),
|
|
|
|
theme: 'latte', // todo
|
|
|
|
scrollBeyondLastLine: false,
|
|
|
|
})
|
|
|
|
|
2025-01-14 06:34:42 +03:00
|
|
|
await setupMonaco()
|
2025-01-12 20:20:36 +03:00
|
|
|
|
|
|
|
for (const tab of tabs()) {
|
|
|
|
const model = mEditor.createModel(tab.main ? DEFAULT_CODE : '', 'typescript', Uri.parse(`file:///${tab.id}.ts`))
|
|
|
|
modelsByTab.set(tab.id, model)
|
|
|
|
}
|
|
|
|
|
|
|
|
editor.setModel(modelsByTab.get(activeTab())!)
|
|
|
|
|
|
|
|
// editor.onDidChangeModelContent(() => {
|
|
|
|
// props.onCodeChange(editor?.getValue() ?? '')
|
|
|
|
// })
|
|
|
|
|
|
|
|
return () => editor?.dispose()
|
|
|
|
})
|
|
|
|
|
|
|
|
// createEffect(on(() => monacoTheme(), (theme) => {
|
|
|
|
// if (!editor) return
|
|
|
|
// editor.updateOptions({ theme })
|
|
|
|
// }))
|
|
|
|
|
|
|
|
createEffect(on(activeTab, (tabId) => {
|
|
|
|
if (!editor) return
|
|
|
|
const model = modelsByTab.get(tabId)
|
|
|
|
if (!model) return
|
|
|
|
editor.setModel(model)
|
|
|
|
}))
|
|
|
|
|
|
|
|
createEffect(on(tabs, (tabs, prevTabs) => {
|
|
|
|
if (!editor || !prevTabs) return
|
|
|
|
if (tabs.length === prevTabs.length) {
|
|
|
|
// verify filenames
|
|
|
|
for (const tab of tabs) {
|
|
|
|
const oldName = prevTabs.find(prevTab => prevTab.id === tab.id)?.fileName
|
|
|
|
if (!oldName) continue // weird flex but ok
|
|
|
|
if (oldName === tab.fileName) continue
|
|
|
|
|
|
|
|
// renamed
|
|
|
|
const oldModel = modelsByTab.get(tab.id)
|
|
|
|
if (!oldModel) continue
|
|
|
|
const newModel = mEditor.createModel(oldModel.getValue(), 'typescript', Uri.parse(`file:///${tab.fileName}`))
|
|
|
|
modelsByTab.set(tab.id, newModel)
|
|
|
|
if (editor.getModel() === oldModel) {
|
|
|
|
editor.setModel(newModel)
|
|
|
|
}
|
|
|
|
oldModel.dispose()
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tabs.length > prevTabs.length) {
|
|
|
|
// a tab was created
|
|
|
|
const changed = findChangedTab(prevTabs, tabs)
|
|
|
|
if (!changed) return
|
|
|
|
const model = mEditor.createModel('', 'typescript', Uri.parse(`file:///${changed.fileName}`))
|
|
|
|
modelsByTab.set(changed.id, model)
|
|
|
|
editor.setModel(model)
|
|
|
|
} else {
|
|
|
|
// a tab was deleted
|
|
|
|
const changed = findChangedTab(tabs, prevTabs)
|
|
|
|
if (!changed) return
|
|
|
|
modelsByTab.delete(changed.id)
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
data-monaco-root
|
|
|
|
class={props.class}
|
|
|
|
ref={ref}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
}
|