saiashirwad _ .cursorrules file for TypeScript

* this project is a drag and drop code editor
* this project uses solidjs and typescript. type safety is of the utmost importance
* there's going to be a canvas where you can drag and drop code blocks
* each code block is required to either execute an effect, or return a value
* the code blocks are reactive
* when some value changes, every single code block that depends on said value also is recomputed
* you can have target blocks which can have either like a table or a graph or just a html renderer (for now)
* i think there should be a reactivity class that is responsible for managing the reactivity of the code blocks 
* for the code blocks, we should use monaco
* for drag and drop, for now, let us use https://github.com/thisbeyond/solid-dnd (please index that)
* down the line, might use the webcontainers api to let us have any library in this editor
# here is an example of using monaco with solidjs

```typescript
import { createSignal, createEffect, onCleanup, JSX, onMount, mergeProps, on } from 'solid-js'
import * as monacoEditor from 'monaco-editor'
import loader, { Monaco } from '@monaco-editor/loader'
import { Loader } from './Loader'
import { MonacoContainer } from './MonacoContainer'
import { getOrCreateModel } from './utils'
import { LoaderParams } from './types'

const viewStates = new Map()

export interface MonacoEditorProps {
  language?: string
  value?: string
  loadingState?: JSX.Element
  class?: string
  theme?: monacoEditor.editor.BuiltinTheme | string
  path?: string
  overrideServices?: monacoEditor.editor.IEditorOverrideServices
  width?: string
  height?: string
  options?: monacoEditor.editor.IStandaloneEditorConstructionOptions
  saveViewState?: boolean
  loaderParams?: LoaderParams
  onChange?: (value: string, event: monacoEditor.editor.IModelContentChangedEvent) => void
  onMount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneCodeEditor) => void
  onBeforeUnmount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneCodeEditor) => void
}

export const MonacoEditor = (inputProps: MonacoEditorProps) => {
  const props = mergeProps(
    {
      theme: 'vs',
      width: '100%',
      height: '100%',
      loadingState: 'Loading…',
      saveViewState: true,
    },
    inputProps,
  )

  let containerRef: HTMLDivElement

  const [monaco, setMonaco] = createSignal<Monaco>()
  const [editor, setEditor] = createSignal<monacoEditor.editor.IStandaloneCodeEditor>()

  let abortInitialization: (() => void) | undefined
  let monacoOnChangeSubscription: any
  let isOnChangeSuppressed = false

  onMount(async () => {
    loader.config(inputProps.loaderParams ?? { monaco: monacoEditor })
    const loadMonaco = loader.init()

    abortInitialization = () => loadMonaco.cancel()

    try {
      const monaco = await loadMonaco
      const editor = createEditor(monaco)
      setMonaco(monaco)
      setEditor(editor)
      props.onMount?.(monaco, editor)

      monacoOnChangeSubscription = editor.onDidChangeModelContent(event => {
        if (!isOnChangeSuppressed) {
          props.onChange?.(editor.getValue(), event)
        }
      })
    } catch (error: any) {
      if (error?.type === 'cancelation') {
        return
      }

      console.error('Could not initialize Monaco', error)
    }
  })

  onCleanup(() => {
    const _editor = editor()
    if (!_editor) {
      abortInitialization?.()
      return
    }

    props.onBeforeUnmount?.(monaco()!, _editor)
    monacoOnChangeSubscription?.dispose()
    _editor.getModel()?.dispose()
    _editor.dispose()
  })

  createEffect(
    on(
      () => props.value,
      value => {
        const _editor = editor()
        if (!_editor || typeof value === 'undefined') {
          return
        }

        if (_editor.getOption(monaco()!.editor.EditorOption.readOnly)) {
          _editor.setValue(value)
          return
        }

        if (value !== _editor.getValue()) {
          isOnChangeSuppressed = true

          _editor.executeEdits('', [
            {
              range: _editor.getModel()!.getFullModelRange(),
              text: value,
              forceMoveMarkers: true,
            },
          ])

          _editor.pushUndoStop()
          isOnChangeSuppressed = false
        }
      },
      { defer: true },
    ),
  )

  createEffect(
    on(
      () => props.options,
      options => {
        editor()?.updateOptions(options ?? {})
      },
      { defer: true },
    ),
  )

  createEffect(
    on(
      () => props.theme,
      theme => {
        monaco()?.editor.setTheme(theme)
      },
      { defer: true },
    ),
  )

  createEffect(
    on(
      () => props.language,
      language => {
        const model = editor()?.getModel()
        if (!language || !model) {
          return
        }

        monaco()?.editor.setModelLanguage(model, language)
      },
      { defer: true },
    ),
  )

  createEffect(
    on(
      () => props.path,
      (path, prevPath) => {
        const _monaco = monaco()
        if (!_monaco) {
          return
        }

        const model = getOrCreateModel(_monaco, props.value ?? '', props.language, path)

        if (model !== editor()?.getModel()) {
          if (props.saveViewState) {
            viewStates.set(prevPath, editor()?.saveViewState())
          }
          editor()?.setModel(model)
          if (props.saveViewState) {
            editor()?.restoreViewState(viewStates.get(path))
          }
        }
      },
      { defer: true },
    ),
  )

  const createEditor = (monaco: Monaco) => {
    const model = getOrCreateModel(monaco, props.value ?? '', props.language, props.path)

    return monaco.editor.create(
      containerRef,
      {
        model: model,
        automaticLayout: true,
        ...props.options,
      },
      props.overrideServices,
    )
  }

  return (
    <MonacoContainer class={props.class} width={props.width} height={props.height}>
      {!editor() && <Loader>{props.loadingState}</Loader>}
      <div style={{ width: '100%' }} ref={containerRef!} />
    </MonacoContainer>
  )
}

```

