vzcode 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/assets/index-5f9f376e.css +1 -0
  2. package/dist/assets/index-70b6d947.js +111 -0
  3. package/dist/index.html +2 -2
  4. package/package.json +4 -4
  5. package/src/client/App.tsx +86 -43
  6. package/src/client/CodeEditor/getOrCreateEditor.ts +6 -1
  7. package/src/client/CodeEditor/index.tsx +60 -12
  8. package/src/client/CodeEditor/json1PresenceBroadcast.ts +6 -2
  9. package/src/client/CodeEditor/json1PresenceDisplay.ts +0 -45
  10. package/src/client/CodeEditor/style.scss +5 -1
  11. package/src/client/CodeErrorOverlay/index.tsx +27 -0
  12. package/src/client/CodeErrorOverlay/style.scss +30 -0
  13. package/src/client/PresenceNotifications/PresenceNotifications.test.ts +134 -0
  14. package/src/client/PresenceNotifications/index.tsx +118 -103
  15. package/src/client/SplitPaneResizeContext.tsx +12 -7
  16. package/src/client/TabList/Tab.tsx +20 -6
  17. package/src/client/TabList/index.tsx +18 -8
  18. package/src/client/TabList/style.scss +5 -0
  19. package/src/client/VZSettings.tsx +33 -20
  20. package/src/client/VZSidebar/{ConfirmationBox.tsx → DeleteConfirmationModal.tsx} +17 -14
  21. package/src/client/VZSidebar/DirectoryListing.tsx +27 -26
  22. package/src/client/VZSidebar/FileListing.tsx +16 -19
  23. package/src/client/VZSidebar/Item.tsx +83 -37
  24. package/src/client/VZSidebar/Listing.tsx +17 -14
  25. package/src/client/VZSidebar/index.tsx +43 -24
  26. package/src/client/style.scss +3 -0
  27. package/src/client/useActions.ts +25 -6
  28. package/src/client/useEditorCache.ts +2 -1
  29. package/src/client/useFileCRUD.ts +29 -27
  30. package/src/client/usernameLocalStorage.ts +43 -0
  31. package/src/client/vzReducer/closeTabReducer.test.ts +177 -0
  32. package/src/client/vzReducer/closeTabsReducer.ts +68 -0
  33. package/src/client/vzReducer/createInitialState.ts +19 -0
  34. package/src/client/vzReducer/editorNoLongerWantsFocusReducer.ts +9 -0
  35. package/src/client/vzReducer/index.ts +107 -0
  36. package/src/client/vzReducer/openTabReducer.test.ts +110 -0
  37. package/src/client/vzReducer/openTabReducer.ts +57 -0
  38. package/src/client/vzReducer/setActiveFileIdReducer.ts +9 -0
  39. package/src/client/vzReducer/setIsSettingsOpenReducer.ts +9 -0
  40. package/src/client/vzReducer/setThemeReducer.ts +9 -0
  41. package/src/client/vzReducer/setUsernameReducer.ts +9 -0
  42. package/src/index.ts +9 -3
  43. package/src/types.ts +12 -5
  44. package/dist/assets/index-284b7339.js +0 -111
  45. package/dist/assets/index-e160f758.css +0 -1
  46. package/src/client/PrettierErrorOverlay/index.tsx +0 -9
  47. package/src/client/PrettierErrorOverlay/style.scss +0 -9
  48. package/src/client/vzReducer.ts +0 -118
@@ -1,32 +1,35 @@
1
- import React from 'react';
2
1
  import { Button, Modal } from '../bootstrap';
3
2
 
3
+ // Confirmation modal for deleting a file or directory.
4
4
  // Inspired by https://react-bootstrap.netlify.app/docs/components/modal/#live-demo
5
- export const ConfirmationBox = ({
5
+ export const DeleteConfirmationModal = ({
6
6
  show,
7
7
  onClose,
8
8
  onConfirm,
9
+ isDirectory,
10
+ name,
9
11
  }) => (
10
- <Modal
11
- show={show}
12
- onHide={onClose}
13
- // confirm={onConfirm}
14
- centered
15
- >
12
+ <Modal show={show} onHide={onClose} centered>
16
13
  <Modal.Header closeButton onClick={onClose}>
17
- <Modal.Title>Delete File</Modal.Title>
14
+ <Modal.Title>
15
+ Delete {isDirectory ? 'Directory' : 'File'}
16
+ </Modal.Title>
18
17
  </Modal.Header>
19
18
 
20
19
  <Modal.Body>
21
- <p>Are you sure you want to delete this file?</p>
20
+ <p>
21
+ This will delete the{' '}
22
+ {isDirectory ? 'directory' : 'file'}{' '}
23
+ <strong>{name}</strong>.
24
+ </p>
22
25
  </Modal.Body>
23
26
 
24
27
  <Modal.Footer>
25
- <Button variant="primary" onClick={onConfirm}>
26
- Yes
27
- </Button>
28
28
  <Button variant="secondary" onClick={onClose}>
29
- No
29
+ Cancel
30
+ </Button>
31
+ <Button variant="primary" onClick={onConfirm}>
32
+ Delete
30
33
  </Button>
31
34
  </Modal.Footer>
32
35
  </Modal>
@@ -2,44 +2,44 @@ import { useCallback, useMemo } from 'react';
2
2
  import { Item } from './Item';
3
3
  import { Listing } from './Listing';
4
4
  import { DirectoryArrowSVG } from './DirectoryArrowSVG';
5
- import { FileTree, FileTreeFile } from '../../types';
5
+ import type {
6
+ FileTree,
7
+ FileTreeFile,
8
+ FileTreePath,
9
+ } from '../../types';
6
10
 
7
11
  export const DirectoryListing = ({
8
12
  name,
9
13
  path,
10
14
  children,
11
- handleRenameFileClick,
12
- handleDeleteFileClick,
15
+ renameFile,
16
+ deleteFile,
17
+ deleteDirectory,
13
18
  handleFileClick,
19
+ handleFileDoubleClick,
14
20
  isDirectoryOpen,
15
21
  toggleDirectory,
22
+ activeFileId,
16
23
  }: {
17
24
  name: string;
18
25
  path: string;
19
26
  children: Array<FileTree | FileTreeFile>;
20
- handleRenameFileClick: (
21
- fileId: string,
22
- newName: string,
23
- ) => void;
24
- handleDeleteFileClick: (
25
- fileId: string,
26
- event: React.MouseEvent,
27
- ) => void;
27
+ renameFile: (fileId: string, newName: string) => void;
28
+ deleteFile: (fileId: string) => void;
29
+ deleteDirectory: (path: FileTreePath) => void;
28
30
  handleFileClick: (fileId: string) => void;
31
+ handleFileDoubleClick: (fileId: string) => void;
29
32
  isDirectoryOpen: (path: string) => boolean;
30
33
  toggleDirectory: (path: string) => void;
34
+ activeFileId: string;
31
35
  }) => {
32
36
  const handleClick = useCallback(() => {
33
37
  toggleDirectory(path);
34
- }, [toggleDirectory]);
38
+ }, [toggleDirectory, path]);
35
39
 
36
- const handleDeleteClick = useCallback(
37
- (event) => {
38
- // https://github.com/vizhub-core/vzcode/issues/102
39
- handleDeleteFileClick(path, event);
40
- },
41
- [handleDeleteFileClick, path],
42
- );
40
+ const handleDeleteClick = useCallback(() => {
41
+ deleteDirectory(path);
42
+ }, [deleteDirectory, path]);
43
43
 
44
44
  const handleRenameClick = useCallback(() => {
45
45
  // https://github.com/vizhub-core/vzcode/issues/103
@@ -58,7 +58,7 @@ export const DirectoryListing = ({
58
58
  handleClick={handleClick}
59
59
  handleDeleteClick={handleDeleteClick}
60
60
  handleRenameClick={handleRenameClick}
61
- isDir={true}
61
+ isDirectory={true}
62
62
  >
63
63
  <div
64
64
  className="arrow-wrapper"
@@ -79,15 +79,16 @@ export const DirectoryListing = ({
79
79
  <Listing
80
80
  entity={entity}
81
81
  key={fileId || path}
82
- handleRenameFileClick={
83
- handleRenameFileClick
84
- }
85
- handleDeleteFileClick={
86
- handleDeleteFileClick
87
- }
82
+ renameFile={renameFile}
83
+ deleteFile={deleteFile}
84
+ deleteDirectory={deleteDirectory}
88
85
  handleFileClick={handleFileClick}
86
+ handleFileDoubleClick={
87
+ handleFileDoubleClick
88
+ }
89
89
  isDirectoryOpen={isDirectoryOpen}
90
90
  toggleDirectory={toggleDirectory}
91
+ activeFileId={activeFileId}
91
92
  />
92
93
  );
93
94
  })}
@@ -5,49 +5,46 @@ import { FileId } from '../../types';
5
5
  export const FileListing = ({
6
6
  name,
7
7
  fileId,
8
- handleRenameFileClick,
9
- handleDeleteFileClick,
8
+ renameFile,
9
+ deleteFile,
10
10
  handleFileClick,
11
+ handleFileDoubleClick,
11
12
  isActive,
12
13
  }: {
13
14
  name: string;
14
15
  fileId: FileId;
15
- handleRenameFileClick: (
16
- fileId: FileId,
17
- newName: string,
18
- ) => void;
19
- handleDeleteFileClick: (
20
- fileId: FileId,
21
- event: React.MouseEvent,
22
- ) => void;
16
+ renameFile: (fileId: FileId, newName: string) => void;
17
+ deleteFile: (fileId: FileId) => void;
23
18
  handleFileClick: (fileId: FileId) => void;
19
+ handleFileDoubleClick: (fileId: FileId) => void;
24
20
  isActive: boolean;
25
21
  }) => {
26
22
  const handleClick = useCallback(() => {
27
23
  handleFileClick(fileId);
28
24
  }, [fileId, handleFileClick]);
29
25
 
30
- const handleDeleteClick = useCallback(
31
- (event: React.MouseEvent) => {
32
- handleDeleteFileClick(fileId, event);
33
- },
34
- [fileId, handleDeleteFileClick],
35
- );
26
+ const handleDoubleClick = useCallback(() => {
27
+ handleFileDoubleClick(fileId);
28
+ }, [fileId, handleFileDoubleClick]);
29
+
30
+ const handleDeleteClick = useCallback(() => {
31
+ deleteFile(fileId);
32
+ }, [fileId, deleteFile]);
36
33
 
37
34
  const handleRenameClick = useCallback(
38
35
  (newName: string) => {
39
- handleRenameFileClick(fileId, newName);
36
+ renameFile(fileId, newName);
40
37
  },
41
- [fileId, handleRenameFileClick],
38
+ [fileId, renameFile],
42
39
  );
43
40
 
44
41
  return (
45
42
  <Item
46
43
  name={name}
47
44
  handleClick={handleClick}
45
+ handleDoubleClick={handleDoubleClick}
48
46
  handleDeleteClick={handleDeleteClick}
49
47
  handleRenameClick={handleRenameClick}
50
- isDir={false}
51
48
  isActive={isActive}
52
49
  >
53
50
  {name}
@@ -1,14 +1,36 @@
1
- import { useState, useCallback, useMemo } from 'react';
2
- import { ConfirmationBox } from './ConfirmationBox';
1
+ import {
2
+ useState,
3
+ useCallback,
4
+ useRef,
5
+ useEffect,
6
+ } from 'react';
3
7
 
8
+ // TODO consider moving this up to a higher level of the component tree
9
+ import { DeleteConfirmationModal } from './DeleteConfirmationModal';
10
+
11
+ // TODO support renaming directories
12
+ // See https://github.com/vizhub-core/vzcode/issues/103
13
+ const enableRenameDirectory = false;
14
+
15
+ // A file or directory in the sidebar.
4
16
  export const Item = ({
5
17
  name,
6
18
  children,
7
19
  handleClick,
20
+ handleDoubleClick,
8
21
  handleRenameClick,
9
22
  handleDeleteClick,
10
- isDir,
23
+ isDirectory = false,
11
24
  isActive = false,
25
+ }: {
26
+ name: string;
27
+ children: React.ReactNode;
28
+ handleClick: (event: React.MouseEvent) => void;
29
+ handleDoubleClick?: (event: React.MouseEvent) => void;
30
+ handleRenameClick: (newName: string) => void;
31
+ handleDeleteClick: () => void;
32
+ isDirectory?: boolean;
33
+ isActive?: boolean;
12
34
  }) => {
13
35
  // Tracks whether the mouse is hovering over the file or directory
14
36
  const [isHovered, setIsHovered] = useState(false);
@@ -19,28 +41,48 @@ export const Item = ({
19
41
  // Tracks the value of the input field when renaming
20
42
  const [renameValue, setRenameValue] = useState('');
21
43
 
22
- const onKeyDown = useCallback((event) => {
23
- if (event.key === 'Escape') {
24
- setIsRenaming(false);
25
- } else if (event.key === 'Enter') {
26
- event.target.blur();
27
- }
28
- }, []);
44
+ // Tracks whether the delete confirmation modal is open
45
+ const [showModal, setShowModal] = useState(false);
46
+
47
+ // Ref to track the input DOM, so that we can focus and blur it
48
+ const renameInputRef = useRef(null);
29
49
 
30
- const onBlur = useCallback(
31
- (event: React.FocusEvent<HTMLInputElement>) => {
32
- if (event.target.value.trim() === '') {
33
- // setEditingValue(value);
34
- console.log('TODO: handle empty rename');
35
- } else {
50
+ const onKeyDown = useCallback(
51
+ (event: React.KeyboardEvent<HTMLInputElement>) => {
52
+ if (event.key === 'Escape') {
36
53
  setIsRenaming(false);
37
- handleRenameClick(event.target.value);
54
+ } else if (event.key === 'Enter') {
55
+ renameInputRef.current.blur();
38
56
  }
39
57
  },
40
- [handleRenameClick],
58
+ [],
41
59
  );
42
60
 
43
- const [showModal, setShowModal] = useState(false);
61
+ const onBlur = useCallback(() => {
62
+ setIsRenaming(false);
63
+ if (renameInputRef.current.value.trim() === '') {
64
+ // Renaming to empty signals deletion
65
+ setShowModal(true);
66
+ } else {
67
+ handleRenameClick(renameInputRef.current.value);
68
+ }
69
+ }, [handleRenameClick]);
70
+
71
+ const handleRenameIconClick = useCallback(
72
+ (event: React.MouseEvent) => {
73
+ event.stopPropagation();
74
+ setIsRenaming(!isRenaming);
75
+ setRenameValue(name);
76
+ },
77
+ [name, isRenaming],
78
+ );
79
+
80
+ // Focus the input field when renaming
81
+ useEffect(() => {
82
+ if (isRenaming) {
83
+ renameInputRef.current.focus();
84
+ }
85
+ }, [isRenaming]);
44
86
 
45
87
  // Function to open the delete file confirmation modal
46
88
  const handleModalOpen = useCallback(
@@ -74,25 +116,28 @@ export const Item = ({
74
116
  setIsHovered(false);
75
117
  }, []);
76
118
 
77
- const onChange = useCallback(
78
- (event: React.ChangeEvent<HTMLInputElement>) => {
79
- setRenameValue(event.target.value);
80
- },
81
- [],
82
- );
119
+ const onChange = useCallback(() => {
120
+ setRenameValue(renameInputRef.current.value);
121
+ }, []);
83
122
 
84
123
  return (
85
124
  <div
86
- className={`full-box file-or-directory ${
125
+ className={`full-box file-or-directory user-select-none ${
87
126
  isActive ? 'active-file' : ''
88
127
  }`}
89
128
  onClick={isRenaming ? null : handleClick}
129
+ onDoubleClick={
130
+ isRenaming || !handleDoubleClick
131
+ ? null
132
+ : handleDoubleClick
133
+ }
90
134
  onMouseEnter={handleMouseEnter}
91
135
  onMouseLeave={handleMouseLeave}
92
136
  >
93
137
  <div className="name">
94
138
  {isRenaming ? (
95
139
  <input
140
+ ref={renameInputRef}
96
141
  type="text"
97
142
  aria-label="Field name"
98
143
  value={renameValue}
@@ -104,17 +149,16 @@ export const Item = ({
104
149
  children
105
150
  )}
106
151
  </div>
107
- {isHovered ? (
152
+ {isHovered && !isRenaming ? (
108
153
  <div className="utils">
109
- <i
110
- className="bx bxs-edit utilities"
111
- style={{ color: '#9edcff' }}
112
- onClick={(event: React.MouseEvent) => {
113
- event.stopPropagation();
114
- setIsRenaming(!isRenaming);
115
- setRenameValue(isDir ? name : children);
116
- }}
117
- ></i>
154
+ {(isDirectory ? enableRenameDirectory : true) ? (
155
+ <i
156
+ className="bx bxs-edit utilities"
157
+ style={{ color: '#9edcff' }}
158
+ onClick={handleRenameIconClick}
159
+ ></i>
160
+ ) : null}
161
+
118
162
  <i
119
163
  className="bx bx-trash utilities"
120
164
  style={{ color: '#ff006b' }}
@@ -123,10 +167,12 @@ export const Item = ({
123
167
  </div>
124
168
  ) : null}
125
169
 
126
- <ConfirmationBox
170
+ <DeleteConfirmationModal
127
171
  show={showModal}
128
172
  onClose={handleModalClose}
129
173
  onConfirm={handleConfirmModal}
174
+ isDirectory={isDirectory}
175
+ name={name}
130
176
  />
131
177
  </div>
132
178
  );
@@ -3,6 +3,7 @@ import {
3
3
  FileId,
4
4
  FileTree,
5
5
  FileTreeFile,
6
+ FileTreePath,
6
7
  } from '../../types';
7
8
  import { FileListing } from './FileListing';
8
9
 
@@ -10,23 +11,21 @@ import { FileListing } from './FileListing';
10
11
  // that appears in the Sidebar.
11
12
  export const Listing = ({
12
13
  entity,
13
- handleRenameFileClick,
14
- handleDeleteFileClick,
14
+ renameFile,
15
+ deleteFile,
16
+ deleteDirectory,
15
17
  handleFileClick,
18
+ handleFileDoubleClick,
16
19
  isDirectoryOpen,
17
20
  toggleDirectory,
18
21
  activeFileId,
19
22
  }: {
20
23
  entity: FileTree | FileTreeFile;
21
- handleRenameFileClick: (
22
- fileId: FileId,
23
- newName: string,
24
- ) => void;
25
- handleDeleteFileClick: (
26
- fileId: FileId,
27
- event: React.MouseEvent,
28
- ) => void;
24
+ renameFile: (fileId: FileId, newName: string) => void;
25
+ deleteFile: (fileId: FileId) => void;
26
+ deleteDirectory: (path: FileTreePath) => void;
29
27
  handleFileClick: (fileId: FileId) => void;
28
+ handleFileDoubleClick: (fileId: FileId) => void;
30
29
  isDirectoryOpen: (path: string) => boolean;
31
30
  toggleDirectory: (path: string) => void;
32
31
  activeFileId: FileId;
@@ -37,9 +36,10 @@ export const Listing = ({
37
36
  <FileListing
38
37
  fileId={fileId}
39
38
  name={name}
40
- handleRenameFileClick={handleRenameFileClick}
41
- handleDeleteFileClick={handleDeleteFileClick}
39
+ renameFile={renameFile}
40
+ deleteFile={deleteFile}
42
41
  handleFileClick={handleFileClick}
42
+ handleFileDoubleClick={handleFileDoubleClick}
43
43
  isActive={fileId === activeFileId}
44
44
  />
45
45
  ) : (
@@ -47,11 +47,14 @@ export const Listing = ({
47
47
  name={name}
48
48
  path={path}
49
49
  children={children}
50
- handleRenameFileClick={handleRenameFileClick}
51
- handleDeleteFileClick={handleDeleteFileClick}
50
+ renameFile={renameFile}
51
+ deleteFile={deleteFile}
52
+ deleteDirectory={deleteDirectory}
52
53
  handleFileClick={handleFileClick}
54
+ handleFileDoubleClick={handleFileDoubleClick}
53
55
  isDirectoryOpen={isDirectoryOpen}
54
56
  toggleDirectory={toggleDirectory}
57
+ activeFileId={activeFileId}
55
58
  />
56
59
  );
57
60
  };
@@ -3,6 +3,7 @@ import {
3
3
  FileId,
4
4
  FileTree,
5
5
  FileTreeFile,
6
+ FileTreePath,
6
7
  Files,
7
8
  } from '../../types';
8
9
  import { getFileTree } from '../getFileTree';
@@ -13,30 +14,32 @@ import { Listing } from './Listing';
13
14
  import './styles.scss';
14
15
 
15
16
  export const VZSidebar = ({
16
- createFile,
17
17
  files,
18
- handleRenameFileClick,
19
- handleDeleteFileClick,
20
- handleFileClick,
18
+ createFile,
19
+ renameFile,
20
+ deleteFile,
21
+ deleteDirectory,
22
+ openTab,
21
23
  setIsSettingsOpen,
22
24
  isDirectoryOpen,
23
25
  toggleDirectory,
24
26
  activeFileId,
25
27
  }: {
26
28
  files: Files;
27
- createFile?: () => void;
28
- handleRenameFileClick?: (
29
- fileId: FileId,
30
- newName: string,
31
- ) => void;
32
- handleDeleteFileClick?: (
33
- fileId: FileId,
34
- event: React.MouseEvent,
35
- ) => void;
36
- handleFileClick?: (fileId: FileId) => void;
37
- setIsSettingsOpen?: (isSettingsOpen: boolean) => void;
38
- isDirectoryOpen?: (path: string) => boolean;
39
- toggleDirectory?: (path: string) => void;
29
+ createFile: () => void;
30
+ renameFile: (fileId: FileId, newName: string) => void;
31
+ deleteFile: (fileId: FileId) => void;
32
+ deleteDirectory: (path: FileTreePath) => void;
33
+ openTab: ({
34
+ fileId,
35
+ isTransient,
36
+ }: {
37
+ fileId: FileId;
38
+ isTransient?: boolean;
39
+ }) => void;
40
+ setIsSettingsOpen: (isSettingsOpen: boolean) => void;
41
+ isDirectoryOpen: (path: string) => boolean;
42
+ toggleDirectory: (path: string) => void;
40
43
  activeFileId?: FileId;
41
44
  }) => {
42
45
  const fileTree = useMemo(
@@ -52,6 +55,22 @@ export const VZSidebar = ({
52
55
  SplitPaneResizeContext,
53
56
  );
54
57
 
58
+ // On single-click, open the file in a transient tab.
59
+ const handleFileClick = useCallback(
60
+ (fileId: FileId) => {
61
+ openTab({ fileId, isTransient: true });
62
+ },
63
+ [openTab],
64
+ );
65
+
66
+ // On double-click, open the file in a persistent tab.
67
+ const handleFileDoubleClick = useCallback(
68
+ (fileId: FileId) => {
69
+ openTab({ fileId, isTransient: false });
70
+ },
71
+ [openTab],
72
+ );
73
+
55
74
  return (
56
75
  <div
57
76
  className="vz-sidebar"
@@ -75,15 +94,15 @@ export const VZSidebar = ({
75
94
  const key = fileId ? fileId : path;
76
95
  return (
77
96
  <Listing
78
- entity={entity}
79
97
  key={key}
80
- handleRenameFileClick={
81
- handleRenameFileClick
82
- }
83
- handleDeleteFileClick={
84
- handleDeleteFileClick
85
- }
98
+ entity={entity}
99
+ renameFile={renameFile}
100
+ deleteFile={deleteFile}
101
+ deleteDirectory={deleteDirectory}
86
102
  handleFileClick={handleFileClick}
103
+ handleFileDoubleClick={
104
+ handleFileDoubleClick
105
+ }
87
106
  isDirectoryOpen={isDirectoryOpen}
88
107
  toggleDirectory={toggleDirectory}
89
108
  activeFileId={activeFileId}
@@ -1,6 +1,9 @@
1
1
  :root {
2
2
  --vzcode-font-family: ui-monospace, SFMono-Regular,
3
3
  'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace !important;
4
+
5
+ --vzcode-font-size: 16px;
6
+
4
7
  /* Variable for the background color */
5
8
  --vz-bg-color: rgb(
6
9
  41,
@@ -1,7 +1,7 @@
1
1
  import { useCallback } from 'react';
2
2
  import { ThemeLabel } from './themes';
3
- import { FileId } from '../types';
4
- import { VZAction } from './vzReducer';
3
+ import { FileId, Username } from '../types';
4
+ import { TabState, VZAction } from './vzReducer';
5
5
 
6
6
  // This is a custom hook that returns a set of functions
7
7
  // that can be used to dispatch actions to the reducer.
@@ -19,20 +19,21 @@ export const useActions = (
19
19
  );
20
20
 
21
21
  const openTab = useCallback(
22
- (fileId: FileId) => {
22
+ (tabState: TabState): void => {
23
23
  dispatch({
24
24
  type: 'open_tab',
25
- fileId,
25
+ fileId: tabState.fileId,
26
+ isTransient: tabState.isTransient,
26
27
  });
27
28
  },
28
29
  [dispatch],
29
30
  );
30
31
 
31
32
  const closeTabs = useCallback(
32
- (idsToDelete: Array<FileId>) => {
33
+ (fileIdsToClose: Array<FileId>) => {
33
34
  dispatch({
34
35
  type: 'close_tabs',
35
- idsToDelete,
36
+ fileIdsToClose,
36
37
  });
37
38
  },
38
39
  [dispatch],
@@ -63,6 +64,22 @@ export const useActions = (
63
64
  setIsSettingsOpen(false);
64
65
  }, [setIsSettingsOpen]);
65
66
 
67
+ const editorNoLongerWantsFocus = useCallback(() => {
68
+ dispatch({
69
+ type: 'editor_no_longer_wants_focus',
70
+ });
71
+ }, [dispatch]);
72
+
73
+ const setUsername = useCallback(
74
+ (username: Username) => {
75
+ dispatch({
76
+ type: 'set_username',
77
+ username,
78
+ });
79
+ },
80
+ [dispatch],
81
+ );
82
+
66
83
  return {
67
84
  setActiveFileId,
68
85
  openTab,
@@ -70,5 +87,7 @@ export const useActions = (
70
87
  setTheme,
71
88
  setIsSettingsOpen,
72
89
  closeSettings,
90
+ editorNoLongerWantsFocus,
91
+ setUsername,
73
92
  };
74
93
  };
@@ -1,8 +1,9 @@
1
1
  import { useRef } from 'react';
2
2
  import { FileId } from '../types';
3
+ import { EditorView } from 'codemirror';
3
4
 
4
5
  export type EditorCacheValue = {
5
- editor: any;
6
+ editor: EditorView;
6
7
  themeCompartment: any;
7
8
  };
8
9