<svelte:options customElement={{ props: {} }} />

<script lang="ts">
  import {onMount} from 'svelte';
  import {Compartment, EditorState, type Transaction} from '@codemirror/state';
  import {
    autocompletion,
    closeBrackets,
    closeBracketsKeymap,
    completionKeymap,
    pickedCompletion
  } from '@codemirror/autocomplete';
  import {defaultKeymap, history, historyKeymap, indentWithTab} from '@codemirror/commands';
  import {javascript} from '@codemirror/lang-javascript';
  import {
    defaultHighlightStyle,
    indentOnInput,
    indentUnit,
    syntaxHighlighting
  } from '@codemirror/language';
  import {python} from '@codemirror/lang-python';
  import {
    Decoration,
    type DecorationSet,
    EditorView,
    keymap,
    placeholder as cmPlaceholder,
    ViewPlugin,
    type ViewUpdate
  } from '@codemirror/view';
  import {Button, FloatingTrigger} from '../base';
  import type {Parameter} from '#types';
  import {flattenParameterCandidates} from '../utils/paramToken';
  import {
    type CodeEngine,
    createBusinessCompletionSource,
    normalizeCodeEngine,
    shouldAutoAppendCallParens
  } from '../utils/codeCompletion';

  const {
    value = '',
    mode = 'textarea',
    engine = 'js',
    parameters = [],
    rows = 10,
    placeholder = '',
    disabled = false,
    class: className = '',
    style = '',
    ...rest
  }: {
    value?: string;
    mode?: 'input' | 'textarea';
    engine?: string;
    parameters?: Parameter[];
    rows?: number;
    placeholder?: string;
    disabled?: boolean;
    class?: string;
    style?: string;
    [key: string]: any;
  } = $props();

  const TOKEN_PATTERN = /\{\{\s*([^{}]+?)\s*}}/g;
  const RESULT_PATTERN = /\b_result\b/g;
  const languageCompartment = new Compartment();
  const indentCompartment = new Compartment();
  const completionCompartment = new Compartment();
  const completionSourceCompartment = new Compartment();
  const decorationCompartment = new Compartment();
  const editableCompartment = new Compartment();
  const placeholderCompartment = new Compartment();

  let containerEl = $state<HTMLDivElement | null>(null);
  let editorShellEl = $state<HTMLDivElement | null>(null);
  let editorView = $state<EditorView | null>(null);
  let triggerObject: any;
  let applyingExternalValue = $state(false);
  let expanded = $state(false);
  let shellOriginalParent = $state<HTMLElement | null>(null);
  let shellOriginalNextSibling = $state<ChildNode | null>(null);

  const isSingleLine = $derived(mode === 'input');
  const normalizedEngine = $derived(normalizeCodeEngine(engine));
  const minRows = $derived(isSingleLine ? 1 : Math.max(3, rows || 3));
  const minHeight = $derived(`${minRows * 24}px`);
  const editorMinHeight = $derived(expanded ? '100%' : minHeight);
  const editorStyle = $derived(`${style};--code-editor-min-height:${editorMinHeight};`);
  const paramCandidates = $derived(flattenParameterCandidates(parameters));
  const hasParams = $derived(paramCandidates.length > 0);
  const paramResolvedMap = $derived.by(() => {
    const map = new Map<string, boolean>();
    for (const candidate of paramCandidates) {
      map.set(candidate.name, candidate.resolved);
    }
    return map;
  });

  function emitInput(nextValue: string) {
    rest.oninput?.({
      target: {
        value: nextValue
      }
    });
  }

  function emitChange(nextValue: string) {
    rest.onchange?.({
      target: {
        value: nextValue
      }
    });
  }

  function createLanguageExtension(targetEngine: CodeEngine) {
    if (targetEngine === 'python') {
      return python();
    }
    return javascript();
  }

  function getIndentUnitForEngine(targetEngine: CodeEngine) {
    if (targetEngine === 'python') {
      return '    ';
    }
    return '  ';
  }

  function createBusinessCompletionExtension(targetEngine: CodeEngine, candidates: typeof paramCandidates) {
    return EditorState.languageData.of((_state, _pos, _side) => {
      return [
        {
          autocomplete: createBusinessCompletionSource({
            engine: targetEngine,
            paramCandidates: candidates
          })
        }
      ];
    });
  }

  function handlePickedCompletionParens(transactions: readonly Transaction[], currentState: EditorState) {
    for (const tr of transactions) {
      const picked = tr.annotation(pickedCompletion);
      if (!picked) {
        continue;
      }

      const cursor = currentState.selection.main.head;
      const nextChar = currentState.sliceDoc(cursor, cursor + 1);
      if (!shouldAutoAppendCallParens(picked, nextChar)) {
        continue;
      }

      queueMicrotask(() => {
        if (!editorView) {
          return;
        }
        const head = editorView.state.selection.main.head;
        const afterHead = editorView.state.sliceDoc(head, head + 1);
        if (afterHead === '(') {
          return;
        }
        editorView.dispatch({
          changes: {
            from: head,
            to: head,
            insert: '()'
          },
          selection: {
            anchor: head + 1
          }
        });
      });
    }
  }

  function createHighlightDecorations(resolvedMap: Map<string, boolean>) {
    const resultMark = Decoration.mark({class: 'cm-special-result'});
    const validTokenMark = Decoration.mark({class: 'cm-template-token-valid'});
    const unresolvedTokenMark = Decoration.mark({class: 'cm-template-token-unresolved'});
    const invalidTokenMark = Decoration.mark({class: 'cm-template-token-invalid'});

    function buildDecorations(source: string): DecorationSet {
      const ranges: Array<{from: number; to: number; value: Decoration}> = [];

      RESULT_PATTERN.lastIndex = 0;
      let resultMatch: RegExpExecArray | null = RESULT_PATTERN.exec(source);
      while (resultMatch) {
        ranges.push({
          from: resultMatch.index,
          to: resultMatch.index + resultMatch[0].length,
          value: resultMark
        });
        resultMatch = RESULT_PATTERN.exec(source);
      }

      TOKEN_PATTERN.lastIndex = 0;
      let tokenMatch: RegExpExecArray | null = TOKEN_PATTERN.exec(source);
      while (tokenMatch) {
        const tokenKey = (tokenMatch[1] || '').trim();
        const resolved = resolvedMap.get(tokenKey);
        const decoration =
          resolved === undefined ? invalidTokenMark : resolved ? validTokenMark : unresolvedTokenMark;
        ranges.push({
          from: tokenMatch.index,
          to: tokenMatch.index + tokenMatch[0].length,
          value: decoration
        });
        tokenMatch = TOKEN_PATTERN.exec(source);
      }

      return Decoration.set(ranges, true);
    }

    return ViewPlugin.fromClass(
      class {
        decorations: DecorationSet;

        constructor(view: EditorView) {
          this.decorations = buildDecorations(view.state.doc.toString());
        }

        update(update: ViewUpdate) {
          if (update.docChanged || update.viewportChanged) {
            this.decorations = buildDecorations(update.state.doc.toString());
          }
        }
      },
      {
        decorations: (value) => value.decorations
      }
    );
  }

  function createEditorTheme() {
    return EditorView.theme({
      '&': {
        border: '1px solid var(--tf-border-color)',
        borderRadius: '6px',
        backgroundColor: 'var(--tf-bg-surface)',
        fontSize: '13px'
      },
      '&.cm-focused': {
        outline: 'none',
        borderColor: 'var(--tf-primary-color)',
        boxShadow: '0 0 0 1px var(--tf-primary-ghost-bg)'
      },
      '&.cm-editor.cm-disabled': {
        backgroundColor: 'var(--tf-bg-input-disabled)',
        opacity: 0.85
      },
      '.cm-scroller': {
        minHeight: 'var(--code-editor-min-height)',
        lineHeight: '1.6',
        fontFamily:
          "ui-monospace, SFMono-Regular, SF Mono, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace"
      },
      '.cm-content': {
        padding: '8px 38px 8px 10px',
        caretColor: 'var(--tf-text-primary)'
      },
      '.cm-gutters': {
        border: 'none',
        background: 'transparent'
      },
      '.cm-activeLine': {
        background: 'var(--tf-primary-ghost-bg)'
      },
      '.cm-placeholder': {
        color: 'var(--tf-text-muted)'
      }
    });
  }

  function insertTextAtSelection(text: string) {
    if (!editorView) {
      return;
    }
    const selection = editorView.state.selection.main;
    const from = selection.from;
    const to = selection.to;
    editorView.dispatch({
      changes: {
        from,
        to,
        insert: text
      },
      selection: {
        anchor: from + text.length
      }
    });
    editorView.focus();
  }

  function insertParam(paramName: string) {
    insertTextAtSelection(`{{${paramName}}}`);
    emitChange(editorView?.state.doc.toString() || '');
    triggerObject?.hide?.();
  }

  function toggleExpanded() {
    expanded = !expanded;
  }

  function closeExpanded() {
    expanded = false;
  }

  function moveExpandedShellToBody() {
    if (typeof document === 'undefined' || !editorShellEl) {
      return;
    }

    if (editorShellEl.parentElement === document.body) {
      return;
    }

    shellOriginalParent = editorShellEl.parentElement;
    shellOriginalNextSibling = editorShellEl.nextSibling;
    document.body.appendChild(editorShellEl);
  }

  function restoreExpandedShell() {
    if (!editorShellEl || !shellOriginalParent) {
      return;
    }

    if (editorShellEl.parentElement !== document.body) {
      return;
    }

    if (shellOriginalNextSibling && shellOriginalNextSibling.parentNode === shellOriginalParent) {
      shellOriginalParent.insertBefore(editorShellEl, shellOriginalNextSibling);
    } else {
      shellOriginalParent.appendChild(editorShellEl);
    }
  }

  onMount(() => {
    if (!containerEl) {
      return;
    }

    editorView = new EditorView({
      parent: containerEl,
      state: EditorState.create({
        doc: (value || '') as string,
        extensions: [
          createEditorTheme(),
          syntaxHighlighting(defaultHighlightStyle, {fallback: true}),
          history(),
          indentOnInput(),
          closeBrackets(),
          EditorView.lineWrapping,
          EditorView.updateListener.of((update) => {
            handlePickedCompletionParens(update.transactions, update.state);

            if (!update.docChanged) {
              return;
            }

            const nextValue = update.state.doc.toString();
            if (!applyingExternalValue) {
              emitInput(nextValue);
            }
          }),
          EditorView.contentAttributes.of({spellcheck: 'false'}),
          EditorView.domEventHandlers({
            focus: (event) => {
              rest.onfocus?.(event);
              return false;
            },
            blur: (event) => {
              emitChange(editorView?.state.doc.toString() || '');
              rest.onblur?.(event);
              return false;
            }
          }),
          keymap.of([
            indentWithTab,
            ...defaultKeymap,
            ...historyKeymap,
            ...closeBracketsKeymap,
            ...completionKeymap
          ]),
          languageCompartment.of(createLanguageExtension(normalizedEngine)),
          indentCompartment.of(indentUnit.of(getIndentUnitForEngine(normalizedEngine))),
          completionSourceCompartment.of(createBusinessCompletionExtension(normalizedEngine, paramCandidates)),
          completionCompartment.of(
            autocompletion({
              activateOnTyping: true
            })
          ),
          decorationCompartment.of(createHighlightDecorations(paramResolvedMap)),
          editableCompartment.of([EditorView.editable.of(!disabled), EditorState.readOnly.of(disabled)]),
          placeholderCompartment.of(placeholder ? cmPlaceholder(placeholder) : [])
        ]
      })
    });

    return () => {
      editorView?.destroy();
      editorView = null;
    };
  });

  $effect(() => {
    if (!editorView) {
      return;
    }
    editorView.dispatch({
      effects: languageCompartment.reconfigure(createLanguageExtension(normalizedEngine))
    });
  });

  $effect(() => {
    if (!editorView) {
      return;
    }
    editorView.dispatch({
      effects: indentCompartment.reconfigure(indentUnit.of(getIndentUnitForEngine(normalizedEngine)))
    });
  });

  $effect(() => {
    if (!editorView) {
      return;
    }
    editorView.dispatch({
      effects: completionSourceCompartment.reconfigure(
        createBusinessCompletionExtension(normalizedEngine, paramCandidates)
      )
    });
  });

  $effect(() => {
    if (!editorView) {
      return;
    }
    editorView.dispatch({
      effects: decorationCompartment.reconfigure(createHighlightDecorations(paramResolvedMap))
    });
  });

  $effect(() => {
    if (!editorView) {
      return;
    }
    editorView.dispatch({
      effects: editableCompartment.reconfigure([EditorView.editable.of(!disabled), EditorState.readOnly.of(disabled)])
    });

    const editorDom = editorView.dom;
    if (disabled) {
      editorDom.classList.add('cm-disabled');
    } else {
      editorDom.classList.remove('cm-disabled');
    }
  });

  $effect(() => {
    if (!editorView) {
      return;
    }
    editorView.dispatch({
      effects: placeholderCompartment.reconfigure(placeholder ? cmPlaceholder(placeholder) : [])
    });
  });

  $effect(() => {
    if (!editorView) {
      return;
    }

    const nextValue = (value || '') as string;
    const currentValue = editorView.state.doc.toString();
    if (nextValue === currentValue) {
      return;
    }

    applyingExternalValue = true;
    editorView.dispatch({
      changes: {
        from: 0,
        to: currentValue.length,
        insert: nextValue
      }
    });
    applyingExternalValue = false;
  });

  $effect(() => {
    if (!expanded || typeof window === 'undefined') {
      return;
    }

    const onKeydown = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        closeExpanded();
      }
    };
    window.addEventListener('keydown', onKeydown);
    return () => {
      window.removeEventListener('keydown', onKeydown);
    };
  });

  $effect(() => {
    if (typeof document === 'undefined') {
      return;
    }
    const className = 'code-script-expanded-open';
    if (expanded) {
      document.body.classList.add(className);
    } else {
      document.body.classList.remove(className);
    }
    return () => {
      document.body.classList.remove(className);
    };
  });

  $effect(() => {
    if (typeof document === 'undefined' || !editorShellEl) {
      return;
    }

    if (expanded) {
      moveExpandedShellToBody();
    } else {
      restoreExpandedShell();
    }

    return () => {
      restoreExpandedShell();
    };
  });

