Skip to content

Commit

Permalink
feat(runtime): store to support file and folder creation
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Sep 9, 2024
1 parent 7a5faac commit a3944c3
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 3 deletions.
5 changes: 4 additions & 1 deletion packages/react/src/Panels/EditorPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { I18n } from '@tutorialkit/types';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, type ComponentProps } from 'react';
import { Panel, PanelGroup, PanelResizeHandle, type ImperativePanelHandle } from 'react-resizable-panels';
import {
CodeMirrorEditor,
Expand Down Expand Up @@ -29,6 +29,7 @@ interface Props {
onEditorScroll?: OnEditorScroll;
onHelpClick?: () => void;
onFileSelect?: (value?: string) => void;
onFileTreeChange?: ComponentProps<typeof FileTree>['onFileChange'];
}

export function EditorPanel({
Expand All @@ -46,6 +47,7 @@ export function EditorPanel({
onEditorScroll,
onHelpClick,
onFileSelect,
onFileTreeChange,
}: Props) {
const fileTreePanelRef = useRef<ImperativePanelHandle>(null);

Expand Down Expand Up @@ -81,6 +83,7 @@ export function EditorPanel({
files={files}
scope={fileTreeScope}
onFileSelect={onFileSelect}
onFileChange={onFileTreeChange}
/>
</Panel>
<PanelResizeHandle
Expand Down
15 changes: 14 additions & 1 deletion packages/react/src/Panels/WorkspacePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useStore } from '@nanostores/react';
import { TutorialStore } from '@tutorialkit/runtime';
import type { I18n } from '@tutorialkit/types';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useRef, useState, type ComponentProps } from 'react';
import { Panel, PanelGroup, PanelResizeHandle, type ImperativePanelHandle } from 'react-resizable-panels';
import type { Theme } from '../core/types.js';
import resizePanelStyles from '../styles/resize-panel.module.css';
Expand All @@ -12,6 +12,8 @@ import { TerminalPanel } from './TerminalPanel.js';

const DEFAULT_TERMINAL_SIZE = 25;

type FileTreeChangeEvent = Parameters<NonNullable<ComponentProps<typeof EditorPanel>['onFileTreeChange']>>[0];

interface Props {
tutorialStore: TutorialStore;
theme: Theme;
Expand Down Expand Up @@ -111,6 +113,16 @@ function EditorSection({ theme, tutorialStore, hasEditor }: PanelProps) {
}
}

function onFileTreeChange({ method, type, value }: FileTreeChangeEvent) {
if (method == 'ADD' && type === 'FILE') {
return tutorialStore.addFile(value);
}

if (method == 'ADD' && type === 'DIRECTORY') {
return tutorialStore.addFolder(value);
}
}

useEffect(() => {
if (tutorialStore.hasSolution()) {
setHelpAction('solve');
Expand Down Expand Up @@ -139,6 +151,7 @@ function EditorSection({ theme, tutorialStore, hasEditor }: PanelProps) {
helpAction={helpAction}
onHelpClick={lessonFullyLoaded ? onHelpClick : undefined}
onFileSelect={(filePath) => tutorialStore.setSelectedFile(filePath)}
onFileTreeChange={onFileTreeChange}
selectedFile={selectedFile}
onEditorScroll={(position) => tutorialStore.setCurrentDocumentScrollPosition(position)}
onEditorChange={(update) => tutorialStore.setCurrentDocumentContent(update.content)}
Expand Down
7 changes: 7 additions & 0 deletions packages/react/src/core/FileTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ import { classNames } from '../utils/classnames.js';
const NODE_PADDING_LEFT = 12;
const DEFAULT_HIDDEN_FILES = [/\/node_modules\//];

interface FileChangeEvent {
type: 'FILE' | 'DIRECTORY';
method: 'ADD' | 'REMOVE' | 'RENAME';
value: string;
}

interface Props {
files: string[];
selectedFile?: string;
onFileSelect?: (filePath: string) => void;
onFileChange?: (event: FileChangeEvent) => void;
hideRoot: boolean;
scope?: string;
hiddenFiles?: Array<string | RegExp>;
Expand Down
17 changes: 16 additions & 1 deletion packages/runtime/src/store/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface ScrollPosition {
left: number;
}

export type EditorDocuments = Record<string, EditorDocument>;
export type EditorDocuments = Record<string, EditorDocument | undefined>;

export class EditorStore {
selectedFile = atom<string | undefined>();
Expand Down Expand Up @@ -83,6 +83,21 @@ export class EditorStore {
});
}

addFileOrFolder(filePath: string) {
// when adding file to empty folder, remove the empty folder from documents
const emptyDirectory = this.files.value?.find((path) => filePath.startsWith(path));

if (emptyDirectory) {
this.documents.setKey(emptyDirectory, undefined);
}

this.documents.setKey(filePath, {
filePath,
value: '',
loading: false,
});
}

updateFile(filePath: string, content: string): boolean {
const documentState = this.documents.get()[filePath];

Expand Down
21 changes: 21 additions & 0 deletions packages/runtime/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,27 @@ export class TutorialStore {
this._editorStore.setSelectedFile(filePath);
}

addFile(filePath: string) {
// prevent creating duplicates
if (this._editorStore.files.get().includes(filePath)) {
return this.setSelectedFile(filePath);
}

this._editorStore.addFileOrFolder(filePath);
this.setSelectedFile(filePath);
this._runner.updateFile(filePath, '');
}

addFolder(folderPath: string) {
// prevent creating duplicates
if (this._editorStore.files.get().includes(folderPath)) {
return this.setSelectedFile(folderPath);
}

this._editorStore.addFileOrFolder(folderPath);
this._runner.createFolder(folderPath);
}

updateFile(filePath: string, content: string) {
const hasChanged = this._editorStore.updateFile(filePath, content);

Expand Down
17 changes: 17 additions & 0 deletions packages/runtime/src/tutorial-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,23 @@ export class TutorialRunner {
this._currentCommandProcess?.resize({ cols, rows });
}

createFolder(folderPAth: string): void {
const previousLoadPromise = this._currentLoadTask?.promise;

this._currentLoadTask = newTask(
async (signal) => {
await previousLoadPromise;

const webcontainer = await this._webcontainer;

signal.throwIfAborted();

await webcontainer.fs.mkdir(folderPAth);
},
{ ignoreCancel: true },
);
}

/**
* Update the content of a single file in WebContainer.
*
Expand Down

0 comments on commit a3944c3

Please sign in to comment.