initial commit
This commit is contained in:
commit
3e90445fab
20 changed files with 961 additions and 0 deletions
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
.DS_Store
|
||||
**/target
|
||||
**/zig-cache
|
||||
**/zig-out
|
||||
node_modules
|
||||
build
|
||||
/package/vendors
|
||||
/package/src/extractor/.zig-cache
|
||||
/package/src/launcher/.zig-cache
|
||||
.zig-cache
|
||||
.colab.json
|
||||
dist
|
||||
.cache
|
||||
/package/bin/electrobun
|
||||
/package/bin/electrobun.exe
|
||||
electrobun-cef*
|
||||
electrobun-core*
|
||||
electrobun-cli*
|
||||
dist-*
|
||||
/package/src/cli/templates/embedded.ts
|
||||
*.tar.gz
|
||||
settings.local.json
|
||||
.tanstack
|
||||
artifacts
|
||||
9
.vscode/settings.json
vendored
Normal file
9
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
"files.readonlyInclude": {
|
||||
"**/routeTree.gen.ts": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/routeTree.gen.ts": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/routeTree.gen.ts": true
|
||||
}
|
||||
18
README.md
Normal file
18
README.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Gameflow Deck
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
2. Run in development mode:
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
3. Build for production:
|
||||
```bash
|
||||
bun run build
|
||||
```
|
||||
225
bun.lock
Normal file
225
bun.lock
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "electrobun-hello-world",
|
||||
"dependencies": {
|
||||
"electrobun": "^0.0.19-beta.110",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
||||
|
||||
"@oneidentity/zstd-js": ["@oneidentity/zstd-js@1.0.3", "", { "dependencies": { "@types/emscripten": "^1.39.4" } }, "sha512-Jm6sawqxLzBrjC4sg2BeXToa33yPzUmq20CKsehKY2++D/gHb/oSwVjNgT+RH4vys+r8FynrgcNzGwhZWMLzfQ=="],
|
||||
|
||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
|
||||
|
||||
"@types/emscripten": ["@types/emscripten@1.40.1", "", {}, "sha512-sr53lnYkQNhjHNN0oJDdUm5564biioI5DuOpycufDVK7D3y+GR3oUswe2rlwY1nPNyusHbrJ9WoTyIHl4/Bpwg=="],
|
||||
|
||||
"@types/filesystem": ["@types/filesystem@0.0.36", "", { "dependencies": { "@types/filewriter": "*" } }, "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA=="],
|
||||
|
||||
"@types/filewriter": ["@types/filewriter@0.0.33", "", {}, "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g=="],
|
||||
|
||||
"@types/har-format": ["@types/har-format@1.2.16", "", {}, "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A=="],
|
||||
|
||||
"@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
|
||||
|
||||
"@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="],
|
||||
|
||||
"@types/webextension-polyfill": ["@types/webextension-polyfill@0.12.3", "", {}, "sha512-F58aDVSeN/MjUGazXo/cPsmR76EvqQhQ1v4x23hFjUX0cfAJYE+JBWwiOGW36/VJGGxoH74sVlRIF3z7SJCKyg=="],
|
||||
|
||||
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||
|
||||
"archiver": ["archiver@7.0.1", "", { "dependencies": { "archiver-utils": "^5.0.2", "async": "^3.2.4", "buffer-crc32": "^1.0.0", "readable-stream": "^4.0.0", "readdir-glob": "^1.1.2", "tar-stream": "^3.0.0", "zip-stream": "^6.0.1" } }, "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ=="],
|
||||
|
||||
"archiver-utils": ["archiver-utils@5.0.2", "", { "dependencies": { "glob": "^10.0.0", "graceful-fs": "^4.2.0", "is-stream": "^2.0.1", "lazystream": "^1.0.0", "lodash": "^4.17.15", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA=="],
|
||||
|
||||
"async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
|
||||
|
||||
"b4a": ["b4a@1.6.7", "", {}, "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg=="],
|
||||
|
||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"bare-events": ["bare-events@2.6.1", "", {}, "sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g=="],
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||
|
||||
"browser-namespace": ["browser-namespace@1.4.0", "", { "dependencies": { "@types/filesystem": "*", "@types/har-format": "*", "@types/webextension-polyfill": "*" } }, "sha512-9b4yNTNs+8HVPssSq8RSZMRunf+G4cVQ2PMtOTn+uEVFOW5C0Uo+eGXuJ5LfxS1UDph5oAdWj92thPyxVhpqXg=="],
|
||||
|
||||
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||
|
||||
"buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
|
||||
|
||||
"chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"compress-commons": ["compress-commons@6.0.2", "", { "dependencies": { "crc-32": "^1.2.0", "crc32-stream": "^6.0.0", "is-stream": "^2.0.1", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg=="],
|
||||
|
||||
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
||||
|
||||
"crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="],
|
||||
|
||||
"crc32-stream": ["crc32-stream@6.0.0", "", { "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^4.0.0" } }, "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
|
||||
|
||||
"electrobun": ["electrobun@0.0.19-beta.111", "", { "dependencies": { "@oneidentity/zstd-js": "^1.0.3", "archiver": "^7.0.1", "rpc-anywhere": "1.5.0", "tar": "^6.2.1" }, "bin": { "electrobun": "bin/electrobun.cjs" } }, "sha512-Sijyfi2CqSqh5NUnRPWMrjb/wHOs9sohFP5kEsVhf7eNJIS++/advXi9VkGu6lhhBXTnraJq/ED8UBOo1WUzbw=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
|
||||
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
|
||||
|
||||
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
||||
|
||||
"fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],
|
||||
|
||||
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="],
|
||||
|
||||
"glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
||||
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||
|
||||
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||
|
||||
"lazystream": ["lazystream@1.0.1", "", { "dependencies": { "readable-stream": "^2.0.5" } }, "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw=="],
|
||||
|
||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
|
||||
"lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
|
||||
|
||||
"minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
|
||||
|
||||
"minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
|
||||
|
||||
"mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
|
||||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||
|
||||
"process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
|
||||
|
||||
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
|
||||
|
||||
"readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
|
||||
|
||||
"readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "^5.1.0" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="],
|
||||
|
||||
"rpc-anywhere": ["rpc-anywhere@1.5.0", "", { "dependencies": { "browser-namespace": "^1.4.0" } }, "sha512-ZYrB0foAM4oE7oBnUH3BL7LwtW9d6+RkzL/rFnjj8GCaFt5c81Rbw6oVl6u9AMsGONsKeJX0mL62TpbPXSO6og=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"streamx": ["streamx@2.22.1", "", { "dependencies": { "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" }, "optionalDependencies": { "bare-events": "^2.2.0" } }, "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA=="],
|
||||
|
||||
"string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||
|
||||
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
|
||||
"strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
|
||||
|
||||
"tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
|
||||
|
||||
"text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="],
|
||||
|
||||
"undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||
|
||||
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||
|
||||
"zip-stream": ["zip-stream@6.0.1", "", { "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", "readable-stream": "^4.0.0" } }, "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA=="],
|
||||
|
||||
"fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||
|
||||
"glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
||||
|
||||
"lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||
|
||||
"path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
||||
|
||||
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
|
||||
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
|
||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
}
|
||||
}
|
||||
BIN
bun.lockb
Normal file
BIN
bun.lockb
Normal file
Binary file not shown.
23
electrobun.config.ts
Normal file
23
electrobun.config.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
export default {
|
||||
app: {
|
||||
name: "gameflow-deck",
|
||||
identifier: "simeonradivoev.gameflow-deck.app",
|
||||
version: "0.0.1",
|
||||
},
|
||||
build: {
|
||||
// Vite builds to dist/, we copy from there
|
||||
copy: {
|
||||
"dist/index.html": "views/mainview/index.html",
|
||||
"dist/assets": "views/mainview/assets",
|
||||
},
|
||||
mac: {
|
||||
bundleCEF: false,
|
||||
},
|
||||
linux: {
|
||||
bundleCEF: false,
|
||||
},
|
||||
win: {
|
||||
bundleCEF: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
36
package.json
Normal file
36
package.json
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"name": "gameflow-deck",
|
||||
"version": "1.0.0",
|
||||
"description": "Game Launcher",
|
||||
"scripts": {
|
||||
"dev": "bun run build && electrobun dev",
|
||||
"dev:hmr": "concurrently \"bun run hmr\" \"bun run dev\"",
|
||||
"build": "vite build && electrobun build",
|
||||
"build:canary": "vite build && electrobun build --env=canary",
|
||||
"build:stable": "vite build && electrobun build --env=stable",
|
||||
"hmr": "vite --port 5173",
|
||||
"start": "bun run dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-router": "^1.154.10",
|
||||
"@tanstack/router-plugin": "^1.154.10",
|
||||
"classnames": "^2.5.1",
|
||||
"electrobun": "latest",
|
||||
"gamepad.css": "^0.0.4",
|
||||
"lucide-react": "^0.562.0",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@tanstack/react-router-devtools": "^1.154.10",
|
||||
"@types/bun": "latest",
|
||||
"@types/react": "^19.2.9",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"concurrently": "^9.2.1",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.3.1"
|
||||
}
|
||||
}
|
||||
38
src/bun/index.ts
Normal file
38
src/bun/index.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { BrowserWindow, Updater } from "electrobun/bun";
|
||||
|
||||
const DEV_SERVER_PORT = 5173;
|
||||
const DEV_SERVER_URL = `http://localhost:${DEV_SERVER_PORT}/Dashboard`;
|
||||
|
||||
// Check if Vite dev server is running for HMR
|
||||
async function getMainViewUrl(): Promise<string> {
|
||||
const channel = await Updater.localInfo.channel();
|
||||
if (channel === "dev") {
|
||||
try {
|
||||
await fetch(DEV_SERVER_URL, { method: "HEAD" });
|
||||
console.log(`HMR enabled: Using Vite dev server at ${DEV_SERVER_URL}`);
|
||||
return DEV_SERVER_URL;
|
||||
} catch {
|
||||
console.log("Vite dev server not running. Run 'bun run dev:hmr' for HMR support.");
|
||||
}
|
||||
}
|
||||
return "views://mainview/index.html";
|
||||
}
|
||||
|
||||
// Create the main application window
|
||||
const url = await getMainViewUrl();
|
||||
|
||||
const mainWindow = new BrowserWindow({
|
||||
title: "GameFlow",
|
||||
url,
|
||||
styleMask: {
|
||||
Borderless: true,
|
||||
},
|
||||
frame: {
|
||||
width: 1280,
|
||||
height: 800,
|
||||
x: 200,
|
||||
y: 200,
|
||||
},
|
||||
});
|
||||
|
||||
console.log("React Tailwind Vite app started!");
|
||||
21
src/mainview/components/Clock.tsx
Normal file
21
src/mainview/components/Clock.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
|
||||
export default function Clock() {
|
||||
const locale = "en";
|
||||
const [today, setDate] = useState(new Date());
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setDate(new Date());
|
||||
}, 60 * 1000);
|
||||
|
||||
return () => {
|
||||
clearInterval(timer);
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<div className="flex font-semibold gap-2 items-center">
|
||||
{today.toLocaleTimeString(locale, { hour: "numeric", minute: "numeric" })}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
23
src/mainview/components/GamepadIcon.tsx
Normal file
23
src/mainview/components/GamepadIcon.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import React from "react";
|
||||
|
||||
export default function GamepadIcon({
|
||||
platform,
|
||||
variant,
|
||||
button,
|
||||
text,
|
||||
}: {
|
||||
platform: "xbox" | "playstation" | "nintendo";
|
||||
variant: string;
|
||||
button: string;
|
||||
text?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="gamepad-button-wrapper">
|
||||
<i
|
||||
className={`gamepad-button gamepad-button-${platform} gamepad-button-${platform}--${button} gamepad-button-${platform}--variant-${variant}`}
|
||||
>
|
||||
{text}
|
||||
</i>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
23
src/mainview/index.css
Normal file
23
src/mainview/index.css
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--color-dark: #333333;
|
||||
--color-light: #464646;
|
||||
--color-light: #828282;
|
||||
--color-light2: #bcbcbc;
|
||||
--color-primary: #E5FF00;
|
||||
--color-alt: #4656E6;
|
||||
--color-alert: #E60012;
|
||||
}
|
||||
|
||||
html {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
body {
|
||||
}
|
||||
|
||||
#root {
|
||||
}
|
||||
12
src/mainview/index.html
Normal file
12
src/mainview/index.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>GameFlow</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
30
src/mainview/index.tsx
Normal file
30
src/mainview/index.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import "./index.css";
|
||||
import { createRouter, RouterProvider } from "@tanstack/react-router";
|
||||
import { routeTree } from "./routeTree.gen";
|
||||
|
||||
// Set up a Router instance
|
||||
const router = createRouter({
|
||||
routeTree,
|
||||
defaultPreload: "intent",
|
||||
scrollRestoration: true,
|
||||
});
|
||||
|
||||
// Register things for typesafety
|
||||
declare module "@tanstack/react-router" {
|
||||
interface Register {
|
||||
router: typeof router;
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById("root")!;
|
||||
|
||||
if (!rootElement.innerHTML) {
|
||||
const root = createRoot(rootElement);
|
||||
root.render(
|
||||
<StrictMode>
|
||||
<RouterProvider router={router} />
|
||||
</StrictMode>,
|
||||
);
|
||||
}
|
||||
77
src/mainview/routeTree.gen.ts
Normal file
77
src/mainview/routeTree.gen.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
// This file was automatically generated by TanStack Router.
|
||||
// You should NOT make any changes in this file as it will be overwritten.
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as GameDetailsRouteImport } from './routes/GameDetails'
|
||||
import { Route as DashboardRouteImport } from './routes/Dashboard'
|
||||
|
||||
const GameDetailsRoute = GameDetailsRouteImport.update({
|
||||
id: '/GameDetails',
|
||||
path: '/GameDetails',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const DashboardRoute = DashboardRouteImport.update({
|
||||
id: '/Dashboard',
|
||||
path: '/Dashboard',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/Dashboard': typeof DashboardRoute
|
||||
'/GameDetails': typeof GameDetailsRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/Dashboard': typeof DashboardRoute
|
||||
'/GameDetails': typeof GameDetailsRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/Dashboard': typeof DashboardRoute
|
||||
'/GameDetails': typeof GameDetailsRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths: '/Dashboard' | '/GameDetails'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to: '/Dashboard' | '/GameDetails'
|
||||
id: '__root__' | '/Dashboard' | '/GameDetails'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
DashboardRoute: typeof DashboardRoute
|
||||
GameDetailsRoute: typeof GameDetailsRoute
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/GameDetails': {
|
||||
id: '/GameDetails'
|
||||
path: '/GameDetails'
|
||||
fullPath: '/GameDetails'
|
||||
preLoaderRoute: typeof GameDetailsRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/Dashboard': {
|
||||
id: '/Dashboard'
|
||||
path: '/Dashboard'
|
||||
fullPath: '/Dashboard'
|
||||
preLoaderRoute: typeof DashboardRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
DashboardRoute: DashboardRoute,
|
||||
GameDetailsRoute: GameDetailsRoute,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>()
|
||||
225
src/mainview/routes/Dashboard.tsx
Normal file
225
src/mainview/routes/Dashboard.tsx
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
Plus,
|
||||
Search,
|
||||
Settings,
|
||||
Power,
|
||||
Sun,
|
||||
Wifi,
|
||||
BatteryFull,
|
||||
Gamepad2,
|
||||
Bluetooth,
|
||||
Settings2,
|
||||
Bell,
|
||||
HardDrive,
|
||||
} from "lucide-react";
|
||||
import { createFileRoute, Link, linkOptions } from "@tanstack/react-router";
|
||||
import "gamepad.css/styles.min.css";
|
||||
import GamepadIcon from "../components/GamepadIcon";
|
||||
import Clock from "../components/Clock";
|
||||
import classNames from "classnames";
|
||||
|
||||
export const Route = createFileRoute("/Dashboard")({
|
||||
component: ConsoleHomeUI,
|
||||
});
|
||||
|
||||
const games = [
|
||||
{
|
||||
title: "The Legend of Zelda",
|
||||
subtitle: "Link's Awakening",
|
||||
},
|
||||
{
|
||||
title: "Captain Toad",
|
||||
subtitle: "Treasure Tracker",
|
||||
focused: true,
|
||||
},
|
||||
{
|
||||
title: "Crash Bandicoot",
|
||||
subtitle: "N. Sane Trilogy",
|
||||
},
|
||||
{
|
||||
title: "Super Mario",
|
||||
subtitle: "Odyssey",
|
||||
},
|
||||
{
|
||||
title: "Animal Crossing",
|
||||
subtitle: "New Horizons",
|
||||
},
|
||||
];
|
||||
|
||||
export default function ConsoleHomeUI() {
|
||||
const [focus, setFocus] = useState(1);
|
||||
|
||||
useEffect(() => {
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.key === "ArrowRight")
|
||||
setFocus((i) => Math.min(i + 1, games.length - 1));
|
||||
if (e.key === "ArrowLeft") setFocus((i) => Math.max(i - 1, 0));
|
||||
};
|
||||
window.addEventListener("keydown", onKey);
|
||||
return () => window.removeEventListener("keydown", onKey);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full h-full flex flex-col overflow-hidden justify-around"
|
||||
style={{
|
||||
background: `linear-gradient(
|
||||
color-mix(in srgb, var(--color-dark) 60%, transparent),
|
||||
color-mix(in srgb, var(--color-dark) 60%, transparent)
|
||||
), url(https://picsum.photos/id/${10 + focus}/1920/1080.webp?blur=10)`,
|
||||
backgroundSize: "cover",
|
||||
}}
|
||||
>
|
||||
{/* Top bar */}
|
||||
<header className="h-14 px-6 mt-2 flex items-center justify-between text-white">
|
||||
<div className="flex items-center gap-3 drop-shadow-sm">
|
||||
<div className="w-16 h-16 rounded-full bg-alert" />
|
||||
<div className="w-16 h-16 rounded-full bg-cyan-500 ring-4 ring-primary" />
|
||||
<button className="w-16 h-16 rounded-full bg-dark flex items-center justify-center">
|
||||
<Plus className="w-8 h-8" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-5 text drop-shadow-sm">
|
||||
<Clock />
|
||||
<Wifi className="w-6 h-6" />
|
||||
<Bluetooth className="w-6 h-6" />
|
||||
<Bell className="w-6 h-6" />
|
||||
<div className="flex gap-2 items-center">
|
||||
<BatteryFull className="w-6 h-6" />
|
||||
<span className="font-semibold">100%</span>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<div className="w-16 h-16 rounded-full flex items-center justify-center text-dark bg-white">
|
||||
<Sun className="w-8 h-8" />
|
||||
</div>
|
||||
<div className="w-16 h-16 rounded-full flex items-center justify-center text-dark bg-white">
|
||||
<Power className="w-8 h-8" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Filter bar */}
|
||||
<div className="flex items-center justify-center px-8 gap-2 py-3 drop-shadow-sm">
|
||||
<button className="flex w-14 h-14 items-center justify-center bg-dark rounded-full text-white">
|
||||
<Settings2 className="w-5 h-5" />
|
||||
</button>
|
||||
<div className="flex bg-dark rounded-full p-1">
|
||||
<button className="px-4 h-12 rounded-full text-white/70">All</button>
|
||||
<button className="px-4 h-12 rounded-full bg-primary drop-shadow-sm text-black font-bold">
|
||||
Digital
|
||||
</button>
|
||||
<button className="px-4 h-12 rounded-full text-white/70">
|
||||
Physical
|
||||
</button>
|
||||
</div>
|
||||
<button className="flex w-14 h-14 items-center justify-center bg-dark rounded-full text-white">
|
||||
<Search className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Game carousel */}
|
||||
<main
|
||||
className="flex w-full px-8 py-4 overflow-x-scroll items-center gap-6"
|
||||
style={{ scrollbarWidth: "none" }}
|
||||
>
|
||||
{games.map((g, i) => {
|
||||
const focused = i === focus;
|
||||
return (
|
||||
<div
|
||||
key={g.title}
|
||||
className={classNames(
|
||||
`min-w-64 h-82 rounded-2xl bg-dark flex flex-col justify-end overflow-hidden transition-all duration-200 drop-shadow-md`,
|
||||
{
|
||||
"ring-7 ring-primary scale-105": focused,
|
||||
"drop-shadow-lg": focused,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="flex-1 bg-white p-4"
|
||||
style={{
|
||||
backgroundImage: `url(https://picsum.photos/id/${10 + i}/300/300.webp)`,
|
||||
}}
|
||||
></div>
|
||||
<div className="h-0 flex pr-2 justify-end items-center">
|
||||
<div className="flex rounded-full bg-white w-10 h-10 justify-center items-center text-dark drop-shadow-sm">
|
||||
<HardDrive className="w-6 h-6" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col p-4 pt-6 text-light2">
|
||||
<div className="text-xl font-bold">{g.title}</div>
|
||||
<div className="text-s">{g.subtitle}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</main>
|
||||
|
||||
{/* Menu */}
|
||||
|
||||
<div className="flex w-full items-center justify-center gap-3">
|
||||
<CircleIcon
|
||||
to={linkOptions({
|
||||
to: "/Dashboard",
|
||||
})}
|
||||
label="Home"
|
||||
active
|
||||
/>
|
||||
<CircleIcon label="News" />
|
||||
<CircleIcon label="Shop" />
|
||||
<CircleIcon label="Album" />
|
||||
<CircleIcon label="Controllers" />
|
||||
<CircleIcon label="Settings" highlight />
|
||||
<span className="flex items-center rounded-full bg-primary text-dark px-4 py-2 font-semibold">
|
||||
Settings
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Bottom bar */}
|
||||
<footer className="px-8 flex flex-col items-center justify-between text-light2">
|
||||
<div className="flex gap-2 text-sm text-light2">
|
||||
<span className="flex gap-2 bg-dark pl-2 pr-3 py-1.5 rounded-full items-center text-lg font-semibold drop-shadow-sm">
|
||||
<GamepadIcon platform="xbox" variant="one" button="a" text="a" />
|
||||
Continue
|
||||
</span>
|
||||
<span className="flex gap-2 bg-dark pl-2 pr-3 py-1.5 rounded-full items-center text-lg font-semibold drop-shadow-sm">
|
||||
<GamepadIcon platform="xbox" variant="one" button="b" text="b" />
|
||||
Back
|
||||
</span>
|
||||
<span className="flex gap-2 bg-dark pl-2 pr-3 py-1.5 rounded-full items-center text-lg font-semibold drop-shadow-sm">
|
||||
<GamepadIcon platform="xbox" variant="one" button="x" text="x" />
|
||||
Close
|
||||
</span>
|
||||
<span className="flex gap-2 bg-dark pl-2 pr-3 py-1.5 rounded-full items-center text-lg font-semibold drop-shadow-sm">
|
||||
<GamepadIcon platform="xbox" variant="one" button="y" text="y" />
|
||||
Options
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CircleIcon({
|
||||
to,
|
||||
active,
|
||||
highlight,
|
||||
}: {
|
||||
to?: any;
|
||||
active?: boolean;
|
||||
highlight?: boolean;
|
||||
label?: string;
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
{...to}
|
||||
className={`w-20 h-20 rounded-full flex items-center justify-center text-dark drop-shadow-lg
|
||||
${highlight === true ? "bg-primary" : active === true ? "bg-alert text-white" : "bg-white"}`}
|
||||
>
|
||||
<Gamepad2 className="w-10 h-10" />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
110
src/mainview/routes/GameDetails.tsx
Normal file
110
src/mainview/routes/GameDetails.tsx
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Bell, Library, Store, Settings, Gamepad2 } from "lucide-react";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
const games = [
|
||||
"Halo Infinite",
|
||||
"Cyberpunk",
|
||||
"Hades",
|
||||
"Stardew Valley",
|
||||
"Neon Skies",
|
||||
"Void Runner",
|
||||
"Rogue Light",
|
||||
"Drift City",
|
||||
];
|
||||
|
||||
export const Route = createFileRoute("/GameDetails")({
|
||||
loader: ({ params }) => params.postId,
|
||||
component: GameDetailsUI,
|
||||
});
|
||||
|
||||
export function GameDetailsUI() {
|
||||
// In a component!
|
||||
const { postId } = Route.useParams();
|
||||
|
||||
return (
|
||||
<main className="flex-1 p-10 flex flex-col gap-10">
|
||||
{/* Header */}
|
||||
<header className="flex items-start justify-between">
|
||||
<div>
|
||||
<div className="text-sm text-slate-400">Now Playing</div>
|
||||
<h1 className="text-3xl font-semibold text-cyan-400">
|
||||
Halo Infinite
|
||||
</h1>
|
||||
<div className="mt-2 text-slate-400 text-sm">
|
||||
Action · FPS · Sci-Fi
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 text-slate-300">
|
||||
<Bell className="w-5 h-5" />
|
||||
<span className="text-sm">3</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Content split */}
|
||||
<section className="flex gap-10 flex-1">
|
||||
{/* Cover / media */}
|
||||
<div className="w-[360px] shrink-0">
|
||||
<div className="relative h-[480px] rounded-3xl bg-gradient-to-br from-slate-700/60 to-slate-900/90 ring-4 ring-cyan-400/80 shadow-[0_0_50px_rgba(34,211,238,0.6)]" />
|
||||
|
||||
{/* Primary action */}
|
||||
<button className="mt-6 w-full rounded-xl bg-cyan-400 text-black font-semibold py-3 text-lg shadow-[0_0_30px_rgba(34,211,238,0.6)]">
|
||||
▶ Play
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Details */}
|
||||
<div className="flex-1 flex flex-col gap-6">
|
||||
{/* Description */}
|
||||
<p className="text-slate-300 leading-relaxed max-w-3xl">
|
||||
Experience the epic sci-fi saga and master chief’s greatest journey
|
||||
yet. Explore vast open worlds, engage in tactical combat, and
|
||||
uncover the mysteries of Zeta Halo.
|
||||
</p>
|
||||
|
||||
{/* Metadata */}
|
||||
<div className="grid grid-cols-2 gap-6 max-w-3xl">
|
||||
<Detail label="Developer" value="343 Industries" />
|
||||
<Detail label="Publisher" value="Xbox Game Studios" />
|
||||
<Detail label="Release" value="Dec 8, 2021" />
|
||||
<Detail label="Playtime" value="42 hours" />
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-4 mt-4">
|
||||
<SecondaryButton label="Achievements" />
|
||||
<SecondaryButton label="DLC" />
|
||||
<SecondaryButton label="Settings" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer hints */}
|
||||
<footer className="text-sm text-slate-400 flex gap-6">
|
||||
<span>A Play</span>
|
||||
<span>B Back</span>
|
||||
<span>Y Options</span>
|
||||
</footer>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
function Detail({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<div>
|
||||
<div className="text-xs uppercase tracking-wide text-slate-500">
|
||||
{label}
|
||||
</div>
|
||||
<div className="text-slate-200 text-sm mt-1">{value}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SecondaryButton({ label }: { label: string }) {
|
||||
return (
|
||||
<button className="px-5 py-3 rounded-xl bg-white/5 hover:bg-white/10 text-slate-200">
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
16
src/mainview/routes/__root.tsx
Normal file
16
src/mainview/routes/__root.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { Link, Outlet, createRootRoute } from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||
import { Gamepad2, Library, Settings, Store } from "lucide-react";
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: RootComponent,
|
||||
});
|
||||
|
||||
function RootComponent() {
|
||||
return (
|
||||
<div className="w-screen h-screen overflow-hidden">
|
||||
<Outlet />
|
||||
<TanStackRouterDevtools position="bottom-right" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
5
tailwind.config.js
Normal file
5
tailwind.config.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./src/mainview/**/*.{html,js,ts,jsx,tsx}"],
|
||||
plugins: [],
|
||||
}
|
||||
20
tsconfig.json
Normal file
20
tsconfig.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src", "vite.config.ts"]
|
||||
}
|
||||
26
vite.config.ts
Normal file
26
vite.config.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import { tanstackRouter } from '@tanstack/router-plugin/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
tanstackRouter({
|
||||
target: 'react',
|
||||
routesDirectory: "./routes/",
|
||||
generatedRouteTree: "./routeTree.gen.ts",
|
||||
autoCodeSplitting: true,
|
||||
}),
|
||||
react(),
|
||||
],
|
||||
root: "src/mainview",
|
||||
build: {
|
||||
outDir: "../../dist",
|
||||
emptyOutDir: true,
|
||||
},
|
||||
server: {
|
||||
port: 5173,
|
||||
strictPort: true,
|
||||
},
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue