@tutorialkit-rb/runtime 0.1.5 → 0.1.6

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.
@@ -71,6 +71,11 @@ export declare class TutorialStore {
71
71
  * @internal
72
72
  */
73
73
  get steps(): import("nanostores").WritableAtom<import("../webcontainer/steps.js").Steps | undefined>;
74
+ /**
75
+ * Whether the environment is being prepared (prepare commands like npm install are running).
76
+ * Use this to block terminal input until the environment is ready.
77
+ */
78
+ get isPreparing(): ReadableAtom<boolean>;
74
79
  /** Check if file tree is visible */
75
80
  hasFileTree(): boolean;
76
81
  /** Check if editor is visible */
@@ -187,6 +187,13 @@ export class TutorialStore {
187
187
  get steps() {
188
188
  return this._stepController.steps;
189
189
  }
190
+ /**
191
+ * Whether the environment is being prepared (prepare commands like npm install are running).
192
+ * Use this to block terminal input until the environment is ready.
193
+ */
194
+ get isPreparing() {
195
+ return this._stepController.isPreparing;
196
+ }
190
197
  /** Check if file tree is visible */
191
198
  hasFileTree() {
192
199
  if (!this._lesson) {
@@ -58,6 +58,8 @@ export class TutorialRunner {
58
58
  setCommands(commands) {
59
59
  const newCommands = new Commands(commands);
60
60
  const anyChange = this._changeDetection(commands);
61
+ // update the blocking count (this affects terminal input blocking)
62
+ this._stepController.setBlockingCount(commands.terminalBlockingPrepareCommandsCount);
61
63
  // if we already know that there's a change we can update the steps now
62
64
  if (anyChange) {
63
65
  this._stepController.setFromCommands(Array.from(newCommands));
@@ -187,6 +189,8 @@ export class TutorialRunner {
187
189
  this._currentTemplate = { ...template };
188
190
  this._currentFiles = { ...files };
189
191
  this._updateDirtyState({ ...template, ...files });
192
+ // set up watcher early so file events are captured during prepareCommands
193
+ this._setupWatcher(webcontainer);
190
194
  }, { ignoreCancel: true, signal });
191
195
  return this._currentLoadTask.promise;
192
196
  }
@@ -509,16 +513,10 @@ export class TutorialRunner {
509
513
  }
510
514
  }
511
515
  if (!this._editorStore.documents.get()[filePath]) {
512
- const isDirectory = await _isDirectory(webcontainer, filePath);
513
- if (isDirectory) {
514
- this._editorStore.addFileOrFolder({ path: filePath, type: 'folder' });
515
- }
516
- else {
517
- this._editorStore.addFileOrFolder({ path: filePath, type: 'file' });
518
- this._updateCurrentFiles({ [filePath]: '' });
519
- scheduleReadFor(filePath, 'utf-8');
520
- }
516
+ this._editorStore.addFileOrFolder({ path: filePath, type: 'file' });
521
517
  }
518
+ this._updateCurrentFiles({ [filePath]: '' });
519
+ scheduleReadFor(filePath, 'utf-8');
522
520
  }
523
521
  }
524
522
  });
@@ -9,6 +9,21 @@ export declare class StepsController {
9
9
  * Steps that the runner is or will be executing.
10
10
  */
11
11
  steps: import("nanostores").WritableAtom<Steps | undefined>;
12
+ /**
13
+ * Number of prepare commands that should block terminal input.
14
+ * If undefined, terminal is not blocked during prepare commands.
15
+ */
16
+ private _blockingCount;
17
+ /**
18
+ * Whether the environment is being prepared (blocking prepare commands are running).
19
+ * This is true when any of the first N prepare commands (where N = terminalBlockingPrepareCommandsCount)
20
+ * has status 'idle' or 'running'. Used to block terminal input during npm install and similar setup commands.
21
+ */
22
+ isPreparing: import("nanostores").ReadableAtom<boolean>;
23
+ /**
24
+ * Set the number of prepare commands that should block terminal input.
25
+ */
26
+ setBlockingCount(count: number | undefined): void;
12
27
  setFromCommands(commands: Command[]): void;
13
28
  updateStep(index: number, step: Step): void;
14
29
  skipRemaining(index: number): void;
@@ -1,9 +1,38 @@
1
- import { atom } from 'nanostores';
1
+ import { atom, computed } from 'nanostores';
2
2
  export class StepsController {
3
3
  /**
4
4
  * Steps that the runner is or will be executing.
5
5
  */
6
6
  steps = atom(undefined);
7
+ /**
8
+ * Number of prepare commands that should block terminal input.
9
+ * If undefined, terminal is not blocked during prepare commands.
10
+ */
11
+ _blockingCount = atom(undefined);
12
+ /**
13
+ * Whether the environment is being prepared (blocking prepare commands are running).
14
+ * This is true when any of the first N prepare commands (where N = terminalBlockingPrepareCommandsCount)
15
+ * has status 'idle' or 'running'. Used to block terminal input during npm install and similar setup commands.
16
+ */
17
+ isPreparing = computed([this.steps, this._blockingCount], (steps, blockingCount) => {
18
+ // if no blocking count is set, terminal is never blocked
19
+ if (blockingCount === undefined || blockingCount === 0) {
20
+ return false;
21
+ }
22
+ if (!steps || steps.length === 0) {
23
+ return false;
24
+ }
25
+ // only check the first N steps where N is the blocking count
26
+ const blockingSteps = steps.slice(0, blockingCount);
27
+ // environment is preparing if any blocking step is idle or running
28
+ return blockingSteps.some((step) => step.status === 'idle' || step.status === 'running');
29
+ });
30
+ /**
31
+ * Set the number of prepare commands that should block terminal input.
32
+ */
33
+ setBlockingCount(count) {
34
+ this._blockingCount.set(count);
35
+ }
7
36
  setFromCommands(commands) {
8
37
  if (commands.length > 0) {
9
38
  this.steps.set(commands.map((command) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tutorialkit-rb/runtime",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "TutorialKit runtime",
5
5
  "author": "StackBlitz Inc.",
6
6
  "type": "module",
@@ -31,7 +31,7 @@
31
31
  "@webcontainer/api": "1.5.1",
32
32
  "nanostores": "^0.10.3",
33
33
  "picomatch": "^4.0.2",
34
- "@tutorialkit-rb/types": "0.1.5"
34
+ "@tutorialkit-rb/types": "0.1.6"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/picomatch": "^3.0.1",