```typescript
import { createSignal, createEffect, onCleanup, JSX, onMount, mergeProps, on } from 'solid-js'
import * as monacoEditor from 'monaco-editor'
import loader, { Monaco } from '@monaco-editor/loader'
import { Loader } from './Loader'
import { MonacoContainer } from './MonacoContainer'
import { getOrCreateModel } from './utils'
import { LoaderParams } from './types'

const viewStates = new Map()

export interface MonacoDiffEditorProps {
  original?: string
  modified?: string

  originalLanguage?: string
  modifiedLanguage?: string

  originalPath?: string
  modifiedPath?: string

  loadingState?: JSX.Element
  class?: string
  theme?: monacoEditor.editor.BuiltinTheme | string
  overrideServices?: monacoEditor.editor.IEditorOverrideServices
  width?: string
  height?: string
  options?: monacoEditor.editor.IStandaloneEditorConstructionOptions
  saveViewState?: boolean
  loaderParams?: LoaderParams
  onChange?: (value: string) => void
  onMount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneDiffEditor) => void
  onBeforeUnmount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneDiffEditor) => void
}

export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => {
  const props = mergeProps(
    {
      theme: 'vs',
      width: '100%',
      height: '100%',
      loadingState: 'Loading…',
      saveViewState: true,
    },
    inputProps,
  )

  let containerRef: HTMLDivElement

  const [monaco, setMonaco] = createSignal<Monaco>()
  const [editor, setEditor] = createSignal<monacoEditor.editor.IStandaloneDiffEditor>()

  let abortInitialization: (() => void) | undefined
  let monacoOnChangeSubscription: any
  let isOnChangeSuppressed = false

  onMount(async () => {
    loader.config(inputProps.loaderParams ?? { monaco: monacoEditor })
    const loadMonaco = loader.init()

    abortInitialization = () => loadMonaco.cancel()

    try {
      const monaco = await loadMonaco
      const editor = createEditor(monaco)
      setMonaco(monaco)
      setEditor(editor)
      props.onMount?.(monaco, editor)

      monacoOnChangeSubscription = editor.onDidUpdateDiff(() => {
        if (!isOnChangeSuppressed) {
          props.onChange?.(editor.getModifiedEditor().getValue())
        }
      })
    } catch (error: any) {
      if (error?.type === 'cancelation') {
        return
      }

      console.error('Could not initialize Monaco', error)
    }
  })

  onCleanup(() => {
    const _editor = editor()
    if (!_editor) {
      abortInitialization?.()
      return
    }

    props.onBeforeUnmount?.(monaco()!, _editor)
    monacoOnChangeSubscription?.dispose()
    _editor.getModel()?.original.dispose()
    _editor.getModel()?.modified.dispose()
    _editor.dispose()
  })

  createEffect(
    on(
      () => props.modified,
      modified => {
        const _editor = editor()?.getModifiedEditor()
        if (!_editor || typeof modified === 'undefined') {
          return
        }

        if (_editor.getOption(monaco()!.editor.EditorOption.readOnly)) {
          _editor.setValue(modified)
          return
        }

        if (modified !== _editor.getValue()) {
          isOnChangeSuppressed = true

          _editor.executeEdits('', [
            {
              range: _editor.getModel()!.getFullModelRange(),
              text: modified,
              forceMoveMarkers: true,
            },
          ])

          _editor.pushUndoStop()
          isOnChangeSuppressed = false
        }
      },
      { defer: true },
    ),
  )

  createEffect(
    on(
      () => props.original,
      original => {
        const _editor = editor()?.getOriginalEditor()
        if (!_editor || typeof original === 'undefined') {
          return
        }

        if (_editor.getOption(monaco()!.editor.EditorOption.readOnly)) {
          _editor.setValue(original)
        }
      },
      { defer: true },
    ),
  )

  createEffect(
    on(
      () => props.options,
      options => {
        editor()?.updateOptions(options ?? {})
      },
      { defer: true },
    ),
  )

  createEffect(
    on(
      () => props.theme,
      theme => {
        monaco()?.editor.setTheme(theme)
      },
      { defer: true },
    ),
  )

  createEffect(
    on(
      () => props.originalLanguage,
      language => {
        const model = editor()?.getModel()
        if (!language || !model) {
          return
        }

        monaco()?.editor.setModelLanguage(model.original, language)
      },
      { defer: true },
    ),
  )

  createEffect(
    on(
      () => props.modifiedLanguage,
      language => {
        const model = editor()?.getModel()
        if (!language || !model) {
          return
        }

        monaco()?.editor.setModelLanguage(model.modified, language)
      },
      { defer: true },
    ),
  )

  createEffect(
    on(
      () => [props.originalPath, props.modifiedPath],
      ([originalPath, modifiedPath], prevPaths) => {
        const _monaco = monaco()
        if (!_monaco || !prevPaths) {
          return
        }

        const [prevOriginalPath, prevModifiedPath] = prevPaths

        const currentModels = editor()?.getModel()
        let originalModel = currentModels?.original
        let modifiedModel = currentModels?.modified

        if (prevOriginalPath !== originalPath) {
          if (props.saveViewState && originalPath != null) {
            viewStates.set(prevOriginalPath, editor()?.getOriginalEditor().saveViewState())
          }

          originalModel = getOrCreateModel(
            _monaco,
            props.original ?? '',
            props.originalLanguage,
            originalPath,
          )
        }

        if (prevModifiedPath !== modifiedPath) {
          if (props.saveViewState && prevModifiedPath != null) {
            viewStates.set(prevModifiedPath, editor()?.getModifiedEditor().saveViewState())
          }

          modifiedModel = getOrCreateModel(
            _monaco,
            props.modified ?? '',
            props.modifiedLanguage,
            modifiedPath,
          )
        }

        editor()?.setModel({ modified: modifiedModel!, original: originalModel! })

        if (props.saveViewState) {
          editor()?.getOriginalEditor().restoreViewState(viewStates.get(originalPath))
          editor()?.getModifiedEditor().restoreViewState(viewStates.get(modifiedPath))
        }
      },
      { defer: true },
    ),
  )

  const createEditor = (monaco: Monaco) => {
    const originalModel = getOrCreateModel(
      monaco,
      props.original ?? '',
      props.originalLanguage,
      props.originalPath,
    )
    const modifiedModel = getOrCreateModel(
      monaco,
      props.modified ?? '',
      props.modifiedLanguage,
      props.modifiedPath,
    )

    const editor = monaco.editor.createDiffEditor(
      containerRef,
      {
        automaticLayout: true,
        ...props.options,
      },
      props.overrideServices,
    )

    editor.setModel({
      original: originalModel,
      modified: modifiedModel,
    })

    return editor
  }

  return (
    <MonacoContainer class={props.class} width={props.width} height={props.height}>
      {!editor() && <Loader>{props.loadingState}</Loader>}
      <div style={{ width: '100%' }} ref={containerRef!} />
    </MonacoContainer>
  )
}
```

