Skip to content

Commit 0d1d3e4

Browse files
committed
feat(runtime): new metadata option editor.fileTree.allowEdits
1 parent 35b666c commit 0d1d3e4

File tree

6 files changed

+117
-19
lines changed

6 files changed

+117
-19
lines changed

Diff for: packages/react/src/Panels/WorkspacePanel.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ function EditorSection({ theme, tutorialStore, hasEditor }: PanelProps) {
9898
const selectedFile = useStore(tutorialStore.selectedFile);
9999
const currentDocument = useStore(tutorialStore.currentDocument);
100100
const lessonFullyLoaded = useStore(tutorialStore.lessonFullyLoaded);
101+
const editorConfig = useStore(tutorialStore.editorConfig);
101102
const storeRef = useStore(tutorialStore.ref);
102103
const files = useStore(tutorialStore.files);
103104

@@ -159,7 +160,7 @@ function EditorSection({ theme, tutorialStore, hasEditor }: PanelProps) {
159160
helpAction={helpAction}
160161
onHelpClick={lessonFullyLoaded ? onHelpClick : undefined}
161162
onFileSelect={(filePath) => tutorialStore.setSelectedFile(filePath)}
162-
onFileTreeChange={onFileTreeChange}
163+
onFileTreeChange={editorConfig.fileTree.allowEdits ? onFileTreeChange : undefined}
163164
selectedFile={selectedFile}
164165
onEditorScroll={(position) => tutorialStore.setCurrentDocumentScrollPosition(position)}
165166
onEditorChange={(update) => tutorialStore.setCurrentDocumentContent(update.content)}

Diff for: packages/runtime/src/store/editor.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import type { FilesRefList, Files } from '@tutorialkit/types';
1+
import type { FilesRefList, Files, EditorSchema } from '@tutorialkit/types';
22
import { atom, map, computed } from 'nanostores';
3+
import { EditorConfig } from '../webcontainer/editor-config.js';
34

45
export interface EditorDocument {
56
value: string | Uint8Array;
@@ -16,6 +17,7 @@ export interface ScrollPosition {
1617
export type EditorDocuments = Record<string, EditorDocument | undefined>;
1718

1819
export class EditorStore {
20+
editorConfig = atom<EditorConfig>(new EditorConfig());
1921
selectedFile = atom<string | undefined>();
2022
documents = map<EditorDocuments>({});
2123

@@ -28,6 +30,10 @@ export class EditorStore {
2830
return documents[selectedFile];
2931
});
3032

33+
setEditorConfig(config?: EditorSchema) {
34+
this.editorConfig.set(new EditorConfig(config));
35+
}
36+
3137
setSelectedFile(filePath: string | undefined) {
3238
this.selectedFile.set(filePath);
3339
}

Diff for: packages/runtime/src/store/index.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { ITerminal } from '../utils/terminal.js';
88
import { bootStatus, unblockBoot, type BootStatus } from '../webcontainer/on-demand-boot.js';
99
import type { PreviewInfo } from '../webcontainer/preview-info.js';
1010
import { StepsController } from '../webcontainer/steps.js';
11+
import type { EditorConfig } from '../webcontainer/editor-config.js';
1112
import type { TerminalConfig } from '../webcontainer/terminal-config.js';
1213
import { EditorStore, type EditorDocument, type EditorDocuments, type ScrollPosition } from './editor.js';
1314
import { PreviewsStore } from './previews.js';
@@ -141,6 +142,7 @@ export class TutorialStore {
141142

142143
this._previewsStore.setPreviews(lesson.data.previews ?? true);
143144
this._terminalStore.setTerminalConfiguration(lesson.data.terminal);
145+
this._editorStore.setEditorConfig(lesson.data.editor);
144146
this._runner.setCommands(lesson.data);
145147
this._editorStore.setDocuments(lesson.files);
146148

@@ -195,6 +197,10 @@ export class TutorialStore {
195197
return this._terminalStore.terminalConfig;
196198
}
197199

200+
get editorConfig(): ReadableAtom<EditorConfig> {
201+
return this._editorStore.editorConfig;
202+
}
203+
198204
get currentDocument(): ReadableAtom<EditorDocument | undefined> {
199205
return this._editorStore.currentDocument;
200206
}
@@ -243,19 +249,15 @@ export class TutorialStore {
243249
return false;
244250
}
245251

246-
const { editor } = this._lesson.data;
247-
248-
return editor === undefined || editor === true || (editor !== false && editor?.fileTree !== false);
252+
return this.editorConfig.get().fileTree.visible;
249253
}
250254

251255
hasEditor(): boolean {
252256
if (!this._lesson) {
253257
return false;
254258
}
255259

256-
const { editor } = this._lesson.data;
257-
258-
return editor !== false;
260+
return this.editorConfig.get().visible;
259261
}
260262

261263
hasPreviews(): boolean {

Diff for: packages/runtime/src/webcontainer/editor-config.ts

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import type { EditorSchema } from '@tutorialkit/types';
2+
3+
interface NormalizedEditorConfig {
4+
/** Visibility of editor */
5+
visible: boolean;
6+
7+
fileTree: {
8+
/** Visibility of file tree */
9+
visible: boolean;
10+
11+
/** Whether to allow file and folder editing in file tree */
12+
allowEdits: boolean;
13+
};
14+
}
15+
16+
export class EditorConfig {
17+
private _config: NormalizedEditorConfig;
18+
19+
constructor(config?: EditorSchema) {
20+
this._config = normalizeEditorConfig(config);
21+
}
22+
23+
get visible() {
24+
return this._config.visible;
25+
}
26+
27+
get fileTree() {
28+
return this._config.fileTree;
29+
}
30+
}
31+
32+
function normalizeEditorConfig(config?: EditorSchema): NormalizedEditorConfig {
33+
if (config === false) {
34+
return {
35+
visible: false,
36+
fileTree: {
37+
visible: false,
38+
allowEdits: false,
39+
},
40+
};
41+
}
42+
43+
if (config === undefined || config === true) {
44+
return {
45+
visible: true,
46+
fileTree: {
47+
visible: true,
48+
allowEdits: false,
49+
},
50+
};
51+
}
52+
53+
if (typeof config.fileTree === 'boolean') {
54+
return {
55+
visible: true,
56+
fileTree: {
57+
visible: config.fileTree,
58+
allowEdits: false,
59+
},
60+
};
61+
}
62+
63+
return {
64+
visible: true,
65+
fileTree: {
66+
visible: true,
67+
allowEdits: config.fileTree?.allowEdits || false,
68+
},
69+
};
70+
}

Diff for: packages/template/src/content/tutorial/1-basics/1-introduction/2-foo/content.mdx

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ previews:
88
- [1, 'Test Runner']
99
- '2/some/custom/pathname'
1010
- '2/another/pathname'
11+
editor:
12+
fileTree:
13+
allowEdits: true
1114
terminal:
1215
panels: 'terminal'
1316
editPageLink: 'https://tutorialkit.dev'

Diff for: packages/types/src/schemas/common.ts

+27-11
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,32 @@ export const terminalSchema = z.union([
161161
}),
162162
]);
163163

164+
export const editorSchema = z.union([
165+
// can either be completely removed by setting it to `false`
166+
z.boolean().optional(),
167+
168+
z.strictObject({
169+
fileTree: z
170+
.union([
171+
// or you can only remove the file tree
172+
z.boolean(),
173+
174+
// or configure file tree with options
175+
z.strictObject({
176+
allowEdits: z
177+
.boolean()
178+
.describe(
179+
'Allow file tree’s items to be edited by right clicking them. Supports file and folder creation.',
180+
),
181+
}),
182+
])
183+
.optional(),
184+
}),
185+
]);
186+
164187
export type TerminalPanelType = z.infer<typeof panelTypeSchema>;
165188
export type TerminalSchema = z.infer<typeof terminalSchema>;
189+
export type EditorSchema = z.infer<typeof editorSchema>;
166190

167191
export const webcontainerSchema = commandsSchema.extend({
168192
previews: previewSchema
@@ -191,18 +215,10 @@ export const webcontainerSchema = commandsSchema.extend({
191215
.string()
192216
.optional()
193217
.describe('Defines which file should be opened in the code editor by default when lesson loads.'),
194-
editor: z
195-
.union([
196-
// can either be completely removed by setting it to `false`
197-
z.boolean().optional(),
198-
199-
// or you can only remove the file tree
200-
z.strictObject({
201-
fileTree: z.boolean().optional(),
202-
}),
203-
])
218+
editor: editorSchema
219+
.optional()
204220
.describe(
205-
'Configure whether or not the editor should be rendered. If an object is provided with fileTree: false, only the file tree is hidden.',
221+
'Configure whether or not the editor should be rendered. File tree can be configured by proving an object with fileTree option.',
206222
),
207223
i18n: i18nSchema
208224
.optional()

0 commit comments

Comments
 (0)