1
0
Эх сурвалжийг харах

Merge branch 'main' of https://github.com/HeyPuter/puter into main

Nariman Jelveh 8 сар өмнө
parent
commit
377c9f04b1

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "submodules/v86"]
+	path = submodules/v86
+	url = git@github.com:copy/v86.git

+ 131 - 0
doc/i18n/README.hy.md

@@ -0,0 +1,131 @@
+<h3 align="center"><img width="80" alt="Puter.com, The Personal Cloud Computer: All your files, apps, and games in one place accessible from anywhere at any time." src="https://assets.puter.site/puter-logo.png"></h3>
+
+<h3 align="center">Ինտերնետ ՕՀ! Անվճար, բաց կոդով և ինքնահոսթ հնարավորությամբ։</h3>
+
+<p align="center">
+    <img alt="GitHub repo size" src="https://img.shields.io/github/repo-size/HeyPuter/puter"> <img alt="GitHub Release" src="https://img.shields.io/github/v/release/HeyPuter/puter?label=latest%20version"> <img alt="GitHub License" src="https://img.shields.io/github/license/HeyPuter/puter">
+</p>
+<p align="center">
+    <a href="https://puter.com/"><strong>« Օնլայն դեմո »</strong></a>
+    <br />
+    <br />
+    <a href="https://puter.com">Puter.com</a>
+    ·
+    <a href="https://docs.puter.com" target="_blank">SDK</a>
+    ·
+    <a href="https://discord.com/invite/PQcx7Teh8u">Discord</a>
+    ·
+    <a href="https://www.youtube.com/@EricsPuterVideos">YouTube</a>
+    ·
+    <a href="https://reddit.com/r/puter">Reddit</a>
+    ·
+    <a href="https://twitter.com/HeyPuter">X (Twitter)</a>
+    ·
+    <a href="https://hackerone.com/puter_h1b">Bug Bounty</a>
+</p>
+
+<h3 align="center"><img width="800" style="border-radius:5px;" alt="screenshot" src="https://assets.puter.site/puter.com-screenshot-3.webp"></h3>
+
+<br/>
+
+## Puter
+
+Puter-ը առաջադեմ, բաց կոդով ինտերնետային օպերացիոն համակարգ է, որը նախագծված է լինել ֆունկցիոնալ հարուստ, բացառիկ արագ և բարձր ընդլայնելի։ Puter-ը կարող է օգտագործվել հետևյալ կերպ․
+
+- Անձնական ամպային համակարգ՝ առաջնային գաղտնիությամբ, որը թույլ է տալիս պահել ձեր բոլոր ֆայլերը, հավելվածները և խաղերը մեկ անվտանգ վայրում, որը հասանելի է ցանկացած վայրից և ցանկացած ժամանակ։
+- Պլատֆորմ կայքերի, վեբ հավելվածների և խաղերի ստեղծման և հրապարակման համար։
+- Dropbox, Google Drive, OneDrive և այլ ծառայությունների այլընտրանք՝ նոր ինտերֆեյսով և հզոր գործառույթներով։
+- Հեռավոր աշխատասեղանի միջավայր սերվերների և աշխատանքային կայանների համար։
+- Պարզ, բաց կոդով նախագիծ և համայնք՝ վեբ ծրագրավորման, ամպային հաշվարկների, բաշխված համակարգերի և այլ թեմաների մասին սովորելու համար։
+
+<br/>
+
+## Սկսել
+
+
+### 💻 Լոկալ ծրագրավորում
+
+```bash
+git clone https://github.com/HeyPuter/puter
+cd puter
+npm install
+npm start
+```
+
+Սա կգործակի Puter-ը հետևյալ հասցեով՝ http://puter.localhost:4100 (կամ հաջորդ հասանելի պորտով)։
+
+<br/>
+
+### 🐳 Docker
+
+
+```bash
+mkdir puter && cd puter && mkdir -p puter/config puter/data && sudo chown -R 1000:1000 puter && docker run --rm -p 4100:4100 -v `pwd`/puter/config:/etc/puter -v `pwd`/puter/data:/var/puter  ghcr.io/heyputer/puter
+```
+
+<br/>
+
+
+### 🐙 Docker Compose
+
+
+#### Linux/macOS
+```bash
+mkdir -p puter/config puter/data
+sudo chown -R 1000:1000 puter
+wget https://raw.githubusercontent.com/HeyPuter/puter/main/docker-compose.yml
+docker compose up
+```
+<br/>
+
+#### Windows
+
+
+```powershell
+mkdir -p puter
+cd puter
+New-Item -Path "puter\config" -ItemType Directory -Force
+New-Item -Path "puter\data" -ItemType Directory -Force
+Invoke-WebRequest -Uri "https://raw.githubusercontent.com/HeyPuter/puter/main/docker-compose.yml" -OutFile "docker-compose.yml"
+docker compose up
+```
+<br/>
+
+### ☁️ Puter.com
+
+Puter-ը հասանելի է որպես հյուրընկալվող ծառայություն [**puter.com**](https://puter.com).
+
+<br/>
+
+## System Requirements
+
+- **Օպերացիոն համակարգ:** Linux, macOS, Windows
+- **Օպերատիվ հիշողություն:** 2GB նվազագույնը (4GB խորհուրդ է տրվում)
+- **Համակարգչի հիշողություն:** 1GB ազատ տարածություն
+- **Node.js:** Տարբերակ 16+ (Տարբերակ 22+ խորհուրդ է տրվում)
+- **npm:** Վերջին կայուն տարբերակը
+
+<br/>
+
+## Աջակցություն
+
+Կապվեք համակարգողների և համայնքի հետ այս կայքերի միջոցով՝
+
+- Սխալների կամ գործառույթի հարցում՝ (https://github.com/HeyPuter/puter/issues/new/choose).
+- Discord: [discord.com/invite/PQcx7Teh8u](https://discord.com/invite/PQcx7Teh8u)
+- X (Twitter): [x.com/HeyPuter](https://x.com/HeyPuter)
+- Reddit: [reddit.com/r/puter/](https://www.reddit.com/r/puter/)
+- Mastodon: [mastodon.social/@puter](https://mastodon.social/@puter)
+- Անվտանգության խնդիրներ՝ [security@puter.com](mailto:security@puter.com)
+- Email maintainers at [hi@puter.com](mailto:hi@puter.com)
+
+Մենք միշտ ուրախ ենք օգնել ձեզ ցանկացած հարցում։ Մի կաշկանդվեք հարցնել։
+
+<br/>
+
+
+##  Լիցենզիա
+
+Այս պահոցարանը, ներառյալ բոլոր իր բովանդակությունը, ենթա-պրոյեկտները, մոդուլները և բաղադրիչները, լիցենզավորվում են [AGPL-3.0](https://github.com/HeyPuter/puter/blob/main/LICENSE.txt) լիցենզիայի տակ, եթե այլ կերպ հստակ նշված չէ։ Այս պահոցարանում ներառված երրորդ կողմի գրադարանները կարող են ենթարկվել իրենց սեփական լիցենզիաներին։
+
+<br/>

+ 349 - 0
package-lock.json

@@ -6897,6 +6897,13 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/html-minifier-terser": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+      "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/http-assert": {
       "version": "1.5.5",
       "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.5.tgz",
@@ -8194,6 +8201,17 @@
         "node": ">=6"
       }
     },
+    "node_modules/camel-case": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+      "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pascal-case": "^3.1.2",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/camelcase": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
@@ -9189,6 +9207,16 @@
         "node": ">=8"
       }
     },
+    "node_modules/dom-converter": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
+      "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "utila": "~0.4"
+      }
+    },
     "node_modules/dom-serializer": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
@@ -9249,6 +9277,17 @@
         "url": "https://github.com/fb55/domutils?sponsor=1"
       }
     },
