commit ba68c17898d47f641a65ec28ff8a50d4dd8f649b Author: AnthonyDuan Date: Tue Jul 28 13:05:53 2026 +0800 Initial taiko.su easter-egg site diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..a30e3af --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,3 @@ +{ + "postCreateCommand": "" +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8f8a142 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +/.git +/__pycache__ +**/__pycache__ +/.venv +/venv +/env +/backups +/.taiko-secret-key +/flask_session +/public/songs +*.pyc +*.pyo +*.log +debug-*.png diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bdb0cab --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..0b1ad1d --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,3 @@ + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b61db37 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +__pycache__/ +*.pyc +.DS_Store +.venv/ +.taiko-secret-key +flask_session/ +build/ +dist/ + +# build/version artifacts +version.json + +# large assets not meant for VCS +public/songs/ +public/songs/** +public/preview/ +*.log + +# local editor project artifacts +taiko-editor/.venv/ +taiko-editor/build/ +taiko-editor/dist/ +taiko-editor/__pycache__/ +taiko-editor/build_log.txt +taiko-editor/run_log.txt diff --git a/.trae/documents/Optimize Loading with Multi-Process Workers.md b/.trae/documents/Optimize Loading with Multi-Process Workers.md new file mode 100644 index 0000000..c1d42ec --- /dev/null +++ b/.trae/documents/Optimize Loading with Multi-Process Workers.md @@ -0,0 +1,20 @@ +I will optimize the loading process by implementing a **Multi-Threaded Worker Loader**. This involves creating a pool of Web Workers to fetch assets (JavaScript, Audio, Images, Views) in parallel, offloading the network initiation and handling from the main thread. + +### Plan: +1. **Create `public/src/js/loader-worker.js`**: + * This worker will handle `fetch` requests for different resource types (`text`, `blob`, `arraybuffer`). + * It will transfer the data back to the main thread (using zero-copy transfer for `ArrayBuffer`). + +2. **Modify `public/src/js/loader.js`**: + * **Initialize Worker Pool**: Create a pool of workers (defaulting to 4) in the `Loader` class. + * **Implement `workerFetch(url, type)`**: A method to distribute fetch tasks to the worker pool. + * **Override `ajax(url, ...)`**: Intercept requests for static assets (`src/`, `assets/`, etc.) and route them through `workerFetch`. Keep API calls (`api/`) on the main thread to ensure session stability. + * **Update `loadScript(url)`**: Change it to fetch the script content via `workerFetch` and inject it using a ` + + + + + + + + +
+
+
+
taiko.su
+
+
Loading
+
+
+
+ {% if version.version and version.commit_short and version.commit %} + vSU? ??.??.?? + {% else %} + vSU? ??.??.?? + {% endif %} +
+ + + + + + diff --git a/tests/test_pageevents.js b/tests/test_pageevents.js new file mode 100644 index 0000000..9925ed0 --- /dev/null +++ b/tests/test_pageevents.js @@ -0,0 +1,75 @@ +const assert = require("node:assert/strict") +const fs = require("node:fs") +const path = require("node:path") +const vm = require("node:vm") + +class EventTargetStub { + constructor() { + this.listeners = new Map() + } + + addEventListener(type, callback) { + if (!this.listeners.has(type)) this.listeners.set(type, new Set()) + this.listeners.get(type).add(callback) + } + + removeEventListener(type, callback) { + if (this.listeners.has(type)) this.listeners.get(type).delete(callback) + } + + dispatch(type, event) { + for (const callback of Array.from(this.listeners.get(type) || [])) callback(event) + } + + listenerCount(type) { + return this.listeners.has(type) ? this.listeners.get(type).size : 0 + } +} + +const root = path.resolve(__dirname, "..") +const source = fs.readFileSync(path.join(root, "public/src/js/pageevents.js"), "utf8") +const window = new EventTargetStub() +const context = vm.createContext({window, Map, Date}) +vm.runInContext(source + "; this.PageEvents = PageEvents", context) + +const pageEvents = new context.PageEvents() +let keyboardCalls = 0 +let firstOverlayCalls = 0 +let secondOverlayCalls = 0 + +pageEvents.keyAdd({}, "all", "both", () => keyboardCalls++) +assert.equal(window.listenerCount("keydown"), 1) + +pageEvents.add(window, "keydown", () => firstOverlayCalls++, "library-escape") +assert.equal(window.listenerCount("keydown"), 2, "a named overlay listener must not replace the global keyboard dispatcher") + +const keydown = { + key: "d", + keyCode: 68, + type: "keydown", + target: {tagName: "DIV"}, + ctrlKey: false, + preventDefault() {} +} +window.dispatch("keydown", keydown) +assert.equal(keyboardCalls, 1) +assert.equal(firstOverlayCalls, 1) + +pageEvents.add(window, "keydown", () => secondOverlayCalls++, "library-escape") +assert.equal(window.listenerCount("keydown"), 2, "replacing a named listener must not create duplicates") +window.dispatch("keydown", keydown) +assert.equal(keyboardCalls, 2) +assert.equal(firstOverlayCalls, 1) +assert.equal(secondOverlayCalls, 1) + +pageEvents.remove(window, "keydown", "library-escape") +assert.equal(window.listenerCount("keydown"), 1) +window.dispatch("keydown", keydown) +assert.equal(keyboardCalls, 3, "removing an overlay listener must leave keyboard input active") + +let keyupCalls = 0 +pageEvents.keyAdd({}, "all", "up", () => keyupCalls++) +window.dispatch("keyup", {...keydown, type: "keyup"}) +assert.equal(keyupCalls, 1) + +console.log("page event listener isolation ok") diff --git a/update.sh b/update.sh new file mode 100644 index 0000000..2c8914e --- /dev/null +++ b/update.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +case "$0" in + */*) SCRIPT_PATH="$0" ;; + *) SCRIPT_PATH="./$0" ;; +esac + +SCRIPT_DIR=$(CDPATH= cd -- "${SCRIPT_PATH%/*}" && pwd) +INSTALL_DIR=${INSTALL_DIR:-/srv/taiko-web} +UPDATE_MODE=${TAIKO_WEB_UPDATE_MODE:-auto} + +if [ ! -r "$SCRIPT_DIR/setup.sh" ]; then + echo "Update source is incomplete: $SCRIPT_DIR/setup.sh is missing or unreadable." >&2 + exit 1 +fi + +detect_update_mode() { + if command -v docker >/dev/null 2>&1 && + docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^taiko-web-app$'; then + echo container + return + fi + + if command -v systemctl >/dev/null 2>&1 && + systemctl is-active --quiet taiko-web 2>/dev/null; then + echo direct + return + fi + + if command -v docker >/dev/null 2>&1 && + docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q '^taiko-web-app$'; then + echo container + return + fi + + if command -v systemctl >/dev/null 2>&1 && + { systemctl is-enabled --quiet taiko-web 2>/dev/null || + [ -f /etc/systemd/system/taiko-web.service ]; }; then + echo direct + return + fi + + if command -v docker >/dev/null 2>&1 && [ -f "$INSTALL_DIR/docker-compose.yml" ]; then + echo container + return + fi + + # Preserve the historical behavior for installations that cannot be detected. + echo container +} + +case "$UPDATE_MODE" in + auto) UPDATE_MODE=$(detect_update_mode) ;; + container|direct) ;; + *) + echo "Invalid TAIKO_WEB_UPDATE_MODE: $UPDATE_MODE (expected auto, container, or direct)" + exit 2 + ;; +esac + +echo "[taiko-web] Detected update mode: $UPDATE_MODE" +UPDATE_ACTION="upgrade-$UPDATE_MODE" +if [ "${TAIKO_WEB_UPDATE_DRY_RUN:-0}" = "1" ]; then + printf '[taiko-web] Dry run: %s %s %s' "${BASH:-bash}" "$SCRIPT_DIR/setup.sh" "$UPDATE_ACTION" + if [ "$#" -gt 0 ]; then + printf ' %s' "$@" + fi + printf '\n' + exit 0 +fi + +exec "${BASH:-bash}" "$SCRIPT_DIR/setup.sh" "$UPDATE_ACTION" "$@"