</script>

<div class="code-script-editor-shell {expanded ? 'expanded-host' : ''}" bind:this={editorShellEl}>
  {#if expanded}
    <button type="button" class="code-script-overlay" aria-label="关闭放大代码编辑器" onclick={closeExpanded}></button>
  {/if}

  <div
    class="code-script-editor nopan nodrag {className} {expanded ? 'expanded' : ''}"
    style={editorStyle}
  >
    <div class="code-script-editor-inner" bind:this={containerEl}></div>

    <div class="code-script-action">
      <div class="code-script-action-buttons">
        <Button
          class="code-script-button {expanded ? 'expanded-cancel' : ''}"
          onclick={toggleExpanded}
          title={expanded ? '取消放大' : '放大编辑区域'}
          aria-label={expanded ? '取消放大' : '放大编辑区域'}
        >
          {#if expanded}
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
              <path d="M21 3V9H19V6.414L15.707 9.707L14.293 8.293L17.586 5H15V3H21ZM3 21V15H5V17.586L8.293 14.293L9.707 15.707L6.414 19H9V21H3ZM9.707 8.293L8.293 9.707L5 6.414V9H3V3H9V5H6.414L9.707 8.293ZM14.293 15.707L15.707 14.293L19 17.586V15H21V21H15V19H17.586L14.293 15.707Z"></path>
            </svg>
          {:else}
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
              <path d="M15 3H21V9H19V5H15V3ZM3 9V3H9V5H5V9H3ZM15 21V19H19V15H21V21H15ZM3 15H5V19H9V21H3V15Z"></path>
            </svg>
          {/if}
        </Button>
        <FloatingTrigger placement="bottom-end" bind:this={triggerObject}>
          <Button
            class="code-script-button"
            disabled={!hasParams || disabled}
            title={hasParams ? '插入参数模板' : '请先在输入参数中定义参数'}
          >
            <span>{"{x}"}</span>
          </Button>
          {#snippet floating()}
            <div class="code-script-panel nowheel">
              {#if hasParams}
                {#each paramCandidates as candidate}
                  <button
                    class="code-script-item {candidate.resolved ? '' : 'unresolved'}"
                    title={candidate.resolved ? candidate.name : `${candidate.name}（未映射）`}
                    onclick={() => {
                      insertParam(candidate.name);
                    }}
                  >
                    <span>{candidate.name}</span>
                    {#if !candidate.resolved}
                      <span class="code-script-item-warn" title="该参数未映射，运行时可能为空">!</span>
                    {/if}
                  </button>
                {/each}
              {:else}
                <div class="code-script-empty">请先在输入参数中定义参数</div>
              {/if}
            </div>
          {/snippet}
        </FloatingTrigger>
      </div>
    </div>
  </div>
</div>

<style lang="less">
  .code-script-editor-shell {
    width: 100%;
    position: relative;
  }

  .code-script-editor-shell.expanded-host {
    position: fixed;
    inset: 0;
    z-index: 9998;
    pointer-events: none;
  }

  .code-script-editor {
    width: 100%;
    position: relative;
    pointer-events: auto;
  }

  .code-script-overlay {
    position: fixed;
    inset: 0;
    z-index: 9998;
    pointer-events: auto;
    border: none;
    margin: 0;
    padding: 0;
    background: rgba(15, 23, 42, 0.28);
    backdrop-filter: blur(1px);
  }

  .code-script-editor :global(.cm-editor),
  .code-script-editor :global(.cm-scroller),
  .code-script-editor :global(.cm-content),
  .code-script-editor :global(.cm-line) {
    cursor: text;
  }

  .code-script-editor.expanded {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 700px;
    max-width: calc(100vw - 360px);
    min-width: 520px;
    height: min(80vh, calc(100vh - 180px));
    max-height: none;
    z-index: 9999;
  }

  .code-script-editor.expanded .code-script-editor-inner {
    height: 100%;
  }

  .code-script-editor.expanded :global(.cm-editor) {
    height: 100%;
    box-shadow: 0 16px 40px rgba(15, 23, 42, 0.22);
  }

  .code-script-editor.expanded :global(.cm-scroller) {
    height: 100%;
    min-height: 0;
  }

  .code-script-editor-inner {
    width: 100%;
  }

  .code-script-action {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 5;
  }

  .code-script-action-buttons {
    display: flex;
    align-items: center;
    gap: 6px;
  }

  :global(.code-script-button) {
    height: 20px;
    min-width: 20px;
    border: 1px solid transparent;
    padding: 0 4px;
    font-size: 10px;
    border-radius: 4px;
    color: var(--tf-text-secondary);
    background: var(--tf-bg-surface);
    border-color: var(--tf-border-color);
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  :global(.code-script-button svg) {
    width: 12px;
    height: 12px;
  }

  :global(.code-script-button.expanded-cancel) {
    height: 20px;
    min-width: 20px;
    padding: 0 4px;
    font-size: 10px;
    border-color: var(--tf-primary-soft-border);
    color: var(--tf-primary-color);
    background: var(--tf-primary-soft-bg);
  }

  @media (max-width: 900px) {
    .code-script-editor.expanded {
      width: calc(100vw - 120px);
      max-width: none;
      min-width: 0;
      height: calc(100vh - 96px);
    }
  }

  :global(.code-script-button:hover) {
    background: var(--tf-primary-soft-bg);
    color: var(--tf-primary-color);
    border-color: var(--tf-primary-soft-border);
  }

  .code-script-panel {
    min-width: 220px;
    max-width: 340px;
    max-height: 240px;
    overflow: auto;
    background: var(--tf-bg-surface);
    border: 1px solid var(--tf-border-color-strong);
    border-radius: 5px;
    box-shadow: var(--tf-shadow-medium);
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 4px;
  }

  .code-script-item {
    border: none;
    background: var(--tf-bg-surface);
    padding: 6px 8px;
    text-align: left;
    cursor: pointer;
    border-radius: 4px;
    font-size: 12px;
    line-height: 1.3;
    color: var(--tf-text-primary);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
  }

  .code-script-item:hover {
    background: var(--tf-bg-surface-alt);
    color: var(--tf-primary-color);
  }

  .code-script-item.unresolved {
    background: var(--tf-warning-soft-bg);
    color: var(--tf-warning-soft-text);
  }

  .code-script-item-warn {
    width: 14px;
    height: 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--tf-warning-icon-color);
    color: var(--tf-bg-surface);
    font-size: 10px;
    line-height: 1;
    flex-shrink: 0;
  }

  .code-script-empty {
    padding: 8px;
    font-size: 12px;
    color: var(--tf-text-muted);
    text-align: center;
  }

  .code-script-editor :global(.cm-special-result) {
    color: var(--tf-primary-color);
    font-weight: 700;
  }

  .code-script-editor :global(.cm-template-token-valid) {
    color: var(--tf-primary-color);
    background: var(--tf-primary-ghost-bg);
    border-radius: 4px;
  }

  .code-script-editor :global(.cm-template-token-unresolved) {
    color: var(--tf-warning-soft-text);
    background: var(--tf-warning-soft-bg);
    border-radius: 4px;
  }

  .code-script-editor :global(.cm-template-token-invalid) {
    color: var(--tf-danger-soft-text);
    background: var(--tf-danger-soft-bg);
    border-radius: 4px;
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete) {
    border: 1px solid var(--tf-border-color);
    background: var(--tf-bg-surface);
    border-radius: 10px;
    box-shadow: var(--tf-shadow-medium);
    padding: 6px;
    min-width: 260px;
    max-width: 360px;
    z-index: 60;
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete > ul) {
    font-family:
      "ui-monospace, SFMono-Regular, SF Mono, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace";
    font-size: 13px;
    max-height: 240px;
    overflow: auto;
    padding: 0;
    margin: 0;
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete > ul > li) {
    border: none;
    border-radius: 8px;
    padding: 8px 10px;
    line-height: 1.35;
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--tf-text-primary);
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]) {
    background: var(--tf-primary-soft-bg);
    color: var(--tf-primary-color);
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete > ul > li:hover) {
    background: var(--tf-bg-surface-alt);
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete .cm-completionIcon) {
    width: 16px;
    height: 16px;
    color: var(--tf-text-secondary);
    opacity: 1;
    font-size: 12px;
    margin-right: 0;
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected] .cm-completionIcon) {
    color: var(--tf-primary-color);
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete .cm-completionLabel) {
    color: inherit;
    font-weight: 500;
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete .cm-completionDetail) {
    margin-left: auto;
    color: var(--tf-text-muted);
    font-style: normal;
    font-size: 12px;
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete .cm-completionMatchedText) {
    text-decoration: none;
    color: var(--tf-primary-color);
    font-weight: 700;
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete > ul::-webkit-scrollbar) {
    width: 8px;
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete > ul::-webkit-scrollbar-thumb) {
    background: var(--tf-scrollbar-thumb);
    border-radius: 999px;
  }

  .code-script-editor :global(.cm-tooltip.cm-tooltip-autocomplete > ul::-webkit-scrollbar-track) {
    background: transparent;
  }

  :global(body.code-script-expanded-open) {
    overflow: hidden;
  }
</style>
