File size: 4,954 Bytes
baa4c21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { useStore } from '@nanostores/react';
import { memo, useMemo } from 'react';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import {
  CodeMirrorEditor,
  type EditorDocument,
  type EditorSettings,
  type OnChangeCallback as OnEditorChange,
  type OnSaveCallback as OnEditorSave,
  type OnScrollCallback as OnEditorScroll,
} from '~/components/editor/codemirror/CodeMirrorEditor';
import { PanelHeader } from '~/components/ui/PanelHeader';
import { PanelHeaderButton } from '~/components/ui/PanelHeaderButton';
import type { FileMap } from '~/lib/stores/files';
import { themeStore } from '~/lib/stores/theme';
import { WORK_DIR } from '~/utils/constants';
import { renderLogger } from '~/utils/logger';
import { isMobile } from '~/utils/mobile';
import { FileBreadcrumb } from './FileBreadcrumb';
import { FileTree } from './FileTree';
import { DEFAULT_TERMINAL_SIZE, TerminalTabs } from './terminal/TerminalTabs';
import { workbenchStore } from '~/lib/stores/workbench';

interface EditorPanelProps {
  files?: FileMap;
  unsavedFiles?: Set<string>;
  editorDocument?: EditorDocument;
  selectedFile?: string | undefined;
  isStreaming?: boolean;
  onEditorChange?: OnEditorChange;
  onEditorScroll?: OnEditorScroll;
  onFileSelect?: (value?: string) => void;
  onFileSave?: OnEditorSave;
  onFileReset?: () => void;
}

const DEFAULT_EDITOR_SIZE = 100 - DEFAULT_TERMINAL_SIZE;

const editorSettings: EditorSettings = { tabSize: 2 };

export const EditorPanel = memo(
  ({

    files,

    unsavedFiles,

    editorDocument,

    selectedFile,

    isStreaming,

    onFileSelect,

    onEditorChange,

    onEditorScroll,

    onFileSave,

    onFileReset,

  }: EditorPanelProps) => {
    renderLogger.trace('EditorPanel');

    const theme = useStore(themeStore);
    const showTerminal = useStore(workbenchStore.showTerminal);

    const activeFileSegments = useMemo(() => {
      if (!editorDocument) {
        return undefined;
      }

      return editorDocument.filePath.split('/');
    }, [editorDocument]);

    const activeFileUnsaved = useMemo(() => {
      return editorDocument !== undefined && unsavedFiles?.has(editorDocument.filePath);
    }, [editorDocument, unsavedFiles]);

    return (
      <PanelGroup direction="vertical">

        <Panel defaultSize={showTerminal ? DEFAULT_EDITOR_SIZE : 100} minSize={20}>

          <PanelGroup direction="horizontal">

            <Panel defaultSize={20} minSize={10} collapsible>

              <div className="flex flex-col border-r border-bolt-elements-borderColor h-full">

                <PanelHeader>

                  <div className="i-ph:tree-structure-duotone shrink-0" />

                  Files

                </PanelHeader>

                <FileTree

                  className="h-full"

                  files={files}

                  hideRoot

                  unsavedFiles={unsavedFiles}

                  rootFolder={WORK_DIR}

                  selectedFile={selectedFile}

                  onFileSelect={onFileSelect}

                />

              </div>

            </Panel>

            <PanelResizeHandle />

            <Panel className="flex flex-col" defaultSize={80} minSize={20}>

              <PanelHeader className="overflow-x-auto">

                {activeFileSegments?.length && (

                  <div className="flex items-center flex-1 text-sm">

                    <FileBreadcrumb pathSegments={activeFileSegments} files={files} onFileSelect={onFileSelect} />

                    {activeFileUnsaved && (

                      <div className="flex gap-1 ml-auto -mr-1.5">

                        <PanelHeaderButton onClick={onFileSave}>

                          <div className="i-ph:floppy-disk-duotone" />

                          Save

                        </PanelHeaderButton>

                        <PanelHeaderButton onClick={onFileReset}>

                          <div className="i-ph:clock-counter-clockwise-duotone" />

                          Reset

                        </PanelHeaderButton>

                      </div>

                    )}

                  </div>

                )}

              </PanelHeader>

              <div className="h-full flex-1 overflow-hidden">

                <CodeMirrorEditor

                  theme={theme}

                  editable={!isStreaming && editorDocument !== undefined}

                  settings={editorSettings}

                  doc={editorDocument}

                  autoFocusOnDocumentChange={!isMobile()}

                  onScroll={onEditorScroll}

                  onChange={onEditorChange}

                  onSave={onFileSave}

                />

              </div>

            </Panel>

          </PanelGroup>

        </Panel>

        <PanelResizeHandle />

        <TerminalTabs />

      </PanelGroup>
    );
  },
);