Skip to content

Commit 0b2896a

Browse files
authored
Merge branch 'main' into feat/flat-tutorial-structure
2 parents 5630f6b + a4bbe78 commit 0b2896a

File tree

31 files changed

+311
-35
lines changed

31 files changed

+311
-35
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# [1.2.0](https://github.com/stackblitz/tutorialkit/compare/1.1.1...1.2.0) (2024-11-05)
2+
3+
4+
### Bug Fixes
5+
6+
* **react:** file tree scroll visibility ([#399](https://github.com/stackblitz/tutorialkit/issues/399)) ([e1e9160](https://github.com/stackblitz/tutorialkit/commit/e1e916044cc225dab925bd846d9208181f2080e1))
7+
8+
9+
### Features
10+
11+
* **runtime:** `fs.watch` to support syncing new files from webcontainer ([#394](https://github.com/stackblitz/tutorialkit/issues/394)) ([3beda90](https://github.com/stackblitz/tutorialkit/commit/3beda905df20ed9c7d286fc02007cf5b2e74835a))
12+
13+
14+
115
## [1.1.1](https://github.com/stackblitz/tutorialkit/compare/1.1.0...1.1.1) (2024-10-20)
216

317

docs/tutorialkit.dev/src/content/docs/guides/creating-content.mdx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,39 @@ const removed = 'This line was removed';
251251
const highlighted = 'This line is highlighted';
252252
```
253253

254+
#### Useful Expressive Code Attributes
255+
256+
- **`title`** - Sets the title of the code block.
257+
- **`frame`** - Defines the frame of the code block. Options: `"code"`, `"terminal"`, `"none"`, `"auto"`.
258+
- **`showLineNumbers`** - Displays line numbers. You can combine this with `startLineNumber=#` to begin numbering from a specific line.
259+
- **`wrap`** - Controls word wrapping. Use `preserveIndent` and `preserveIndent=false` to adjust indentation handling.
260+
261+
##### Marking Lines
262+
263+
- **`{x}`** - Marks specific lines. For example, `{6,10-18}` will mark lines 6 and 10 through 18.
264+
- **`ins`** - Marks inserted lines. Example: `ins={6,10-18}`.
265+
- **`del`** - Marks deleted lines. Example: `del={6,10-18}`.
266+
- **`{"Label":x}`** - Assigns a label to a line or range of lines. Works with `mark`, `ins`, and `del`. Example: `{"Label1":5} del={"Label2":7-8} ins={"Label3":10-12}`. If the label is long, consider placing an empty line in the code for better readability.
267+
- **`collapse={X-Y}`** - Collapses the specified lines. Example: `collapse={1-5,12-14}`.
268+
269+
##### Marking Text
270+
271+
You can highlight text using strings or regular expressions. For example:
272+
273+
````md
274+
```jsx "hello" /ye[sp]/ add=/add[12]/ del=/remove[12]/
275+
console.log(
276+
'Hello, the words yes and yep will be marked. Also add1, add2, remove1, remove2',
277+
)
278+
```
279+
````
280+
281+
```jsx "hello" /ye[sp]/ add=/add[12]/ del=/remove[12]/
282+
console.log(
283+
'Hello, the words yes and yep will be marked. Also add1, add2, remove1, remove2',
284+
)
285+
```
286+
254287
#### Importing files
255288

256289
In addition to Expressive Code features, you can import files from your code template `_files` and `_solution` folders using the file or solution shortcodes. These shortcodes insert the content of the specified file directly into your lesson content.

docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,8 @@ editor: # Editor is visible
221221
222222
##### `previews`
223223
Configure which ports should be used for the previews allowing you to align the behavior with your demo application's dev server setup. If not specified, the lowest port will be used.
224-
225-
<PropertyTable inherited type={'Preview[]'} />
224+
Optionally you can hide the preview by providing `previews: false`.
225+
<PropertyTable inherited type={'Preview[] | false'} />
226226

227227
The `Preview` type has the following shape:
228228

@@ -246,6 +246,9 @@ previews:
246246
- [3003, "Dev Server", "/docs"] # Preview is on :3003/docs/. Displayed title is "Dev Server".
247247
- { port: 3004, title: "Dev Server" } # Preview is on :3004/. Displayed title is "Dev Server".
248248
- { port: 3005, title: "Dev Server", pathname: "/docs" } # Preview is on :3005/docs/. Displayed title is "Dev Server".
249+
250+
previews: false # Do not show preview panel
251+
249252
```
250253

251254
##### `mainCommand`
@@ -281,13 +284,15 @@ An example use case is when a user runs a command that modifies a file. For inst
281284

282285
This property is by default set to `false` as it can impact performance. If you are creating a lesson where the user is expected to modify files outside the editor, you may want to keep this to `false`.
283286

287+
If you would like files to be added or removed from the editor automatically, you need to specify an array of globs that will determine which folders and files to watch for changes.
288+
284289
<PropertyTable inherited type={'FileSystem'} />
285290

286291
The `FileSystem` type has the following shape:
287292

288293
```ts
289294
type FileSystem = {
290-
watch: boolean
295+
watch: boolean | string[]
291296
}
292297
293298
```
@@ -296,10 +301,13 @@ Example values:
296301

297302
```yaml
298303
filesystem:
299-
watch: true # Filesystem changes are reflected in the editor
304+
watch: true # Filesystem changes to files already in the editor are reflected in the editor
300305
301306
filesystem:
302307
watch: false # Or if it's omitted, the default value is false
308+
309+
filesystem:
310+
watch: ['/*.json', '/src/**/*'] # Files changed, added or deleted that match one of the globs are updated in the editor
303311
```
304312

305313

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { webcontainer } from 'tutorialkit:core';
2+
3+
interface Props {
4+
filePath: string;
5+
newContent: string;
6+
7+
// default to 'webcontainer'
8+
access?: 'store' | 'webcontainer';
9+
testId?: string;
10+
}
11+
12+
export function ButtonDeleteFile({ filePath, access = 'webcontainer', testId = 'delete-file' }: Props) {
13+
async function deleteFile() {
14+
switch (access) {
15+
case 'webcontainer': {
16+
const webcontainerInstance = await webcontainer;
17+
18+
await webcontainerInstance.fs.rm(filePath);
19+
20+
return;
21+
}
22+
case 'store': {
23+
throw new Error('Delete from store not implemented');
24+
return;
25+
}
26+
}
27+
}
28+
29+
return (
30+
<button data-testid={testId} onClick={deleteFile}>
31+
Delete File
32+
</button>
33+
);
34+
}

e2e/src/components/ButtonWriteToFile.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ export function ButtonWriteToFile({ filePath, newContent, access = 'store', test
1616
case 'webcontainer': {
1717
const webcontainerInstance = await webcontainer;
1818

19+
const folderPath = filePath.split('/').slice(0, -1).join('/');
20+
21+
if (folderPath) {
22+
await webcontainerInstance.fs.mkdir(folderPath, { recursive: true });
23+
}
24+
1925
await webcontainerInstance.fs.writeFile(filePath, newContent);
2026

2127
return;

e2e/src/content/tutorial/tests/filesystem/no-watch/content.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ import { ButtonWriteToFile } from '@components/ButtonWriteToFile';
99
# Watch filesystem test
1010

1111
<ButtonWriteToFile client:load access="webcontainer" filePath="/bar.txt" newContent='Something else' />
12+
<ButtonWriteToFile client:load access="webcontainer" filePath="/src/new.txt" newContent='New' testId='write-new-file' />
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Baz
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Initial content
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
type: lesson
3+
title: Watch Glob
4+
focus: /bar.txt
5+
filesystem:
6+
watch: ['/*.txt', '/a/**/*', '/src/**/*']
7+
---
8+
9+
import { ButtonWriteToFile } from '@components/ButtonWriteToFile';
10+
import { ButtonDeleteFile } from '@components/ButtonDeleteFile';
11+
12+
# Watch filesystem test
13+
14+
<ButtonWriteToFile client:load access="webcontainer" filePath="/bar.txt" newContent='Something else' />
15+
<ButtonWriteToFile client:load access="webcontainer" filePath="/a/b/baz.txt" newContent='Foo' testId='write-to-file-in-subfolder' />
16+
<ButtonWriteToFile client:load access="webcontainer" filePath="/src/new.txt" newContent='New' testId='write-new-file' />
17+
<ButtonWriteToFile client:load access="webcontainer" filePath="/unknown/other.txt" newContent='Ignore this' testId='write-new-ignored-file' />
18+
19+
<ButtonDeleteFile client:load access="webcontainer" filePath="/bar.txt" testId='delete-file' />

e2e/src/content/tutorial/tests/filesystem/watch/content.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ filesystem:
77
---
88

99
import { ButtonWriteToFile } from '@components/ButtonWriteToFile';
10+
import { ButtonDeleteFile } from '@components/ButtonDeleteFile';
1011

1112
# Watch filesystem test
1213

1314
<ButtonWriteToFile client:load access="webcontainer" filePath="/bar.txt" newContent='Something else' />
1415
<ButtonWriteToFile client:load access="webcontainer" filePath="/a/b/baz.txt" newContent='Foo' testId='write-to-file-in-subfolder' />
16+
<ButtonWriteToFile client:load access="webcontainer" filePath="/unknown/other.txt" newContent='Ignore this' testId='write-new-ignored-file' />
17+
18+
<ButtonDeleteFile client:load access="webcontainer" filePath="/bar.txt" testId='delete-file' />

e2e/test/filesystem.test.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@ test('editor should reflect changes made from webcontainer', async ({ page }) =>
1717
});
1818
});
1919

20-
test('editor should reflect changes made from webcontainer in file in nested folder', async ({ page }) => {
20+
test('editor should reflect changes made from webcontainer in file in nested folder and not add new files', async ({ page }) => {
2121
const testCase = 'watch';
2222
await page.goto(`${BASE_URL}/${testCase}`);
2323

24+
// set up actions that shouldn't do anything
25+
await page.getByTestId('write-new-ignored-file').click();
26+
await page.getByTestId('delete-file').click();
27+
2428
await page.getByRole('button', { name: 'baz.txt' }).click();
2529

2630
await expect(page.getByRole('textbox', { name: 'Editor' })).toHaveText('Baz', {
@@ -32,6 +36,54 @@ test('editor should reflect changes made from webcontainer in file in nested fol
3236
await expect(page.getByRole('textbox', { name: 'Editor' })).toHaveText('Foo', {
3337
useInnerText: true,
3438
});
39+
40+
// test that ignored actions are ignored
41+
await expect(page.getByRole('button', { name: 'other.txt' })).not.toBeVisible();
42+
await expect(page.getByRole('button', { name: 'bar.txt' })).toBeVisible();
43+
});
44+
45+
test('editor should reflect changes made from webcontainer in specified paths', async ({ page }) => {
46+
const testCase = 'watch-glob';
47+
await page.goto(`${BASE_URL}/${testCase}`);
48+
49+
await expect(page.getByRole('textbox', { name: 'Editor' })).toHaveText('Initial content\n', {
50+
useInnerText: true,
51+
});
52+
53+
await page.getByTestId('write-to-file').click();
54+
55+
await expect(page.getByRole('textbox', { name: 'Editor' })).toHaveText('Something else', {
56+
useInnerText: true,
57+
});
58+
});
59+
60+
test('editor should reflect new files added in specified paths in webcontainer', async ({ page }) => {
61+
const testCase = 'watch-glob';
62+
await page.goto(`${BASE_URL}/${testCase}`);
63+
64+
await page.getByTestId('write-new-ignored-file').click();
65+
await page.getByTestId('write-new-file').click();
66+
67+
await page.getByRole('button', { name: 'new.txt' }).click();
68+
await expect(async () => {
69+
await expect(page.getByRole('button', { name: 'unknown' })).not.toBeVisible();
70+
await expect(page.getByRole('button', { name: 'other.txt' })).not.toBeVisible();
71+
}).toPass();
72+
73+
await expect(page.getByRole('textbox', { name: 'Editor' })).toHaveText('New', {
74+
useInnerText: true,
75+
});
76+
});
77+
78+
test('editor should remove deleted files in specified paths in webcontainer', async ({ page }) => {
79+
const testCase = 'watch-glob';
80+
await page.goto(`${BASE_URL}/${testCase}`);
81+
82+
await page.getByTestId('delete-file').click();
83+
84+
await expect(async () => {
85+
await expect(page.getByRole('button', { name: 'bar.txt' })).not.toBeVisible();
86+
}).toPass();
3587
});
3688

3789
test('editor should not reflect changes made from webcontainer if watch is not set', async ({ page }) => {

packages/astro/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# [1.2.0](https://github.com/stackblitz/tutorialkit/compare/1.1.1...1.2.0) "@tutorialkit/astro" (2024-11-05)
2+
3+
4+
15
## [1.1.1](https://github.com/stackblitz/tutorialkit/compare/1.1.0...1.1.1) "@tutorialkit/astro" (2024-10-20)
26

37

packages/astro/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tutorialkit/astro",
3-
"version": "1.1.1",
3+
"version": "1.2.0",
44
"description": "TutorialKit integration for Astro (https://astro.build)",
55
"author": "StackBlitz Inc.",
66
"type": "module",

packages/cli/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# [1.2.0](https://github.com/stackblitz/tutorialkit/compare/1.1.1...1.2.0) "@tutorialkit/cli" (2024-11-05)
2+
3+
4+
15
## [1.1.1](https://github.com/stackblitz/tutorialkit/compare/1.1.0...1.1.1) "@tutorialkit/cli" (2024-10-20)
26

37

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tutorialkit/cli",
3-
"version": "1.1.1",
3+
"version": "1.2.0",
44
"description": "Interactive tutorials powered by WebContainer API",
55
"author": "StackBlitz Inc.",
66
"type": "module",

packages/create-tutorial/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## [0.0.3](https://github.com/stackblitz/tutorialkit/compare/1.1.1...0.0.3) "create-tutorial" (2024-11-05)
2+
3+
4+
15
## [0.0.3](https://github.com/stackblitz/tutorialkit/compare/1.1.0...0.0.3) "create-tutorial" (2024-10-20)
26

37

packages/react/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# [1.2.0](https://github.com/stackblitz/tutorialkit/compare/1.1.1...1.2.0) "@tutorialkit/react" (2024-11-05)
2+
3+
4+
### Bug Fixes
5+
6+
* **react:** file tree scroll visibility ([#399](https://github.com/stackblitz/tutorialkit/issues/399)) ([e1e9160](https://github.com/stackblitz/tutorialkit/commit/e1e916044cc225dab925bd846d9208181f2080e1))
7+
8+
9+
110
## [1.1.1](https://github.com/stackblitz/tutorialkit/compare/1.1.0...1.1.1) "@tutorialkit/react" (2024-10-20)
211

312

packages/react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tutorialkit/react",
3-
"version": "1.1.1",
3+
"version": "1.2.0",
44
"description": "TutorialKit's React components and utilities",
55
"author": "StackBlitz Inc.",
66
"type": "module",

packages/react/src/Panels/EditorPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export function EditorPanel({
7979
</div>
8080
</div>
8181
<FileTree
82-
className="flex-grow py-2 border-r border-tk-elements-app-borderColor text-sm"
82+
className="flex flex-col flex-grow py-2 border-r border-tk-elements-app-borderColor text-sm overflow-y-auto overflow-x-hidden"
8383
i18n={i18n}
8484
selectedFile={selectedFile}
8585
hideRoot={hideRoot ?? true}

packages/react/src/core/FileTree.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export function FileTree({
125125
directory=""
126126
onFileChange={onFileChange}
127127
allowEditPatterns={allowEditPatterns}
128-
triggerProps={{ className: 'h-full', 'data-testid': 'file-tree-root-context-menu' }}
128+
triggerProps={{ className: 'h-full min-h-4', 'data-testid': 'file-tree-root-context-menu' }}
129129
/>
130130
</div>
131131
);

packages/runtime/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# [1.2.0](https://github.com/stackblitz/tutorialkit/compare/1.1.1...1.2.0) "@tutorialkit/runtime" (2024-11-05)
2+
3+
4+
### Features
5+
6+
* **runtime:** `fs.watch` to support syncing new files from webcontainer ([#394](https://github.com/stackblitz/tutorialkit/issues/394)) ([3beda90](https://github.com/stackblitz/tutorialkit/commit/3beda905df20ed9c7d286fc02007cf5b2e74835a))
7+
8+
9+
110
## [1.1.1](https://github.com/stackblitz/tutorialkit/compare/1.1.0...1.1.1) "@tutorialkit/runtime" (2024-10-20)
211

312

packages/runtime/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tutorialkit/runtime",
3-
"version": "1.1.1",
3+
"version": "1.2.0",
44
"description": "TutorialKit runtime",
55
"author": "StackBlitz Inc.",
66
"type": "module",
@@ -35,9 +35,11 @@
3535
"dependencies": {
3636
"@tutorialkit/types": "workspace:*",
3737
"@webcontainer/api": "1.2.4",
38-
"nanostores": "^0.10.3"
38+
"nanostores": "^0.10.3",
39+
"picomatch": "^4.0.2"
3940
},
4041
"devDependencies": {
42+
"@types/picomatch": "^3.0.1",
4143
"typescript": "^5.4.5",
4244
"vite": "^5.3.1",
4345
"vite-tsconfig-paths": "^4.3.2",

0 commit comments

Comments
 (0)