# here are some instructions for using monaco:

Based on your decision to use Monaco instead of CodeMirror, here's how you can integrate custom intellisense for TypeScript in Monaco Editor, including support for your own global variables:

## Setting Up Monaco with Custom Intellisense

1. **Add Type Definitions**

Create a declaration file for your custom globals:

```typescript
// globals.d.ts
declare const myGlobalVar: string;
declare function myGlobalFunction(): void;
// Add more global declarations as needed
```

2. **Configure Monaco**

When initializing Monaco, add the following configurations:

```typescript
import * as monaco from 'monaco-editor';

// Add your custom type definitions
monaco.languages.typescript.javascriptDefaults.addExtraLib(`
    declare const myGlobalVar: string;
    declare function myGlobalFunction(): void;
    // Add more global declarations here
    `, 'globals.d.ts');

// Configure TypeScript compiler options
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES6,
allowNonTsExtensions: true,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
module: monaco.languages.typescript.ModuleKind.CommonJS,
noEmit: true,
typeRoots: ["node_modules/@types"]
});

// Create the editor
const editor = monaco.editor.create(document.getElementById('container'), {
value: '// Your initial TypeScript code here',
language: 'typescript',
// Other editor options...
});
```

3. **Custom Completion Provider (Optional)**

For more advanced scenarios, you can add a custom completion provider:

