+ {{
+ $t({
+ en: `Animation ${animation.name} will be removed. Do you want to preserve the costumes?`,
+ zh: `动画 ${animation.name} 将被移除。是否保留其中的造型?`
+ })
+ }}
+
+
+ {{
+ $t({
+ en: "Preserve (the costumes will be moved to the sprite's costume list)",
+ zh: '保留(造型将被移动到精灵的造型列表中)'
+ })
+ }}
+
+
+
+
+
diff --git a/spx-gui/src/components/editor/sprite/costume-bg.svg b/spx-gui/src/components/editor/sprite/costume-bg.svg
deleted file mode 100644
index fede36619..000000000
--- a/spx-gui/src/components/editor/sprite/costume-bg.svg
+++ /dev/null
@@ -1,88 +0,0 @@
-
diff --git a/spx-gui/src/components/ui/icons/UIIcon.vue b/spx-gui/src/components/ui/icons/UIIcon.vue
index 09bd23fb3..cebdec4f9 100644
--- a/spx-gui/src/components/ui/icons/UIIcon.vue
+++ b/spx-gui/src/components/ui/icons/UIIcon.vue
@@ -32,6 +32,8 @@ import clock from './clock.svg?raw'
import timer from './timer.svg?raw'
import status from './status.svg?raw'
import sound from './sound.svg?raw'
+import volumeUp from './volume-up.svg?raw'
+import volumeOff from './volume-off.svg?raw'
const typeIconMap = {
file,
@@ -60,7 +62,9 @@ const typeIconMap = {
clock,
timer,
status,
- sound
+ sound,
+ volumeUp,
+ volumeOff
}
export type Type = keyof typeof typeIconMap
diff --git a/spx-gui/src/components/editor/sound/icons/volume-down.svg b/spx-gui/src/components/ui/icons/volume-off.svg
similarity index 91%
rename from spx-gui/src/components/editor/sound/icons/volume-down.svg
rename to spx-gui/src/components/ui/icons/volume-off.svg
index d48b7768a..788b7010f 100644
--- a/spx-gui/src/components/editor/sound/icons/volume-down.svg
+++ b/spx-gui/src/components/ui/icons/volume-off.svg
@@ -1,4 +1,4 @@
\ No newline at end of file
diff --git a/spx-gui/src/components/editor/sound/icons/volume-up.svg b/spx-gui/src/components/ui/icons/volume-up.svg
similarity index 98%
rename from spx-gui/src/components/editor/sound/icons/volume-up.svg
rename to spx-gui/src/components/ui/icons/volume-up.svg
index 703dc91a8..d8763ac33 100644
--- a/spx-gui/src/components/editor/sound/icons/volume-up.svg
+++ b/spx-gui/src/components/ui/icons/volume-up.svg
@@ -1,4 +1,4 @@
\ No newline at end of file
diff --git a/spx-gui/src/models/animation.ts b/spx-gui/src/models/animation.ts
index 958f136e0..07174ba1d 100644
--- a/spx-gui/src/models/animation.ts
+++ b/spx-gui/src/models/animation.ts
@@ -76,8 +76,8 @@ export class Animation {
for (const costume of costumes) {
let costumeName = this.stripCostumeNamePrefix(costume.name)
costumeName = ensureValidCostumeName(costumeName, this)
- costume.setName(costumeName)
costume.setParent(this)
+ costume.setName(costumeName)
}
this.costumes = costumes
if (this.duration === 0) {
@@ -111,7 +111,7 @@ export class Animation {
constructor(name: string, inits?: AnimationInits) {
this.name = name
this.costumes = []
- this.duration = inits?.duration ?? 0
+ this.duration = inits?.duration ?? 5
this.sound = inits?.onStart?.play ?? null
for (const field of ['fps', 'isLoop', 'onPlay'] as const) {
@@ -126,9 +126,6 @@ export class Animation {
* Note that the "default" means default behavior for builder, not the default behavior of spx
*/
static create(nameBase: string, sprite: Sprite, costumes: Costume[], inits?: AnimationInits) {
- for (const costume of costumes) {
- sprite.removeCostume(costume.name)
- }
const animation = new Animation(getAnimationName(null, nameBase), inits)
animation.setCostumes(costumes)
return animation
diff --git a/spx-gui/src/models/costume.ts b/spx-gui/src/models/costume.ts
index 368c917c5..2fbbfd377 100644
--- a/spx-gui/src/models/costume.ts
+++ b/spx-gui/src/models/costume.ts
@@ -89,6 +89,15 @@ export class Costume {
return reactive(this) as this
}
+ clone() {
+ return new Costume(this.name, this.img, {
+ x: this.x,
+ y: this.y,
+ faceRight: this.faceRight,
+ bitmapResolution: this.bitmapResolution
+ })
+ }
+
/**
* Create instance with default inits
* Note that the "default" means default behavior for builder, not the default behavior of spx
From 00c76bbe327b141c351d53b9b67bf8938be60251 Mon Sep 17 00:00:00 2001
From: Hanxing Yang
Date: Thu, 4 Jul 2024 08:35:12 +0800
Subject: [PATCH 20/57] hide onTouched (#632)
---
.../editor/code-editor/code-text-editor/tools/index.ts | 2 +-
.../editor/code-editor/code-text-editor/tools/spx.ts | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/spx-gui/src/components/editor/code-editor/code-text-editor/tools/index.ts b/spx-gui/src/components/editor/code-editor/code-text-editor/tools/index.ts
index feba55b6f..7b7f2b105 100644
--- a/spx-gui/src/components/editor/code-editor/code-text-editor/tools/index.ts
+++ b/spx-gui/src/components/editor/code-editor/code-text-editor/tools/index.ts
@@ -25,7 +25,7 @@ export const eventCategory: ToolCategory = {
},
{
label: { en: 'Sensing Events', zh: '感知事件' },
- tools: [spx.onClick, spx.onKey, spx.onAnyKey, spx.onTouched]
+ tools: [spx.onClick, spx.onKey, spx.onAnyKey]
},
{
label: { en: 'Motion Events', zh: '运动事件' },
diff --git a/spx-gui/src/components/editor/code-editor/code-text-editor/tools/spx.ts b/spx-gui/src/components/editor/code-editor/code-text-editor/tools/spx.ts
index 22c125f6c..8c4234425 100644
--- a/spx-gui/src/components/editor/code-editor/code-text-editor/tools/spx.ts
+++ b/spx-gui/src/components/editor/code-editor/code-text-editor/tools/spx.ts
@@ -32,6 +32,8 @@ export const onCloned: Tool = {
}
}
+// For now `onTouched` is not exposed to the user
+// As it behaves strangely in the current implementation, see details in https://github.com/goplus/spx/issues/298
export const onTouched: Tool = {
type: ToolType.method,
callEffect: ToolCallEffect.listen,
@@ -54,7 +56,7 @@ export const onTouched: Tool = {
},
{
desc: { en: 'By some given sprites', zh: '指定的某些精灵' },
- sample: 'onTouched [S1, S2] => {}',
+ sample: 'onTouched [S1, S2], => {}',
insertText: 'onTouched [${1:}], => {\n\t${2}\n}'
}
]
From d534306ab08a2c4dd3c2fb2effb872e465627d45 Mon Sep 17 00:00:00 2001
From: Hanxing Yang
Date: Thu, 4 Jul 2024 08:58:21 +0800
Subject: [PATCH 21/57] Update code-editor for animation (#629)
* update code-editor for animation
* update costume tools
* fix costume add
---
.../asset/preprocessing/PreprocessModal.vue | 10 ++---
.../code-text-editor/tools/index.ts | 28 +++++++++++-
.../code-editor/code-text-editor/tools/spx.ts | 45 ++++++++++++++++---
3 files changed, 67 insertions(+), 16 deletions(-)
diff --git a/spx-gui/src/components/asset/preprocessing/PreprocessModal.vue b/spx-gui/src/components/asset/preprocessing/PreprocessModal.vue
index 8fa53df48..97f19b860 100644
--- a/spx-gui/src/components/asset/preprocessing/PreprocessModal.vue
+++ b/spx-gui/src/components/asset/preprocessing/PreprocessModal.vue
@@ -171,13 +171,11 @@ const selectedCostumes = shallowReactive([])
/** Update costumes based on current process output */
async function updateCostumes(files: File[]) {
- costumes.value = []
- selectedCostumes.splice(0)
const newCostumes = await Promise.all(
files.map((file) => Costume.create(stripExt(file.name), file))
)
costumes.value = newCostumes
- selectedCostumes.push(...newCostumes)
+ selectedCostumes.splice(0, selectedCostumes.length, ...newCostumes)
}
function isCostumeSelected(costume: Costume) {
@@ -196,10 +194,8 @@ function handleConfirm() {
watch(
() => props.files,
- async (files) => {
- cancelMethod(supportedMethods[0].value)
- await updateCostumes(files)
- },
+ // The first method cancelled, all methods cancelled
+ () => cancelMethod(supportedMethods[0].value),
{ immediate: true }
)
diff --git a/spx-gui/src/components/editor/code-editor/code-text-editor/tools/index.ts b/spx-gui/src/components/editor/code-editor/code-text-editor/tools/index.ts
index 7b7f2b105..83578ce0f 100644
--- a/spx-gui/src/components/editor/code-editor/code-text-editor/tools/index.ts
+++ b/spx-gui/src/components/editor/code-editor/code-text-editor/tools/index.ts
@@ -51,7 +51,7 @@ export const motionCategory: ToolCategory = {
groups: [
{
label: { en: 'Move', zh: '移动' },
- tools: [spx.move, spx.goto, spx.glide]
+ tools: [spx.step, spx.move, spx.goto, spx.glide]
},
{
label: { en: 'Position', zh: '位置' },
@@ -103,7 +103,12 @@ export const lookCategory: ToolCategory = {
},
{
label: { en: 'Costume', zh: '造型' },
- tools: [spx.costumeName, spx.costumeIndex, spx.setCostume, spx.nextCostume, spx.prevCostume]
+ // index-related tools are excluded, as they are not recommended to use (animation is prefered)
+ tools: [spx.costumeName, spx.setCostume]
+ },
+ {
+ label: { en: 'Animation', zh: '动画' },
+ tools: [spx.animate]
},
{
label: { en: 'Backdrop', zh: '背景' },
@@ -258,6 +263,25 @@ export function getVariableCategory(project: Project): ToolCategory {
})
if (project.selectedSprite != null) {
+ groups.push({
+ label: {
+ en: `Animations of "${project.selectedSprite.name}"`,
+ zh: `${project.selectedSprite.name} 的动画`
+ },
+ tools: project.selectedSprite.animations.map((animation) => {
+ const keyword = `"${animation.name}"`
+ return {
+ type: ToolType.variable,
+ target: ToolContext.sprite,
+ keyword,
+ desc: { en: `Animation "${animation.name}"`, zh: `动画 ${animation.name}` },
+ usage: {
+ sample: keyword,
+ insertText: keyword
+ }
+ }
+ })
+ })
groups.push({
label: {
en: `Costumes of "${project.selectedSprite.name}"`,
diff --git a/spx-gui/src/components/editor/code-editor/code-text-editor/tools/spx.ts b/spx-gui/src/components/editor/code-editor/code-text-editor/tools/spx.ts
index 8c4234425..2be10c91a 100644
--- a/spx-gui/src/components/editor/code-editor/code-text-editor/tools/spx.ts
+++ b/spx-gui/src/components/editor/code-editor/code-text-editor/tools/spx.ts
@@ -97,7 +97,10 @@ export const die: Tool = {
callEffect: ToolCallEffect.write,
target: ToolContext.sprite,
keyword: 'die',
- desc: { en: 'Let current sprite die', zh: '让当前精灵死亡' },
+ desc: {
+ en: 'Let current sprite die. Animation bound to state "die" will be played.',
+ zh: '让当前精灵死亡,绑定到“死亡”状态的动画会被播放'
+ },
usage: {
sample: 'die',
insertText: 'die'
@@ -170,12 +173,13 @@ export const setCostume: Tool = {
target: ToolContext.sprite,
keyword: 'setCostume',
desc: {
- en: 'Set the current costume by specifying name or index',
- zh: '通过指定名称或索引设置当前造型'
+ en: 'Set the current costume by specifying name',
+ zh: '通过指定名称设置当前造型'
},
+ // index-related usages are excluded, as they are not recommended to use (animation is prefered)
usage: {
- sample: 'setCostume 0',
- insertText: 'setCostume ${1:nameOrIndex}'
+ sample: 'setCostume "happy"',
+ insertText: 'setCostume ${1:name}'
}
}
@@ -203,6 +207,18 @@ export const prevCostume: Tool = {
}
}
+export const animate: Tool = {
+ type: ToolType.method,
+ callEffect: ToolCallEffect.write,
+ target: ToolContext.sprite,
+ keyword: 'animate',
+ desc: { en: 'Play animation with given name', zh: '通过指定名称播放动画' },
+ usage: {
+ sample: 'animate "jump"',
+ insertText: 'animate ${1:name}'
+ }
+}
+
export const say: Tool = {
type: ToolType.method,
callEffect: ToolCallEffect.write,
@@ -263,10 +279,25 @@ export const move: Tool = {
callEffect: ToolCallEffect.write,
target: ToolContext.sprite,
keyword: 'move',
- desc: { en: 'Move given steps, toward current heading', zh: '向当前朝向移动指定的步数' },
+ desc: { en: 'Move given distance, toward current heading', zh: '向当前朝向移动指定的距离' },
usage: {
sample: 'move 10',
- insertText: 'move ${1:steps}'
+ insertText: 'move ${1:distance}'
+ }
+}
+
+export const step: Tool = {
+ type: ToolType.method,
+ callEffect: ToolCallEffect.write,
+ target: ToolContext.sprite,
+ keyword: 'step',
+ desc: {
+ en: 'Step given distance, toward current heading. Animation bound to state "step" will be played',
+ zh: '向当前朝向行走指定的距离,绑定到“行走”状态的动画会被播放'
+ },
+ usage: {
+ sample: 'step 10',
+ insertText: 'step ${1:distance}'
}
}
From 13d851c6fa1e72d3700d0f44bda3f1ce76990c31 Mon Sep 17 00:00:00 2001
From: ComfyFluffy <24245520+ComfyFluffy@users.noreply.github.com>
Date: Thu, 4 Jul 2024 12:21:13 +0800
Subject: [PATCH 22/57] Preload frames & audio / removal modal (#633)
* feat: remove costume & preload frames
* fix: use ref for muted
---
.../editor/sprite/AnimationItem.vue | 16 ++--
.../editor/sprite/AnimationRemoveModal.vue | 34 ++++++--
.../sprite/animation/AnimationPlayer.vue | 81 +++++++++++--------
.../editor/sprite/animation/MuteSwitch.vue | 9 +--
4 files changed, 91 insertions(+), 49 deletions(-)
diff --git a/spx-gui/src/components/editor/sprite/AnimationItem.vue b/spx-gui/src/components/editor/sprite/AnimationItem.vue
index 2c32befbe..645d31506 100644
--- a/spx-gui/src/components/editor/sprite/AnimationItem.vue
+++ b/spx-gui/src/components/editor/sprite/AnimationItem.vue
@@ -13,7 +13,7 @@
From 6003a259f75a313b849cd8a6ce7227aa0327ee3a Mon Sep 17 00:00:00 2001
From: Hanxing Yang
Date: Thu, 4 Jul 2024 20:07:08 +0800
Subject: [PATCH 23/57] animation details (#634)
---
.../common/placeholder/EditorPlaceholder.vue | 37 +----
.../editor/sprite/AnimationEditor.vue | 52 +++---
.../editor/sprite/AnimationItem.vue | 10 +-
.../editor/sprite/AnimationRemoveModal.vue | 13 +-
.../components/editor/sprite/SpriteEditor.vue | 8 +-
.../sprite/animation/DurationEditor.vue | 11 +-
.../sprite/animation/sound/SoundEditor.vue | 11 +-
.../animation/state/BoundStateEditor.vue | 11 +-
.../src/components/editor/sprite/gallery.svg | 3 +
spx-gui/src/components/ui/empty/UIEmpty.vue | 148 +++++++++++-------
.../placeholder.svg => ui/empty/empty-xl.svg} | 2 +-
spx-gui/src/models/animation.test.ts | 82 ++++++++++
spx-gui/src/models/animation.ts | 58 ++++---
spx-gui/src/models/sprite.ts | 22 ++-
14 files changed, 308 insertions(+), 160 deletions(-)
create mode 100644 spx-gui/src/components/editor/sprite/gallery.svg
rename spx-gui/src/components/{editor/common/placeholder/placeholder.svg => ui/empty/empty-xl.svg} (99%)
create mode 100644 spx-gui/src/models/animation.test.ts
diff --git a/spx-gui/src/components/editor/common/placeholder/EditorPlaceholder.vue b/spx-gui/src/components/editor/common/placeholder/EditorPlaceholder.vue
index 58eae5384..59cc484c3 100644
--- a/spx-gui/src/components/editor/common/placeholder/EditorPlaceholder.vue
+++ b/spx-gui/src/components/editor/common/placeholder/EditorPlaceholder.vue
@@ -1,13 +1,11 @@
-
-
-
{{ $t({ en: 'Add a sprite to start', zh: '添加一个精灵' }) }}
-
+
+ {{ $t({ en: 'Add a sprite to start', zh: '添加一个精灵' }) }}
+
{{ $t({ en: 'Choose from asset library', zh: '从素材库选择' }) }}
-
-
+
+
-```
-
diff --git a/docs/contribution/contribution.md b/docs/contribution/contribution.md
deleted file mode 100644
index db2b66e9c..000000000
--- a/docs/contribution/contribution.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Contribution Guide
-
-## Contribution Process
-
-1. **Find or Create an Issue**: Before starting your work, please find or create an issue related to your contribution in the issues section. This helps the community track work and prevents multiple people from working on the same issue simultaneously.
-2. **Fork and Create Your Branch**: Fork the repository and create a new branch on your fork to implement your changes.
-3. **Implement Your Changes**: Make your changes on your branch. Ensure you follow the project's coding style and quality standards.
-4. **Submit a Pull Request (PR)**: After completing your changes, submit a PR to the main repository. Please provide a detailed description of your changes in the PR and reference the related issue.
-5. **Code Review**: Maintainers or community members may review your PR and suggest changes. Please work with reviewers to address any issues until your PR is accepted.
-
-## Project Structure
-
-- **`/spx-gui`**: Contains all front-end code files.
-- **`/spx-backend`**: Contains all back-end code files.
-- **`/docs`**: Project documentation, including design documents and user manuals.
-- **`/scripts`**: Scripts for building, deploying, or other automation tasks.
-- **`README.md`**: Project overview and usage guide.
-
-Please maintain clarity and consistency in the project structure when contributing.
-
-## Issue Tags and Labels
-
-We use GitHub's labeling system to categorize and manage issues and PRs. Here are some common labels and their meanings:
-
-- **`fix`**: Bug fixes.
-- **`feat`**: New features or enhancements.
-- **`docs`**: Documentation updates.
-- **`chore`**: Other changes that don't modify src or test files.
-- **`ci`**: Continuous Integration related changes.
-- **`test`**: Adds or updates tests.
-
-When creating an issue or submitting a PR, please use appropriate labels to help maintainers manage the project more effectively.
-
diff --git a/docs/develop/index.md b/docs/develop/index.md
new file mode 100644
index 000000000..a6de9e6da
--- /dev/null
+++ b/docs/develop/index.md
@@ -0,0 +1,13 @@
+# Develop Go+ Builder
+
+### Project Structure
+
+- **`/docs`**: Documentations for Go+ Builder.
+- **`/spx-gui`**: Front-end project.
+- **`/spx-backend`**: Back-end project.
+- **`/tools`**: Other independent tools that Go+ Builder depends on.
+- **`/scripts`**: Scripts for building, deploying, or other automation tasks.
+
+### Architecture design
+
+TODO.
diff --git a/docs/file-manager.md b/docs/file-manager.md
deleted file mode 100644
index e7375b2f6..000000000
--- a/docs/file-manager.md
+++ /dev/null
@@ -1,91 +0,0 @@
-# FileManager
-
-## Competitive Analysis
-
-| | File System API | JavaScript Tree |
-| :-------------------- | :----------------------------------------------------------- | ------------------------------------------------------------ |
-| **Local Disk Access** | Provides a standard API for interacting with the local file system, supporting reading and writing local files and directories, as well as accessing the disk, but requires user authorization. | Requires uploading directories through file compression, unable to read and write to the disk in real-time, and necessitates abstracting the directory structure. |
-| **Performance** | High | Depends on the implementation |
-| **Compatibility** | Good | Depends on the browser environment, may vary significantly across different browsers |
-| **Security** | File system operations are subject to browser permission controls and provide built-in security measures. | Has good security in the browser environment; manual implementation of security may require additional work. |
-| **Flexibility** | Can flexibly operate on files in the browser environment, avoiding direct handling of underlying operating system differences. | Has high customization capabilities, but may vary in different environments. |
-| **docs** | [File System API - Web APIs](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API) | - |
-
-## Final Selection: Using JavaScript Tree with State Management Tools
-
-Considering specific use cases, we do not advise using the local file read/write disk method. Therefore, we choose to manage files using an abstract JavaScript tree. To better manage this file structure and allow everyone to access corresponding functions, we use state management tools corresponding to mainstream frameworks, such as Vue’s `pinia` or React’s `Redux`.
-
-Below is a type restriction for file management. In this structure, `name` will serve as the file name, `code` will generate a corresponding code file with the `.spx` extension, `file` will save related files (such as audio or images), and `config` represents the configuration file for that item, generating the corresponding `index.json` file.
-
-```typescript
-// types/FileType.ts
-
-export type SpriteType = {
- name: string
- code?: string
- file?: FileType[]
- config: {
- [key: string]: any
- }
-}
-
-export type SoundType = {
- name: string
- file?: FileType[]
- config: {
- path: string
- rate?: number
- [key: string]: any
- }
-}
-
-export type BackdropType = {
- file?: FileType[]
- [key: string]: any
-}
-
-
-export type ProjectType = {
- title: string
- sprites: SpriteType[]
- sounds?: SoundType[]
- backdrop: BackdropType
-}
-
-export type FileType = {
- name: string
- value: Blob | string
- type: string
-}
-```
-
-Finally, we unify them in the repository to form a JavaScript tree for the spx project.
-
-```ts
-// stores/FileManager.ts
-
-export const useFileManagerStore = defineStore('FileManager', () => {
- const spriteStore = useSpriteStore()
- const { sprites } = storeToRefs(spriteStore)
-
- const soundStore = useSoundStore()
- const { sounds } = storeToRefs(soundStore)
-
- const backdropStore = useBackdropStore()
- const { backdrop } = storeToRefs(backdropStore)
-
- const title = ref('')
-
- const project = computed(() => ({
- title: title.value,
- sprites: sprites.value,
- sounds: sounds.value,
- backdrop: backdrop.value
- }))
-
- return {
- project: readonly(project)
- }
-})
-```
-
diff --git a/docs/installation/installation.md b/docs/installation/installation.md
deleted file mode 100644
index c104bb49c..000000000
--- a/docs/installation/installation.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# Installation
-
-## STEM EDUCATION
-
-STEM EDU is an online educational platform for children's programming, designed to offer an interactive learning experience.
-
-## How to Run
-
-### Docker
-
-````bash
-# Build the all-in-one image
-docker build -t spx .
-# Assuming you have a .env file in pwd,
-# which contains the environment variables for backend
-docker run -v $(pwd)/.env:/app/.env spx
-````
-
-### Step by Step
-
-#### Frontend
-
-Before you begin, ensure that you have both **Go** and **Node.js** environments set up on your local machine.
-
-##### Frontend Setup
-
-Clone the repository and install.
-
-```bash
-git clone https://github.com/goplus/builder.git
-cd spx-gui
-npm install
-```
-
-##### Offline SPX Config
-
-In this stage, we build the two WASM components required by the web app and copy them into the app's assets folder.
-
-```bash
-## in spx-gui folder
-./build-wasm.sh
-```
-
-##### Build/Running the project
-
-```bash
-## in spx-gui folder
-# Build the project
-npm run build
-
-# Or, run the project in development mode
-npm run dev
-```
-
-#### Backend
-
-```bash
-cd spx-backend
-# Assuming you have Go & Go+ installed
-gop build -o spx-backend ./cmd/spx-backend
-
-# Run the server, assuming you have a .env file in pwd
-./spx-backend
-```
-
-### 4. Quick Play
-
-To start using Go+ Builder, you need to create a new project. You can do this by clicking on the `New Project` button, which is located at the bottom of the home page, and inside the drop-down menu that looks like a folder in the top navigation bar.
-
-After creating a new project, you will find that the project already contains some preset content. You can explore this content to understand the structure and functionality of the project.
-
-Remember, the preset content in the new project is just a starting point. Feel free to modify it and add your own content to make the project your own.
-
-## API Documentation
-
-[api doc](../api-doc/api-document.md).
diff --git a/docs/io-design.md b/docs/io-design.md
deleted file mode 100644
index 377262dbb..000000000
--- a/docs/io-design.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# I/O Subsystem Design
-
-# Project Import
-
-## Scratch Analysis
-
-### Page Features
-
-![](static/EeslbhYgyoZ8y0xpg0wcIf8Znbg.png)
-
-## Github
-
-### [Go-github](https://github.com/google/go-github)
-
-- Applicable Scenarios: If your main goal is to interact with GitHub through GitHub's REST API without needing to perform low-level Git operations, then go-github is an excellent choice. It is suitable for common GitHub operations such as retrieving information from GitHub, downloading files, managing issues, and pull requests.
-- User Authorization: Guide users through GitHub authentication to obtain an authorization token.
-- Retrieve Repository List: Use the GitHub API and authorization token to retrieve the user's repository list and display it in the front-end interface for user selection.
-- Retrieve File Content: Get the content of files from the repository selected by the user using the GitHub API.
-- Save to Project: Save the retrieved file content to the project.
-
-### [Go-git](https://github.com/src-d/go-git)
-
-- Applicable Scenarios: For more low-level operations on Git repositories, or if you want more advanced control over handling Git objects and version control, then go-git is a great choice. It is suitable for situations that require more Git operations, not just exporting projects from GitHub.
-
-# File Import and Front-End Storage
-
-| Feature/Library | [Dexie.js(10.3k)](https://github.com/dexie/Dexie.js) | [localForage(23.7k)](https://github.com/localForage/localForage) | [PouchDB(16.1k)](https://github.com/pouchdb/pouchdb) |
-|-----------------------|--------------------------------------------------------------------|-----------------------------------------------------------------------------|------------------------------------------------------------------|
-| **Main Features** | Powerful, Promise-based IndexedDB wrapper | Simplified, localStorage-like interface | Database that can run in browsers and servers |
-| **API Style** | Promise-based | localStorage-like API, based on callbacks or Promises | Promise-based, similar to CouchDB style |
-| **Ease of Use** | High (clear and concise API) | High (simple API, easy to use) | Medium to High (feature-rich but requires more learning) |
-| **Data Structure** | Custom, flexible | Key-value storage | Document storage, suitable for complex data structures |
-| **Sync Functionality**| None (local storage only) | None (local storage only) | Supports syncing with other PouchDB or CouchDB instances |
-| **Applicable Scenarios** | Applications requiring complex queries and high performance | Simple applications needing local key-value storage | Applications requiring offline data sync and replication |
-| **Browser Support** | Widely supported | Widely supported | Widely supported |
-| **Data Capacity** | Limited by the browser | Limited by the browser | Limited by the browser |
-| **Performance** | High | Medium | High, especially in terms of data syncing |
-| **Community Support and Documentation** | Good | Good | Good |
-| **Special Advantages**| Powerful query capabilities and flexible data structure | Simple to use, no need to understand complex IndexedDB | Strong sync functionality and complex data structure handling |
-
-# Front-End Zip Package Upload, Decompression, and Storage to IndexedDB
-
-| Feature/Library | JSZip | zip.js |
-|-----------------------|-----------------------------------------------------------|---------------------------------------------------------|
-| **Introduction** | Library for creating, reading, and editing ZIP files | Library for handling ZIP files on the client-side |
-| **Ease of Use** | Easy to use, with clear documentation and wide community support | API is more complex, steep learning curve |
-| **Performance** | Efficient for small to medium-sized files | Supports multithreading, more efficient for large files |
-| **Functionality** | Supports compression, decompression, editing, and other operations | Mainly focused on compression and decompression |
-| **File Size Handling Capability** | Larger files may cause performance issues | More suitable for handling large files |
-| **Compatibility** | Works well in all modern browsers | Works well in most modern browsers |
-| **Asynchronous Processing** | Supports Promises, enabling asynchronous operations | Supports Web Workers, optimizing asynchronous processing |
-| **Community Support and Documentation** | Extensive community support and comprehensive documentation | Documentation is relatively less complete |
-| **Usage Scenarios** | Suitable for most standard front-end ZIP file processing needs | Suitable for high-performance needs of large ZIP file processing |
-
-## Recommended Solution
-
-- File Upload: Combine HTML5 File API and Drag-and-Drop API to implement flexible upload functionality.
-- Zip Package Processing: Opt for **JSZip**, considering its ease of use and sufficient functionality for most needs.
-- Storing Decompressed Files: Use IndexedDB, considering its support in modern browsers and large storage capacity.
diff --git a/docs/library-design.md b/docs/library-design.md
deleted file mode 100644
index 12b82e32b..000000000
--- a/docs/library-design.md
+++ /dev/null
@@ -1,277 +0,0 @@
-# Sprite Library
-
-# Competitive Analysis
-
-| | Scratch | Code Monkey |
-| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
-| Sprite Library | Support for importing materials from the librarySupport for searching materialsSupport for local uploading of materials 🌟🌟Support for hovering over the clip to show multi-frame animation 🌟🌟Support for clicking on the SURPRISE button to randomize the clip. | Supports importing materials from the librarySupport for searching materials 🌟🌟Support for your own material library |
-
-## STEM Functional Design
-
-| Module | Function | Description |
-| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
-| Sprite Library(User) | ✅ Add a Sprite file to your stage | You can select a Sprite file from the Sprite library, or you can import it locally, you can import jpg, png, gif |
-| | ✅Delete the Sprite file | ✅Delete directly ⭕️ expires 30 days after being placed in the recycle bin |
-| | ✅Find a Sprite file | Support name search, fuzzy search, later can support multi-language |
-| | ✅ Online preview of the Multi-Modeling Sprite file | The hover state plays a multistyled Sprite animation (gif) |
-| Sprite Library(Market) | ⭕️ Publishing Sprite Files | User can publish their Sprite files to market |
-| | ⭕️ download Sprite file | User can download Sprite from market to their own local area |
-| | ✅Sprite Ranking | Sort by heat, posting time, etc |
-| Innovative | ⭕️ Support user sprite cloud material library ⭕️ Sprite Favorites ✅Support for clicking on the SURPRISE button to randomly generate material ✅Support for hiding sprite layers | Users upload their own Sprites to add to their own cloud library for easy recall, and users uploading gifs are automatically split into multi-graphic materials Users create their own Sprite favorites- Randomly import a clip from the clip market to the stage- User can choose whether a layer is visible or not |
-
-# Difficulty analysis
-
-## How to realize sprite preview online
-
-Application: Frame Animation with Multi-Shape Sprite Material
-
-### Scratch practices
-
-Switch image URL : a material corresponds to multiple shapes, each shape is saved with a svg, switch md5ext(.svg) to switch the image to realize the animation.
-
-#### Scratch Sprite Json Example
-
-```json
-[
- {
- "name": "Abby",
- "tags": [
- "people",
- "person",
- "drawing"
- ],
- "isStage": false,
- "variables": {},
- "costumes": [
- {
- "assetId": "809d9b47347a6af2860e7a3a35bce057",
- "name": "abby-a",
- "bitmapResolution": 1,
- "md5ext": "809d9b47347a6af2860e7a3a35bce057.svg",
- "dataFormat": "svg",
- "rotationCenterX": 31,
- "rotationCenterY": 100
- },
- ...
- ],
- "sounds": [
- {
- "assetId": "83a9787d4cb6f3b7632b4ddfebf74367",
- "name": "pop",
- "dataFormat": "wav",
- "format": "",
- "rate": 44100,
- "sampleCount": 1032,
- "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav"
- }
- ],
- "blocks": {}
- },
- ...
- ]
-```
-
-### Other possible approaches
-
-#### option 1: generate gif animations
-
-Automatically generate animations on multiple images in the cloud and save the animations to show the animations directly instead of cutting them one by one.
-
-| Library/Tool | Processing Area | Processing Method | Brief Description |
-| ------------ | --------------- | ------------------ | ------------------------------------------------------------ |
-| GIF.js | Client-Side | JavaScript Library | Generates GIF animations directly in the browser using JavaScript. Suitable for small or client-heavy projects. |
-| ImageMagick | Server-Side | Command-Line Tool | Creating complex GIFs or other image formats on the server. |
-| Qiniu Dora | Server-Side | API | Currently only available for images stored in Qiniu Limitations of the original image: Supported formats are JPEG and PNG The maximum number of frames supported is 20 The maximum supported image size is 1080*1080 |
-
-##### GIF.js example
-
-```js
-import GIF from 'gif.js';
-
-function createGif() {
- let gif = new GIF({
- workers: 2,
- quality: 10
- });
-
- // Let's say images are an array of images that you want to convert into GIFs
- images.forEach(image => {
- gif.addFrame(image, {delay: 200}); // 200ms delay per frame
- });
-
- gif.on('finished', function(blob) {
- window.open(URL.createObjectURL(blob));
- });
-
- gif.render();
-}
-
-```
-
-##### Qiniu Dora Animate API
-
-https://developer.qiniu.com/dora/5448/animate
-
-API
-
-```
-animate/duration/
- /merge/key/
- /key/
- ...
- /effect/
-```
-
-Params
-
-| Name | Required | Description |
-| ----------------- | -------- | ------------------------------------------------------------ |
-| | Y | The interval between each frame of a GIF (unit: 0.01s) must be an integer greater than 0. |
-| | N | The source image key (Base64 encoded) of the synthetic GIF ensures that all the source images come from the same bucket. |
-| | N | Define the playback order, with values of 0 and 1. (0: Loop playback in positive order; 1: Reverse loop playback; The default is 0. |
-
-#### option 2: generate real-time canvas animations
-
-| Library/Tool | Processing Area | Processing Method | Brief Description |
-| ------------ | --------------- | ------------------ | ------------------------------------------------------------ |
-| konva.js | Clisent-Side | JavaScript Library | Uses the Canvas API to dynamically draw image frames to create animation effects, entirely running in the browser, suitable for real-time rendering animations. |
-
-##### generate canvas by konva.js
-
-Need Sprite Chart to render.
-
-### Step-by-step Comparison
-
-| Step | Scratch | Gif(gif.js/dora) | Canvas(konva.js) |
-| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
-| 1 | Contains the official default library's material array in the project json file. | Generate gif animations in the cloud and display them directly. | Create a canvas |
-| 2 | Retrieve json data from the sprite-library component; pass it to the LibraryComponent. | Randomize import positions using a random function in Scratch. | Create real-time canvas animations for sprite graphics with multi-frame compositions |
-| 3 | LibraryComponent processes data; categorizes sprite materials by tags. | - | |
-| 4 | Pass processed sprite materials to LibraryItem for rendering, includes multi/single frame modeling objects. | - | |
-| 5 | Handle mouse hover over sprites in library-item for multi-frame materials; rotate icons and update URL in LibraryItemComponent. | - | |
-| 6 | Render image in LibraryItemComponent. | - | |
-
-## How to construct official sprite library
-
-### Homemade sprite material
-
-There are three sources of homemade official sprite stock footage: GIFs, videos, and existing sprites.
-
-#### GiFs
-
-| Implementation method | Tools/techniques | Advantages | Treatment area | Scene |
-| --------------------- | ---------------- | ------------------------------------------------------------ | -------------- | ----------------------------------------------------- |
-| Diffraction (physics) | FFmpeg | Highly efficient with multimedia data handling Supports multiple video and image formats, including GIF Highly customizable with command line arguments | Server-side | More power in video and audio processing |
-| Diffraction (physics) | ImageMagick | Powerful in image editing and conversion Supports a wide range of image formats | Server-side | Excellent performance in image editing and conversion |
-
-Use FFmpeg
-
-```js
-const { exec } = require('child_process');
-
-const generateFramesFromGIF = (inputGifPath, outputFramePrefix) => {
- const command = `ffmpeg -i ${inputGifPath} ${outputFramePrefix}_%04d.png`;
- exec(command, (error, stdout, stderr) => {
- if (error) {
- console.error(`Error: ${error.message}`);
- return;
- }
- console.log('Frames generated successfully');
- });
-};
-
-generateFramesFromGIF('input.gif', 'output_frame');
-
-```
-
-#### Videos
-
-Use FFmpeg
-
-```js
-const { exec } = require('child_process');
-
-const extractFramesFromVideo = (inputVideoPath, outputFramePrefix) => {
- const command = `ffmpeg -i ${inputVideoPath} -vf fps=1 ${outputFramePrefix}_%04d.png`;
- exec(command, (error, stdout, stderr) => {
- if (error) {
- console.error(`Error: ${error.message}`);
- return;
- }
- console.log('Frames extracted successfully');
- });
-};
-
-extractFramesFromVideo('input.mp4', 'output_frame');
-
-```
-
-
-
-#### Sprite Charts
-
-Use PIL (Python)
-
-```python
-from PIL import Image
-
-def extract_sprites(sprite_sheet_path, sprite_size, output_prefix):
- sprite_sheet = Image.open(sprite_sheet_path)
- width, height = sprite_sheet.size
-
- for i in range(0, width, sprite_size[0]):
- for j in range(0, height, sprite_size[1]):
- box = (i, j, i + sprite_size[0], j + sprite_size[1])
- sprite = sprite_sheet.crop(box)
- sprite.save(f"{output_prefix}_{i}_{j}.png")
-
-extract_sprites("sprite_sheet.png", (64, 64), "sprite")
-
-```
-
-
-
-### Ready-made sprite material library
-
-Free: https://www.aigei.com/game2d/character/dynamic_1
-
-Paid: https://craftpix.net/categorys/sprites/
-
-
-
-## How to save the library
-
-### Qiniu Kodo
-
-> Kodo is a self-developed unstructured data storage management platform that supports both central and edge storage
-
-https://developer.qiniu.com/kodo/1234/upload-types
-
-#### Methods
-
-| Method | Description | Advantage | Disadvantage |
-| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
-| Form upload | Complete the upload of a file in a single HTTP POST request | Ideal for simple application scenarios and small file sizes | If the file size is large (larger than 1GB) or the network environment is poor, the HTTP connection may time out and the upload may fail. |
-| Chunked Upload | A Chunked Upload divides a file into multiple small chunks, each of which is uploaded separately in a separate HTTP request. | It is suitable for large file transfers, and uses sharding to avoid connection timeouts caused by the large amount of single HTTP data.Resumable upload is supported. | require multiple HTTP requests to complete the upload process, which will incur additional costsIncreased code complexity |
-
-#### Form Upload Example
-
-```html
-
-```
-
-
-## Recommended Solutions
-
-- **How to realize sprite preview online:** Generate gif animations by Qiniu Dora animate API.
-- **Official sprite material library construction:** Generate our official stock footage library from Gif and Video by FFmpeg, and from Sprite Chart by PIL(Python). The same library is used to reduce the development cost. Because it is an official material library production without offline problem, and we use server-side processing to reduce the overhead of the client.
-- **How to save the library:** Use form-upload method to store library in Qiniu's kodo.
\ No newline at end of file
diff --git a/docs/load-save.md b/docs/load-save.md
deleted file mode 100644
index 55e3d6519..000000000
--- a/docs/load-save.md
+++ /dev/null
@@ -1,96 +0,0 @@
-### Background
-
-#### Project Background
-
-In current software development practices, project management and file version control are core components. Projects involve a variety of file types, including code, images, audio, etc., requiring effective management of these files to support the development and maintenance of the project.
-
-#### Requirement Background
-
-Users need a convenient and efficient way to create, edit, save, and manage project files. Especially during file editing, the ability to perform incremental updates rather than just full updates is needed to improve efficiency and save resources. Additionally, the uniqueness and non-redundancy of project files need to be ensured.
-
-### Solution Overview
-
-**Project files are no longer stored in object storage on a project-by-project basis, but rather on a file-by-file basis**.
-
-#### Roles and Their Responsibilities
-
-- **Frontend Role**: Responsible for the design and implementation of the user interface, including the creation, editing, and display of project files. The frontend also needs to handle communication with the backend and interact directly with the object storage service to upload and delete files.
-- **Backend Role**: Responsible for handling requests from the frontend, including providing complete project information and updating project information. Project information mainly includes the project's file structure, version number, `id`, `uid`, update time, etc. The backend does not directly deal with object storage.
-- **Object Storage Service**: Provides storage capabilities for project files. Each file is accessed through a unique URL generated by a hash algorithm, ensuring the file's uniqueness and non-redundancy.
-
-#### How It Works
-
-1. **Project Loading**: The frontend makes an HTTP request to the backend, which retrieves the complete project information from the database and returns it to the frontend. The frontend parses and displays the project content.
-
- An example of the file structure is as follows (using MySQL database, which cannot store arrays, so JSON is serialized into a string for easy storage in the database):
-
- ```json
- {
- "index.json": "https://xxx.com/d2a6802666d0d9248393d0b02abc93ae.json",
- "assets/demo.png":"https://xxx.com/f535a73a3dfe15ca7c623512f09d5ed4.png"
- }
- ```
-
- The key corresponds to the file's relative path in the project, and the value corresponds to the file's online address (using object storage). The backend needs to deserialize the project file structure for the frontend, thus loading the entire project.
-
- After loading the entire project, the browser might have a data structure like this:
-
- ```json
- const projectFiles = {
- "index.json": {
- onlineUrl: "https://xxx.com/123.json",
- content: '...'
- },
- // ...
- }
-
- ```
-
- The key corresponds to the file's relative path, and the value includes the online address, file content, etc.
-
-
-
-2. **Project Editing and Saving**: After the user edits a project file, the frontend interacts directly with the object storage service for file upload and deletion. After editing, the frontend sends the updated project file structure to the backend, which updates the database.
-
- ### Core Logic Detail Explanation
-
- #### Loading Project Files
-
- - **Frontend Request**: Carries the project ID to initiate a project loading request to the backend.
- - **Backend Processing**: Queries the project file structure in the database based on the project ID, and returns the serialized JSON string to the frontend.
- - **Frontend Parsing**: Converts the JSON string into a data structure of project files for editing and browsing by the user.
-
- #### Saving Edited Projects
-
- - **File Processing**: After editing a file, the frontend removes the file's `onlineUrl` and interacts with the object storage service to delete the old file, upload the new file, and obtain a new `url`.
- - **Project File Structure Update**: The frontend collects all new `urls` of the files, forming an updated project file structure.
- - **Backend Database Update**: The frontend sends the updated project file structure to the backend, which serializes it and updates the database, with the version number changing accordingly.
-
- ### Points to Note
-
- - Using a hash algorithm (e.g., MD5) to generate the file name for a file maintains the original file extension. This ensures the file's online address uniquely identifies the file, avoiding duplicate files and saving resources, as duplicate files will have the same `url`. If a file is changed, its `url` will change, providing a basis for a variant of incremental update between the client and server.
-
- - Users may need to download the entire project. This can be quickly accomplished directly with the object storage's packaging feature, meaning the frontend can directly interact with object storage without going through the backend.
-
- - Upon clicking the edit button in the UI, the frontend deletes the online address of the file, which is the `onlineUrl`. This is because once a file is edited, it is considered changed, and without a new online address, the file's old online address is removed. This approach allows for a kind of incremental update based on the presence or absence of each project file's online address in the browser.
-
- A possible logic for frontend project update:
-
- ```javascript
- function saveProject() {
- var fileTree = {}
- for (const path of projectFiles) {
- var url = projectFiles[path].onlineUrl
- if (url == null) {
- url = uploadFile(path, projectFiles[path].content)
- }
- fileTree[path] = url
- }
- postToServer(fileTree)
- }
- ```
-
- - For a new project, since every file edited by the user does not have an online address, files without `urls` can be uploaded in the manner described above, and the new file structure can be sent to the backend.
- - The initial version of the project is set to 1. When the user saves the project, the backend increases the version number as it updates the database.
- - Throughout the process of loading and saving project files, the interaction between the frontend and backend only involves loading project file structures from the backend and updating the database by the backend, without the backend dealing with object storage. The frontend interacts directly with object storage.
-
diff --git a/docs/project-runner.md b/docs/project-runner.md
deleted file mode 100644
index 464422642..000000000
--- a/docs/project-runner.md
+++ /dev/null
@@ -1,106 +0,0 @@
-# Project runner
-
-# Module Overview
-
-This module is part of the compiler implementation for an online editor. This document investigates the existing ecosystem tools of Spx and organizes the technical challenges and feasible solutions for implementing online compilation.
-
-# GitHub Repository
-- https://github.com/goplus/spx
-- https://github.com/goplus/gop
-- https://github.com/goplus/ispx
-- https://github.com/goplus/igop
-
-# Client-Side vs. Server-Side Compilation Comparison
-
-## Client-Side Compilation
-
-**Advantages:**
-
-1. Immediate Compilation and Execution: Users can compile and run code immediately in the same environment, enhancing user experience.
-2. Reduced Server Load: Compilation occurs on the user's device, not occupying server resources.
-3. Offline Capability: The application can operate offline without needing to interact with the server.
-4. Real-time Feedback: Users can see the results of code changes instantly, aiding in learning and debugging.
-
-**Disadvantages:**
-
-1. Performance Limitations: Client-side devices might not perform as well as servers, especially for complex compilation tasks.
-2. Security Concerns: Exposing the compiler to the client may pose security risks.
-3. Compatibility and Consistency: Different devices and browsers among users may lead to variations in compilation behavior.
-4. Increased Frontend Complexity: Implementing file assembly, reading, and compilation functions on the frontend is required.
-
-## Server-Side Compilation
-
-**Advantages:**
-
-1. Unified Compilation Environment: The server provides a consistent compilation environment, ensuring uniformity in compilation results.
-2. Powerful Processing Capability: Servers typically have more computing power than clients, suitable for complex compilation tasks.
-3. Enhanced Security: Compilation on the server reduces security risks on the client side.
-4. Convenient File Management: Servers are better suited for file storage and management.
-
-**Disadvantages:**
-
-1. Response Time: Uploading files from the client to the server and sending back compilation results may cause delays.
-2. Server Load: All compilation tasks occurring on the server can increase its load.
-3. Network Dependency: Continuous network connection is required for file transfer and receiving compilation results.
-4. Lack of Immediate Feedback: Users may not see the effects of their changes right away, affecting the experience.
-
-## Summary
-
-### **Supported**
-
-> Client-side compilation has unique advantages
-
-| Feature | network dependency | response speed | real time feedback |
-| ----------------------- | ------------------ | -------------- | ------------------ |
-| Client compilation | no | fast | yes |
-| Server side compilation | yes | slow | no |
-
-# Front-End Storage Solutions Research
-
-## Existing Web Storage Technologies
-| Technology | Capacity Limit | Data Persistence | Access Speed | Sync/Async | Security | Use Cases |
-|------------------------|-------------------------------------------------|---------------------------------------------------|---------------------------------------------------|--------------|------------------------------------------------|---------------------------------------------|
-| LocalStorage | 5MB per domain | Persistent until browser cache is cleared | Usually fast | Synchronous | Not suitable for sensitive data | Storing small amounts of non-sensitive data |
-| SessionStorage | 5MB per domain | Persistent only during browser session | Usually fast | Synchronous | Not suitable for sensitive data | Storing small amounts of non-sensitive data |
-| Cookies | 4KB per domain | Expirable, but can be cleared by user | Fast, but sent with every HTTP request | Synchronous | Can be set to HTTPS only to enhance security | Storing small pieces of data like user authentication |
-| IndexedDB | Several hundred MB or more, depending on user's browser and disk space | Persistent storage | Fast, but can be slower due to asynchronicity | Asynchronous | Not suitable for sensitive data | Storing large amounts or structured text data |
-| Cache API | Depends on user's disk space | Persistent, but can be cleared by user | Depends on data size and disk speed | Asynchronous | Not suitable for sensitive data | Caching requests and supporting offline content |
-| File System Access API | Depends on user's disk space | Persistent, but can be cleared by user | Depends on data size and disk speed | Asynchronous | Provides more direct file access, but requires user permission | Applications that interact with user's local files |
-
-## Compare three technologies based on different perspectives
-| Perspectives | IndexedDB | File System Access API | Cache API |
-|-----------------------------|------------------------------------------------------------|----------------------------------------------------------------|-----------------------------------------------------|
-| Basic Compatibility | Supported by all mainstream browsers, but implementation details may vary | Mainly supported by Chrome, lower or no support in Firefox, Safari, and Edge | Part of Service Workers, well supported in mainstream browsers |
-| Performance and Limitations | Performance and database size limits vary by browser | Usage may be limited, especially for security and privacy reasons | Storage policies and limitations may slightly differ |
-| Mobile Device Compatibility | Well supported on mobile versions of Chrome and Firefox, potential issues with Safari | Mainly supported on Chrome, other browsers offer lower or no support | Generally well-supported on mobile browsers |
-| Data Synchronization | Requires custom sync logic with the server; suitable for complex data structures | Sync is more complex, often manual; interacts directly with local files | Mainly used for resource caching, not designed specifically for data sync |
-| Offline Support | Excellent for offline data storage; supports large and structured data | Direct read and write to local files; very suitable for offline operations | Key component for offline access in PWAs; caches network requests and responses |
-| Storage Space Management | Large storage capacity, decided by the browser; monitoring needed to avoid exceeding limits | Depends on the user's local file system; potentially very large, but management is complex | Managed by the browser's caching mechanisms; constrained by browser cache limits |
-| User Prompt | Usually no user prompt required, operates silently in the background | Explicit user permission needed to access files; requires direct and ongoing user interaction | No user prompt needed for caching storage; operates silently in the background |
-| User Interface | Operates in the background; does not directly affect the user interface | Requires direct user interaction, such as file selectors and other UI elements | Invisible to the user; operates silently in the background |
-| Experience Design | Indirectly impacts user experience; very useful for complex data processing within apps | Significantly affects user experience; user permission prompts and file management are core aspects | Improves user experience by speeding up load times and providing offline content access |
-## Summary
-
-### **Supported**
-
-The following web API was chosen to support a generic browser FileSystem.
-
-| Feature | Firefox | Chrome | Edge | Safari | IE |
-| --------------------------------------------------------------------------- | ------- | ------ | ---- | ------ | -- |
-| [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) | yes | yes | yes | yes | no |
-
-### **Rejected**
-
-The following APIs were all considered in choosing a backend to support
-
-| Rejected APIs | Reason |
-| ---------------------------------------------------------------------------------------- | ---------------------- |
-| [LocalStorage](https://developer.mozilla.org/en/docs/Web/API/Window/localStorage) | Limited storage |
-| [SessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) | Limited storage |
-| [Cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) | Limited storage |
-| [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) | Requires a request URL |
-| [File System Access API](https://web.dev/file-system-access/) | Chromium only |
-
-# participants
-
-[a-linye](https://github.com/a-linye), [motongxue](https://github.com/motongxue).
diff --git a/docs/screenshot.jpeg b/docs/screenshot.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..0c0fb51e95177d452fa0d5b51012385a03362f9a
GIT binary patch
literal 554868
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