+    "node_modules/dot-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+      "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/dotenv": {
       "version": "16.4.5",
       "dev": true,
@@ -9286,6 +9325,10 @@
       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
       "license": "MIT"
     },
+    "node_modules/emulator": {
+      "resolved": "src/emulator",
+      "link": true
+    },
     "node_modules/enabled": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
@@ -10800,6 +10843,147 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/html-minifier-terser": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+      "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "camel-case": "^4.1.2",
+        "clean-css": "^5.2.2",
+        "commander": "^8.3.0",
+        "he": "^1.2.0",
+        "param-case": "^3.0.4",
+        "relateurl": "^0.2.7",
+        "terser": "^5.10.0"
+      },
+      "bin": {
+        "html-minifier-terser": "cli.js"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/html-minifier-terser/node_modules/commander": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+      "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/html-webpack-plugin": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz",
+      "integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/html-minifier-terser": "^6.0.0",
+        "html-minifier-terser": "^6.0.2",
+        "lodash": "^4.17.21",
+        "pretty-error": "^4.0.0",
+        "tapable": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/html-webpack-plugin"
+      },
+      "peerDependencies": {
+        "@rspack/core": "0.x || 1.x",
+        "webpack": "^5.20.0"
+      },
+      "peerDependenciesMeta": {
+        "@rspack/core": {
+          "optional": true
+        },
+        "webpack": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/htmlparser2": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+      "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+      "dev": true,
+      "funding": [
+        "https://github.com/fb55/htmlparser2?sponsor=1",
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.0.0",
+        "domutils": "^2.5.2",
+        "entities": "^2.0.0"
+      }
+    },
+    "node_modules/htmlparser2/node_modules/dom-serializer": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+      "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.0",
+        "entities": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/htmlparser2/node_modules/domhandler": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+      "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "domelementtype": "^2.2.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/htmlparser2/node_modules/domutils": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "dom-serializer": "^1.0.1",
+        "domelementtype": "^2.2.0",
+        "domhandler": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
+    "node_modules/htmlparser2/node_modules/entities": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
     "node_modules/http-errors": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -12279,6 +12463,16 @@
         "get-func-name": "^2.0.1"
       }
     },
+    "node_modules/lower-case": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+      "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/lru-cache": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -12825,6 +13019,17 @@
       "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==",
       "license": "MIT"
     },
+    "node_modules/no-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+      "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "lower-case": "^2.0.2",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/node-abi": {
       "version": "3.65.0",
       "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.65.0.tgz",
@@ -13399,6 +13604,17 @@
       "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
       "license": "(MIT AND Zlib)"
     },
+    "node_modules/param-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+      "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "dot-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/parent-module": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -13460,6 +13676,17 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/pascal-case": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+      "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/path-browserify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
@@ -13806,6 +14033,17 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/pretty-error": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
+      "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "lodash": "^4.17.20",
+        "renderkid": "^3.0.0"
+      }
+    },
     "node_modules/process": {
       "version": "0.11.10",
       "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@@ -14152,6 +14390,16 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/relateurl": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+      "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
     "node_modules/release-zalgo": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
@@ -14165,6 +14413,93 @@
         "node": ">=4"
       }
     },
+    "node_modules/renderkid": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
+      "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "css-select": "^4.1.3",
+        "dom-converter": "^0.2.0",
+        "htmlparser2": "^6.1.0",
+        "lodash": "^4.17.21",
+        "strip-ansi": "^6.0.1"
+      }
+    },
+    "node_modules/renderkid/node_modules/css-select": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+      "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.0.1",
+        "domhandler": "^4.3.1",
+        "domutils": "^2.8.0",
+        "nth-check": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/renderkid/node_modules/dom-serializer": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+      "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.0",
+        "entities": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/renderkid/node_modules/domhandler": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+      "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "domelementtype": "^2.2.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/renderkid/node_modules/domutils": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "dom-serializer": "^1.0.1",
+        "domelementtype": "^2.2.0",
+        "domhandler": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
+    "node_modules/renderkid/node_modules/entities": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
     "node_modules/require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -15822,6 +16157,13 @@
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
       "license": "MIT"
     },
+    "node_modules/utila": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+      "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/utils-merge": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -16762,6 +17104,13 @@
         "mocha": "^10.2.0"
       }
     },
+    "src/emulator": {
+      "version": "1.0.0",
+      "license": "AGPL-3.0-only",
+      "devDependencies": {
+        "html-webpack-plugin": "^5.6.0"
+      }
+    },
     "src/git": {
       "version": "1.0.0",
       "license": "AGPL-3.0-only",

+ 18 - 0
src/backend/src/modules/selfhosted/SelfHostedModule.js

@@ -81,6 +81,12 @@ class SelfHostedModule extends AdvancedBase {
                         PUTER_JS_URL: ({ global_config: config }) => config.origin + '/sdk/puter.dev.js',
                     }
                 },
+                {
+                    name: 'emulator:webpack-watch',
+                    directory: 'src/emulator',
+                    command: 'npm',
+                    args: ['run', 'start-webpack'],
+                },
             ],
         });
 
@@ -107,6 +113,18 @@ class SelfHostedModule extends AdvancedBase {
                     prefix: '/builtin/dev-center',
                     path: path_.resolve(__dirname, RELATIVE_PATH, 'src/dev-center'),
                 },
+                {
+                    prefix: '/builtin/dev-center',
+                    path: path_.resolve(__dirname, RELATIVE_PATH, 'src/dev-center'),
+                },
+                {
+                    prefix: '/builtin/emulator',
+                    path: path_.resolve(__dirname, RELATIVE_PATH, 'src/emulator/dist'),
+                },
+                {
+                    prefix: '/vendor/v86',
+                    path: path_.resolve(__dirname, RELATIVE_PATH, 'submodules/v86/build'),
+                },
             ],
         });
         

+ 45 - 0
src/emulator/assets/template.html

@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<head>
+  <meta charset="utf-8">
+  <title><%= htmlWebpackPlugin.options.title || 'Webpack App'%></title>
+
+  <% if (htmlWebpackPlugin.files.favicon) { %>
+  <link rel="shortcut icon" href="<%= htmlWebpackPlugin.files.favicon%>">
+  <% } %>
+  <% if (htmlWebpackPlugin.options.mobile) { %>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <% } %>
+
+  <% for (var css in htmlWebpackPlugin.files.css) { %>
+  <link href="<%= htmlWebpackPlugin.files.css[css] %>" rel="stylesheet">
+  <% } %>
+  
+  <script src="/puter.js/v2"></script>
+  <script src="/vendor/v86/libv86.js"></script>
+</head>
+<body>
+
+<% if (htmlWebpackPlugin.options.appMountId) { %>
+<div id="<%= htmlWebpackPlugin.options.appMountId%>"></div>
+<% } %>
+
+<% if (htmlWebpackPlugin.options.appMountIds && htmlWebpackPlugin.options.appMountIds.length > 0) { %>
+<% for (var index in htmlWebpackPlugin.options.appMountIds) { %>
+<div id="<%= htmlWebpackPlugin.options.appMountIds[index]%>"></div>
+<% } %>
+<% } %>
+
+<% if (htmlWebpackPlugin.options.window) { %>
+<script>
+  <% for (var varName in htmlWebpackPlugin.options.window) { %>
+    window['<%=varName%>'] = <%= JSON.stringify(htmlWebpackPlugin.options.window[varName]) %>;
+  <% } %>
+</script>
+<% } %>
+
+<% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
+<script src="<%= htmlWebpackPlugin.files.chunks[chunk].entry %>"></script>
+<% } %>
+
+</body>
+</html>

+ 16 - 0
src/emulator/package.json

@@ -0,0 +1,16 @@
+{
+  "name": "emulator",
+  "version": "1.0.0",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1",
+    "start-webpack": "webpack --watch --devtool source-map"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "AGPL-3.0-only",
+  "description": "",
+  "devDependencies": {
+    "html-webpack-plugin": "^5.6.0"
+  }
+}

+ 1 - 0
src/emulator/src/main.js