```typescript
monaco.languages.registerCompletionItemProvider('typescript', {
provideCompletionItems: (model, position) => {
const suggestions = [
{
label: 'myGlobalVar',
kind: monaco.languages.CompletionItemKind.Variable,
detail: 'Custom global variable',
insertText: 'myGlobalVar'
},
{
label: 'myGlobalFunction',
kind: monaco.languages.CompletionItemKind.Function,
detail: 'Custom global function',
insertText: 'myGlobalFunction()'
}
// Add more custom suggestions here
];

return { suggestions };
}
});
```

This setup will provide intellisense for your custom globals, including autocomplete suggestions and type information when hovering over the variables or functions in the editor[1][3][4].

## Additional Tips

* **Dynamic Updates**: If your global variables can change at runtime, you may need to update the extra libraries dynamically.
* **Performance**: For large projects, consider optimizing the completion provider to handle a large number of suggestions efficiently.
* **TypeScript Support**: Monaco provides excellent built-in support for TypeScript, so you'll get good intellisense for standard TypeScript features out of the box.

By following these steps, you'll have a Monaco editor with TypeScript support that includes your custom global variables in the intellisense suggestions, providing a rich editing experience for your users.

Citations:
[1] https://stackoverflow.com/questions/51538242/how-to-define-global-variable-in-monaco-editor-for-intellisense/51539385
[2] https://discourse.nodered.org/t/monaco-intellisense-code-completion-subflow/84581
[3] https://www.checklyhq.com/blog/customizing-monaco/
[4] https://github.com/microsoft/monaco-editor/issues/2147
[5] https://mono.software/2017/04/11/custom-intellisense-with-monaco-editor/
[6] https://microsoft.github.io/monaco-editor/
[7] https://www.npmjs.com/package/@monaco-editor/react
[8] https://stackoverflow.com/questions/69702749/how-to-use-intellisense-from-monaco-d-ts
[9] https://stackoverflow.com/questions/67505426/custom-javascript-code-completion-for-this-in-monaco-editor
[10] https://github.com/microsoft/monaco-editor/discussions/4469
css
golang
html
java
javascript
npm
react
rest-api
+2 more

First Time Repository

js in boxes

TypeScript

Languages:

CSS: 1.2KB
HTML: 0.5KB
TypeScript: 16.3KB
Created: 12/24/2024
Updated: 12/25/2024

All Repositories (1)

js in boxes