Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Documentation for SplashKit Online #51

Merged
merged 4 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,40 @@ export default defineConfig({
directory: "products/splashkit/documentation/expansions",
},
},
{
label: "SplashKit Online",
autogenerate: {
directory: "products/splashkit/documentation/splashkit-online",
},
items: [
{
label: "Code Documentation",
autogenerate: {
directory: "products/splashkit/documentation/splashkit-online/code-documentation",
},
items: [
{
label: "Classes",
autogenerate: {
directory: "products/splashkit/documentation/splashkit-online/code-documentation/classes",
},
},
{
label: "Processes",
autogenerate: {
directory: "products/splashkit/documentation/splashkit-online/code-documentation/processes",
},
},
{
label: "Other",
autogenerate: {
directory: "products/splashkit/documentation/splashkit-online/code-documentation/other",
},
},
]
},
]
},
],
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
title: ExecutionEnvironment - Code Documentation
description: An explanation of what ExecutionEnvironment is, its methods, members, and events.
---

[_executionEnvironment.js_](https://github.com/thoth-tech/SplashkitOnline/blob/main/Browser_IDE/executionEnvironment.js)

ExecutionEnvironment is a class designed to abstract out running the user's code, and also handle
the environment itself (such as resetting variables, preloading files, etc). It contains functions
to 'compile' user code, run the main program, reset itself, and create directories/files inside the
environment.

The actual implementation can be found inside `executionEnvironment.js`. Upon creation, it creates
an iFrame (which can be thought of as a page inside the page) - and this is where all the user's
code will be run.

## Why create an iFrame?

The iFrame it creates is sandboxed so that it cannot access anything inside the main page. This is
important, since while we can likely trust code the user writes themselves, we cannot trust code
they may receive from other people. If we ran the code the user writes directly inside the main
page, it could access and manipulate the IDE itself, along with accessing cookies and other things
it shouldn't have access to. By running it inside the iFrame, we can be sure it can't access
anything it shouldn't.

It also makes it clear which files are part of the project (since those exist outside the iFrame),
and which parts are only transient, such as logs (that only exist inside the iFrame and are
destroyed on reloads). It means user code can not permanently overwrite resources.

Additionally, it gives us a way to completely reset the environment the code is running in, as we
can destroy and recreate the iFrame without having to reload the main page itself.

To communicate with the iFrame, we can only send and receive messages, which also limits the number
of potential escape routes from the iFrame.

## Members

- `hasRunOnce` - has the program been run yet? Is reset with `resetEnvironment()`
- `executionStatus` - current status of the program, can be:
- `ExecutionStatus.Unstarted`
- `ExecutionStatus.Running`
- `ExecutionStatus.Paused`

## Methods

- `constructor(container)` - takes a container element to load the iFrame inside.

### Initializing user's code

- `runCodeBlock(block, source)` - takes a code block (which has the block name `block`, and the
source code `source`, syntax checks it, and if it passes, sends the code to the iFrame via a
message.
- `runCodeBlocks(blocks)` - takes an array of dictionaries with the keys {name, code}, and calls
`runCodeBlock` for each one.

### Running user's code

- `runProgram()` - sends a message to the iFrame to run the user's `main` (if it exists).
- `pauseProgram()` - sends a message to pause the user's program - returns a `promise`, that
resolves once the program pauses, or fails after 2 seconds.
- `continueProgram()` - sends a message to continue the user's program (if it has been paused)
- `stopProgram()` - sends a message to stop the user's program completely - returns a `promise`,
that resolves once the program stops, or fails after 2 seconds.

### Handling the environment

- `resetEnvironment()` - completely resets the environment, by destroying and recreating the iFrame.
All files inside the environment will also be lost.
- `cleanEnvironment()` - Does a 'best-efforts' attempt to tidy the environment, such as removing
user created global variables. Much faster than `resetEnvironment()`, and does not reset the file
system.

### Filesystem

- `mkdir(path)` - sends a message to create a directory at `path`
- `writeFile(path, data)` - sends a message to write `data` to a file `path`, creating it if it does
not exist

## Events

The events can be listened to by attaching with `addEventListener(event, callback)`

- `initialized` - the ExecutionEnvironment is setup and ready to execute code.
- `error` - an error has occurred in user code. Members:
- `message` - the error message
- `line` - the line number of the error
- `block` - the name of the code block the error occurred in.
- `programStarted` - the program has started running
- `programStopped` - the program has stopped running
- `programPaused` - the program has paused
- `programContinued` - the program has resumed running
- `onMovePath` - A file or directory has been moved. Members:
- `oldPath` - the original path
- `newPath` - the path it was moved to
- `onMakeDirectory` - A directory has been made. Members:
- `path` - the path to the new directory
- `onDeletePath` - A file or directory has been deleted. Members: - `path` - the path to the
file/directory
- `onOpenFile` - A file has been opened, possibly for reading or writing. Members:
- `path` - the path to the file
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
title: IDBStoredProject - Code Documentation
description: An explanation of what IDBStoredProject is, its methods, members, and events.
---

[_IDBStoredProject.js_](https://github.com/thoth-tech/SplashkitOnline/blob/main/Browser_IDE/IDBStoredProject.js)

IDBStoredProject is a class that handles saving/loading the user's project within the browser
itself. It uses
[IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB) storage,
which allows it to store large amounts of data in a simplified database structure.

It stores a single project inside a single database, creating a new one for each project. It has
functions to read and write files to a virtual filesystem saved inside the database (for storing
user code, and uploaded resources like sprites and sounds). It also has an area for config, and
keeps track of its lastWriteTime inside there.

## Database layout

There are two tables:

- `project` - contains information about the project, such as the last write time. Simple key-value,
with the key's name being 'category'.
- `files` - stores all the user's files and directories. Each entry contains the following:
- `nodeID` - a numerical identifier for the node (file/directory), automatically increments.
- `name` - name of the file/directory
- `type` - either `"FILE"` or `"DIR"` - file or directory
- `data` - the file's contents - a binary blob of data. Or `null` if it's a directory.
- `parent` - the `nodeID` of the parent of the file/directory (what directory is it inside). -1
means it is inside the root directory.

## Members

- `initializer` - a function that can be called to initialize the database - performs the equivalent
of `skm new`
- `projectName` - the name of the project (and therefore database) it is currently attached to.
`null` if detached.
- `lastKnownWriteTime` - the last time the project was written to within this tab.

## Methods

- `constructor(initializer)` - takes an initializer function, used when initializing a project's
database for the first time.
- `attachToProject(storeName)` - attaches it to a project with the name `storeName`. Initializes the
database, and emits an `attached` event.
- `detachFromProject()` - detaches itself from the project, resets its internal state and emits a
`detached` event.
- `deleteProject(storeName)` - deletes the project named `storeName`, and returns a promise which
resolves once the database is truly deleted.
- `checkForWriteConflicts()` - checks the `lastKnownWriteTime` against the actual `lastWriteTime`
inside the database - if they conflict in a way that suggests another tab has written to the
database, throws a `timeConflict` event.
- `access(func)` - a bit of a special function. This function is the only entry point to
reading/writing to the IDBStoredProject. It takes a function, which it will call, passing in a new
object (internally a `__IDBStoredProjectRW`), which has many more methods for reading/writing.
This is done, so that the opening/closing of the database can be wrapped around the user function,
without them having to handle it manually (and potentially leave open connections causing issues
later on). Here's an example of usage:

```javascript
let storedProject = new StoredProject(...)
...
// we get passed a new object, which we called "project", and can use it to get the lastWriteTime.
// this is all performed asynchronously, so we need to "await" it to get the result
let storedTime = await storedProject.access((project)=>project.getLastWriteTime());
// in non-lambda syntax
let storedTime = await storedProject.access(function(project){ return project.getLastWriteTime()});
```

**The following functions are ones accessible from inside the callback to `access` only**

- `getLastWriteTime()` - get the last write time.
- `updateLastWriteTime(time = null)` - set the last write time - defaults to the current time
(stored in unix time)
- `mkdir(path)` - make a directory at path, does nothing if it already exists. Emits
`onMakeDirectory` event.
- `writeFile(path, data)` - overwrites the data inside the file at `path` with `data` - creates the
file if it doesn't exist. Emits `onOpenFile` event. Also emits `onWriteToFile` event.
- `rename(oldPath, newPath)` - moves a file/directory to a new path and/or name. Emits `onMovePath`
event.
- `readFile(path)` - reads a file at `path` and returns the data inside. Returns `null` if the file
doesn't exist.
- `getFileTree()` - returns a complete tree of the file system, in a structure digestible by the
`TreeView`.

## Events

The events can be listened to by attaching with `addEventListener(event, callback)`

- `attached` - Is attached and can be used.
- `detached` - Has been detached.
- `onMovePath` - A file or directory has been moved. Members:
- `oldPath` - the original path
- `newPath` - the path it was moved to
- `onMakeDirectory` - A directory has been made. Members:
- `path` - the path to the new directory
- `onDeletePath` - A file or directory has been deleted. Members:
- `path` - the path to the file/directory
- `onOpenFile` - A file has been opened, possibly for reading or writing. Members:
- `path` - the path to the file
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
title: TreeView - Code Documentation
description: An explanation of what TreeView is, its methods, members, and events.
---

[_treeview.js_](https://github.com/thoth-tech/SplashkitOnline/blob/main/Browser_IDE/treeview.js)

TreeView is a class used for displaying and updating a tree view, designed specifically around
file/directory manipulation. It allows viewing multiple filesystems at once in an overlapping
fashion (important since we have the files in the user's project that will be saved/loaded, and also
the live files inside the ExecutionEnvironment, which may be different). It allows files/folders to
be dragged around and organized, and folders to have a button on the side for uploading new files.

The way it is intended to be used, is to make it listen to events from the target filesystems (such
as file moves/deletes), and update itself accordingly. When it is interacted with by the user, it
will emit its own events - these events should be listened to, and the target filesystem updated
accordingly. It should then look like this :

1. A file is created in the target filesystem and an event is emitted
2. The TreeView reacts to this event and creates a node in its tree with the same name.
3. The user now drags that node to inside another node (directory), and the TreeView emits an event.
Note that it does _not_ change itself here. The node inside the tree has not actually moved yet.
4. A function is called back from this event, that then tells the target filesystem to move the
file.
5. The target filesystem moves the file, and an event is emitted.
6. The TreeView reacts to this event, and moves the node to inside the directory.

See how the TreeView never updates itself - it relies on an event coming _back_ from the target
filesystem. This means that if the target filesystem fails to do the operation for whatever reason,
the TreeView also remains in the same state, meaning the two remain synchronized effectively.

See example usage of it inside `fileview.js`
([here](https://github.com/thoth-tech/SplashkitOnline/blob/main/Browser_IDE/fileview.js)), where it
is attached to both the `IDBStoredProject` filesystem, and also the filesystem inside the
`ExecutionEnvironment`.

### Limitations

Currently there is no way to delete files/folders, or rename files/folders in the interface itself.
This shouldn't be hard to add, however.

## Members

None publicly available.

## Methods

- `constructor(container, FSes)` - takes a container to place the TreeView's elements into, and a
list of FSes, which are the filesystems it will support. An example list looks like this
`{"persistent":"node-persistent", "transient":"node-transient"}`, key-value pairs where the key is
the filesystems name, and the value is a css style to apply to nodes inside this filesystem.
- `moveNode(oldPath, newPath, index = -1, FS)` - moves a node to a new path and/or name. Allows one
to set the index the node will appear at, and also which filesystem(s) (a list) the move occurred
in.
- `deleteNode(path, FS)` - deletes a node from a set of filesystem(s) (a list)
- `addDirectory(path, FS)` - make a directory at path, does nothing if it already exists. Allows one
to set which filesystem(s) (a list) the directory add occurred in.
- `addFile(path, data)` - make a file at path, does nothing if it already exists. Allows one to set
which filesystem(s) (a list) the file was added in.
- `reset(path)` - Deletes all nodes.
- `populatefileView(files, FS)` - Populates the tree with a list of files in a particular structure
(the same one `IDBStoredProject.getFileTree()` returns). Allows one to set which filesystem(s) (a
list) the directory add occurred in.

## Events

The events can be listened to by attaching with `addEventListener(event, callback)`

- `nodeMoveRequest` - A file or directory has been moved. Members:
- `treeView` - the TreeView object
- `oldPath` - the original path
- `newPath` - the path it was moved to
- `FS` - the filesystem(s) the change occurred in.
- `accept` - a function that can be called to announce that the change was successful -
**currently unused**.
- `folderUploadRequest` - The 'add file' button was clicked on on a directory. Members:
- `treeView` - the TreeView object
- `path` - path to the directory
- `FS` - the filesystem(s) the directory exists in.
- `nodeDoubleClick` - A file node has been double clicked. Members:
- `treeView` - the TreeView object
- `path` - path to the file
- `FS` - the filesystem(s) the directory exists in.
Loading
Loading