@@ -0,0 +1 @@
+puter.ui.launchApp('editor');

+ 12 - 0
src/emulator/webpack.config.js

@@ -0,0 +1,12 @@
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+
+module.exports = {
+    entry: [
+        './src/main.js'
+    ],
+    plugins: [
+        new HtmlWebpackPlugin({
+            template: 'assets/template.html'
+        }),
+    ]
+};

+ 2 - 2
src/gui/src/services/ExecService.js

@@ -51,8 +51,8 @@ export class ExecService extends Service {
 
             // If `window-active` is set (meanign the window is focused), focus the window one more time
             // this is to ensure that the iframe is `definitely` focused and can receive keyboard events (e.g. keydown)
-            if(child_process.el_win.hasClass('window-active')){
-                child_process.el_win.focusWindow();
+            if($(child_process.references.el_win).hasClass('window-active')){
+                $(child_process.references.el_win).focusWindow();
             }
         });
 

+ 3 - 3
src/gui/utils.js

@@ -129,11 +129,11 @@ async function build(options){
     for(let i = 0; i < js_paths.length; i++){
         main_array.push(path.join(__dirname, 'src', js_paths[i]));
     }
-    webpack({
-        ...BaseConfig({
+    await webpack({
+        ...(await BaseConfig({
             ...options,
             env: 'prod',
-        }),
+        })),
         mode: 'production',
         optimization: {
             minimize: true,

+ 3 - 3
src/gui/webpack.config.cjs

@@ -1,8 +1,8 @@
 const BaseConfig = require('./webpack/BaseConfig.cjs');
 
-module.exports = {
-    ...BaseConfig({ env: 'dev' }),
+module.exports = async () => ({
+    ...(await BaseConfig({ env: 'dev' })),
     optimization: {
         minimize: false
     },
-};
+});

+ 2 - 2
src/gui/webpack/BaseConfig.cjs

@@ -1,6 +1,6 @@
 const path = require('path');
 const EmitPlugin = require('./EmitPlugin.cjs');
-module.exports = (options = {}) => {
+module.exports = async (options = {}) => {
     const config = {};
     config.entry = [
         './src/init_sync.js',
@@ -18,7 +18,7 @@ module.exports = (options = {}) => {
         filename: 'bundle.min.js',
     };
     config.plugins = [
-        EmitPlugin({
+        await EmitPlugin({
             options,
             dir: path.join(__dirname, '../src/icons'),
         }),

+ 67 - 65
src/gui/webpack/EmitPlugin.cjs

@@ -1,78 +1,80 @@
 const fs = require('fs');
 const path = require('path');
 const uglifyjs = require('uglify-js');
+const webpack = require('webpack');
 
-module.exports = ({ dir, options }) => function () {
-    const compiler = this;
-    compiler.hooks.emit.tapAsync('EmitPlugin', async (compilation, callback) => {
-        let prefix_text = '';
-        prefix_text += `window.gui_env="${options.env}";\n`;
+module.exports = async ({ dir, options }) => {
+    let prefix_text = '';
+    prefix_text += `window.gui_env="${options.env}";\n`;
 
-        // -----------------------------------------------
-        // Combine all images into a single js file
-        // -----------------------------------------------
-        {
-            let icons = 'window.icons = [];\n';
-            fs.readdirSync(dir).forEach(file => {
-                // skip dotfiles
-                if (file.startsWith('.'))
-                    return;
-                // load image
-                let buff = new Buffer.from(fs.readFileSync(dir + '/' + file));
-                // convert to base64
-                let base64data = buff.toString('base64');
-                // add to `window.icons`
-                if (file.endsWith('.png'))
-                    icons += `window.icons['${file}'] = "data:image/png;base64,${base64data}";\n`;
-                else if (file.endsWith('.svg'))
-                    icons += `window.icons['${file}'] = "data:image/svg+xml;base64,${base64data}";\n`;
-            });
-            prefix_text += icons + '\n';
-        }
+    // -----------------------------------------------
+    // Combine all images into a single js file
+    // -----------------------------------------------
+    {
+        let icons = 'window.icons = [];\n';
+        fs.readdirSync(dir).forEach(file => {
+            // skip dotfiles
+            if (file.startsWith('.'))
+                return;
+            // load image
+            let buff = new Buffer.from(fs.readFileSync(dir + '/' + file));
+            // convert to base64
+            let base64data = buff.toString('base64');
+            // add to `window.icons`
+            if (file.endsWith('.png'))
+                icons += `window.icons['${file}'] = "data:image/png;base64,${base64data}";\n`;
+            else if (file.endsWith('.svg'))
+                icons += `window.icons['${file}'] = "data:image/svg+xml;base64,${base64data}";\n`;
+        });
+        prefix_text += icons + '\n';
+    }
 
-        // -----------------------------------------------
-        // Concat/merge the JS libraries and save them to ./dist/libs.js
-        // -----------------------------------------------
-        {
-            const lib_paths = require('./libPaths.cjs');
-            let js = '';
-            for(let i = 0; i < lib_paths.length; i++){
-                const file = path.join(__dirname, '../src/lib/', lib_paths[i]);
-                // js
-                if(file.endsWith('.js') && !file.endsWith('.min.js')){
-                    let minified_code = await uglifyjs.minify(fs.readFileSync(file).toString(), {mangle: false});
-                    if(minified_code && minified_code.code){
-                        js += minified_code.code;
-                        if(options?.verbose)
-                            console.log('minified: ', file);
-                    }
-                }else{
-                    js += fs.readFileSync(file);
+    // -----------------------------------------------
+    // Concat/merge the JS libraries and save them to ./dist/libs.js
+    // -----------------------------------------------
+    {
+        const lib_paths = require('./libPaths.cjs');
+        let js = '';
+        for(let i = 0; i < lib_paths.length; i++){
+            const file = path.join(__dirname, '../src/lib/', lib_paths[i]);
+            // js
+            if(file.endsWith('.js') && !file.endsWith('.min.js')){
+                let minified_code = await uglifyjs.minify(fs.readFileSync(file).toString(), {mangle: false});
+                if(minified_code && minified_code.code){
+                    js += minified_code.code;
                     if(options?.verbose)
-                        console.log('skipped minification: ', file);
+                        console.log('minified: ', file);
                 }
-
-                js += '\n\n\n';
+            }else{
+                js += fs.readFileSync(file);
+                if(options?.verbose)
+                    console.log('skipped minification: ', file);
             }
-            prefix_text += js;
+
+            js += '\n\n\n';
         }
+        prefix_text += js;
+    }
+    
+    return new webpack.BannerPlugin({
+        banner: prefix_text,
+        raw: true,
+    });
 
-        // -----------------------------------------------
-        // Webpack understands this code better than I do
-        // -----------------------------------------------
-        Object.keys(compilation.assets).forEach((assetName) => {
-            if (assetName.endsWith('.js')) {
-                const asset = compilation.assets[assetName];
-                const originalSource = asset.source();
-                const newSource = `${prefix_text}\n${originalSource}`;
-                compilation.assets[assetName] = {
-                    source: () => newSource,
-                    size: () => newSource.length,
-                };
-            }
-        });
+    // -----------------------------------------------
+    // Webpack understands this code better than I do
+    // -----------------------------------------------
+    // Object.keys(compilation.assets).forEach((assetName) => {
+    //     if (assetName.endsWith('.js')) {
+    //         const asset = compilation.assets[assetName];
+    //         const originalSource = asset.source();
+    //         const newSource = `${prefix_text}\n${originalSource}`;
+    //         compilation.assets[assetName] = {
+    //             source: () => newSource,
+    //             size: () => newSource.length,
+    //         };
+    //     }
+    // });
 
-        console.log('END');
-        callback();
-    });
+    console.log('END');
 };

+ 1 - 0
submodules/v86

@@ -0,0 +1 @@
+Subproject commit f3339aa78eeb9221d24ff86a2ea74b9ee1d2ee90