diff --git a/.gitattributes b/.gitattributes index b93aa36b..8d98d174 100644 --- a/.gitattributes +++ b/.gitattributes @@ -48,3 +48,4 @@ *.mp3 filter=lfs diff=lfs merge=lfs -text *.wav filter=lfs diff=lfs merge=lfs -text *.ogg filter=lfs diff=lfs merge=lfs -text +Unity/**/ProjectSettings/ProjectVersion.txt text eol=lf -filter -diff -merge diff --git a/.github/workflows/build-unity-spacecraft.yml b/.github/workflows/build-unity-spacecraft.yml new file mode 100644 index 00000000..48910d6d --- /dev/null +++ b/.github/workflows/build-unity-spacecraft.yml @@ -0,0 +1,345 @@ +################################################################################################### +# Build Unity SpaceCraft – GitHub Actions (Ubuntu runner) +# +# Overview +# - Builds the Unity SpaceCraft project for WebGL on GitHub-hosted Linux runners. +# - Auto-detects Unity version from ProjectVersion.txt (override via input). +# - Supports Production/Development profiles or a fully custom build method. +# - Caches the Unity Library folder for faster imports/compiles between runs. +# - Downloads real LFS asset blobs for the checked-out commit (required for Unity). +# - Uploads build artifacts for later download/deploy. +# +# Requirements (repo secrets) +# - UNITY_LICENSE: Unity license file contents (game-ci format) or ULF text. +# - UNITY_EMAIL: Unity account email (for legacy activation flows in builder). +# - UNITY_PASSWORD: Unity account password (for legacy activation flows in builder). +# +# Inputs (workflow_dispatch) +# - unityVersion: Unity Editor version (e.g., 2022.3.45f1). Use 'auto' to parse ProjectVersion.txt. +# - projectPath: Path to the Unity project. Defaults to Unity/SpaceCraft. +# - targetPlatform: Build target (WebGL). Extendable if needed. +# - buildProfile: Convenience selector mapping to build methods (Production/Development). +# - buildMethod: Explicit Unity C# method string, overrides buildProfile. Example: +# SpaceCraft.Editor.Builds.BuildWebGLProduction +# +# Build method expectations (Unity side) +# - The selected build method should be a static C# function callable by Unity with: +# - No arguments (or signature supported by Unity batchmode). +# - It should configure build options and write the WebGL build output into the project Build/ path +# or another deterministic directory consumed by artifact upload below. +# - Example namespace/class/method naming is provided here as a convention; implement accordingly. +# +# Caching strategy +# - Caches the Library/ folder per (OS, Unity version, Packages/manifest.json hash) to reduce reimport. +# - On cache hits, compilations and imports should be much faster. +# - We intentionally do not cache Temp/ or final Build/ outputs; those are uploaded as artifacts. +# +# LFS and checkout +# - actions/checkout is configured with lfs: true and fetch-depth: 1 +# - Ensures real LFS blobs for the current commit are available to the build. +# - Uses shallow history for speed (no need for full Git history in CI). +# +# Artifacts +# - Uploads the default game-ci build/ directory and the project Build/ directory. +# - Adjust artifact paths if your build method writes to a different location. +# +# Invocation examples +# - Production (auto-detect Unity, default project path): +# unityVersion: auto +# buildProfile: WebGLProductionBuild +# - Development: +# unityVersion: auto +# buildProfile: WebGLDevelopmentBuild +# - Explicit method (overrides profile mapping): +# buildMethod: SpaceCraft.Editor.Builds.BuildWebGLProduction +# +# Notes +# - This workflow focuses on GitHub-hosted runners (no self-hosted required). +# - For further speed-ups, consider enabling editor caching in the action if appropriate and supported +# by your environment, or splitting content generation into a separate, cacheable step. +################################################################################################### +name: Build Unity SpaceCraft + +on: + workflow_dispatch: + inputs: + unityVersion: + description: "Unity Editor version (e.g. 2022.3.45f1). Use 'auto' to detect from ProjectVersion.txt" + required: false + default: "auto" + type: string + projectPath: + description: "Unity project path" + required: false + default: "Unity/SpaceCraft" + type: string + targetPlatform: + description: "Unity target platform" + required: false + default: "WebGL" + type: choice + options: + - WebGL + buildProfile: + description: "Build profile token (maps to a Unity build method)" + required: false + default: "WebGLProductionBuild" + type: choice + options: + - WebGLProductionBuild + - WebGLDevelopmentBuild + buildMethod: + description: "Explicit Unity build method (overrides buildProfile). Example: SpaceCraft.Editor.Builds.BuildWebGLProduction" + required: false + default: "" + type: string + +jobs: + build: + name: Build ${{ inputs.targetPlatform }} (${{ inputs.buildProfile }}) + runs-on: ubuntu-latest + timeout-minutes: 90 + + env: + PROJECT_PATH: ${{ inputs.projectPath }} + TARGET_PLATFORM: ${{ inputs.targetPlatform }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: true + fetch-depth: 1 + + - name: Ensure LFS files are materialized (smudged) + shell: bash + run: | + set -euo pipefail + echo "[lfs] git lfs version:" && git lfs version || true + echo "[lfs] Pulling LFS objects (broad)" + git lfs pull --exclude="" --include="" || true + echo "[lfs] Forcing checkout (smudge) of LFS pointers" + git lfs checkout || true + + - name: Detect Unity version and resolve project path + id: detect + shell: bash + run: | + set -euo pipefail + echo "[detect] GITHUB_WORKSPACE=${{ github.workspace }}" + echo "[detect] inputs.projectPath='${{ inputs.projectPath }}' inputs.unityVersion='${{ inputs.unityVersion }}'" + echo "[detect] Workspace root listing:" + ls -la "${{ github.workspace }}" || true + + # If version provided, honor it and still resolve project path for later steps + REQ_VER="${{ inputs.unityVersion }}" + PROJ_PATH_IN="${{ inputs.projectPath }}" + CANDIDATE_FILE="${{ github.workspace }}/${PROJ_PATH_IN}/ProjectSettings/ProjectVersion.txt" + echo "[detect] Initial candidate file: $CANDIDATE_FILE" + echo "[detect] Listing input project path: '${PROJ_PATH_IN}'" + ls -la "${{ github.workspace }}/${PROJ_PATH_IN}" || true + + if [[ ! -f "$CANDIDATE_FILE" ]]; then + echo "[detect] Input projectPath not found: $CANDIDATE_FILE" >&2 + echo "[detect] Searching for ProjectVersion.txt under workspace..." + MAPFILE -t FOUND < <(find "${{ github.workspace }}" -type f -path "*/ProjectSettings/ProjectVersion.txt" | sort) + echo "[detect] Found ${#FOUND[@]} candidates" + if [[ ${#FOUND[@]} -eq 0 ]]; then + echo "[detect] No ProjectVersion.txt found in repository" >&2 + exit 1 + fi + CANDIDATE_FILE="${FOUND[0]}" + echo "[detect] Using detected ProjectVersion.txt: $CANDIDATE_FILE" + echo "[detect] Candidates (up to 10):" && printf '%s\n' "${FOUND[@]:0:10}" + fi + + # Derive resolved project path (folder containing ProjectSettings) + RESOLVED_PROJECT_PATH="$(cd "$(dirname "$CANDIDATE_FILE")/.." && pwd)" + # Convert to path relative to workspace for downstream steps + RESOLVED_PROJECT_PATH_REL="${RESOLVED_PROJECT_PATH#"${{ github.workspace }}/"}" + echo "projectPath=$RESOLVED_PROJECT_PATH_REL" >> "$GITHUB_OUTPUT" + echo "[detect] RESOLVED_PROJECT_PATH=$RESOLVED_PROJECT_PATH" + echo "[detect] RESOLVED_PROJECT_PATH_REL=$RESOLVED_PROJECT_PATH_REL" + echo "[detect] ProjectSettings listing:" && ls -la "$RESOLVED_PROJECT_PATH/ProjectSettings" || true + echo "[detect] cat ProjectVersion.txt (pre-smudge):" && cat "$CANDIDATE_FILE" || true + + # If file still looks like an LFS pointer, try to smudge just this file and re-check + if head -n 1 "$CANDIDATE_FILE" | grep -q '^version https://git-lfs.github.com/spec/v1'; then + echo "[detect] ProjectVersion.txt appears to be an LFS pointer; attempting smudge" + git lfs checkout -- "$CANDIDATE_FILE" || true + git lfs pull --include="$RESOLVED_PROJECT_PATH_REL/ProjectSettings/ProjectVersion.txt" --exclude="" || true + echo "[detect] cat ProjectVersion.txt (post-smudge):" && cat "$CANDIDATE_FILE" || true + fi + + if [[ "$REQ_VER" != "auto" && -n "$REQ_VER" ]]; then + echo "[detect] Using provided unityVersion: $REQ_VER" + echo "version=$REQ_VER" >> "$GITHUB_OUTPUT" + exit 0 + fi + + VERSION=$(grep -E "^m_EditorVersion:\s*" "$CANDIDATE_FILE" | sed -E 's/^m_EditorVersion:\s*([^[:space:]]+).*/\1/') + if [[ -z "$VERSION" ]]; then + echo "[detect] Failed to parse Unity version from ProjectVersion.txt" >&2 + exit 1 + fi + echo "[detect] Detected Unity version: $VERSION" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Log build configuration + shell: bash + run: | + echo "[config] projectPath='${{ steps.detect.outputs.projectPath }}'" + echo "[config] unityVersion='${{ steps.detect.outputs.version }}'" + echo "[config] targetPlatform='${{ inputs.targetPlatform }}'" + echo "[config] buildProfile='${{ inputs.buildProfile }}'" + + - name: Validate Unity activation secrets + shell: bash + run: | + set -euo pipefail + missing=0 + if [[ -z "${{ secrets.UNITY_LICENSE }}" ]]; then echo "[secrets] UNITY_LICENSE is missing" >&2; missing=1; fi + if [[ -z "${{ secrets.UNITY_EMAIL }}" ]]; then echo "[secrets] UNITY_EMAIL is missing" >&2; missing=1; fi + if [[ -z "${{ secrets.UNITY_PASSWORD }}" ]]; then echo "[secrets] UNITY_PASSWORD is missing" >&2; missing=1; fi + if [[ "$missing" -ne 0 ]]; then + echo "[secrets] One or more required secrets are missing. Per GameCI v4 Personal license activation, provide UNITY_LICENSE, UNITY_EMAIL, UNITY_PASSWORD." >&2 + exit 1 + fi + + - name: Cache Unity Library (project import cache) + uses: actions/cache@v4 + with: + path: | + ${{ steps.detect.outputs.projectPath }}/Library + key: ${{ runner.os }}-unity-library-${{ steps.detect.outputs.version }}-${{ hashFiles(format('{0}/Packages/manifest.json', steps.detect.outputs.projectPath)) }} + restore-keys: | + ${{ runner.os }}-unity-library-${{ steps.detect.outputs.version }}- + + - name: Compute build method + id: compute + shell: bash + run: | + set -euo pipefail + if [[ -n "${{ inputs.buildMethod }}" ]]; then + METHOD="${{ inputs.buildMethod }}" + else + case "${{ inputs.buildProfile }}" in + WebGLProductionBuild) + METHOD="Build.WebGL_Prod" + ;; + WebGLDevelopmentBuild) + METHOD="Build.WebGL_Dev" + ;; + *) + echo "Unknown buildProfile: ${{ inputs.buildProfile }}" >&2 + exit 1 + ;; + esac + fi + echo "buildMethod=$METHOD" >> "$GITHUB_OUTPUT" + echo "[compute] Selected build method: $METHOD" + + - name: Prepare logs directory + shell: bash + run: | + mkdir -p Logs + mkdir -p "${{ steps.detect.outputs.projectPath }}/Logs" + + # Do not pre-create dist; Unity writes under project path, we stage after + + - name: Unity - Builder + uses: game-ci/unity-builder@v4 + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} + with: + projectPath: ${{ steps.detect.outputs.projectPath }} + targetPlatform: ${{ inputs.targetPlatform }} + unityVersion: ${{ steps.detect.outputs.version }} + buildMethod: ${{ steps.compute.outputs.buildMethod }} + customParameters: -logFile Logs/Editor.log -stackTraceLogType Full + runAsHostUser: true + + + - name: Setup Node.js + if: success() + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install controller dependencies + if: success() + working-directory: WebSites/controller + shell: bash + run: | + set -euo pipefail + echo "[controller] PWD=$(pwd) contents:" && ls -la + if [[ -f package-lock.json ]]; then + echo "[controller] Using npm ci" + npm ci + else + echo "[controller] package-lock.json not found; using npm install" + npm install + fi + + - name: Install pnpm for build script + if: success() + working-directory: WebSites/controller + shell: bash + run: | + echo "[controller] Ensuring pnpm is available in $(pwd)" + npm i -g pnpm@8 + + - name: Build controller + if: success() + working-directory: WebSites/controller + shell: bash + run: | + echo "[controller] Building in $(pwd)" + npm run build + + - name: Assemble flat artifact (no wrapper directory) + if: success() + shell: bash + run: | + set -euo pipefail + STAGE="dist" + SRC_BUILDS="${{ steps.detect.outputs.projectPath }}/Builds/SpaceCraft" + echo "[assemble] Expecting Unity output at $SRC_BUILDS" + test -d "$SRC_BUILDS" || { echo "[assemble] ERROR: Unity output missing at $SRC_BUILDS" >&2; exit 1; } + rm -rf "$STAGE" + mkdir -p "$STAGE/SpaceCraft" "$STAGE/controller" + rsync -a "$SRC_BUILDS/" "$STAGE/SpaceCraft/" + # Top-level site index + if [[ -f WebSites/index.html ]]; then + cp -f WebSites/index.html "$STAGE/index.html" + fi + # Controller site + if [[ -f WebSites/controller/index.html ]]; then + cp -f WebSites/controller/index.html "$STAGE/controller/index.html" + fi + if [[ -d WebSites/controller/lib ]]; then + cp -R WebSites/controller/lib "$STAGE/controller/lib" + fi + if [[ -d WebSites/controller/build ]]; then + cp -R WebSites/controller/build "$STAGE/controller/build" + fi + echo "[assemble] Final artifact layout:" + (cd "$STAGE" && find . -maxdepth 2 -type f | sed 's#^./##') + + - name: Package site.zip (flat contents) + if: success() + shell: bash + run: | + set -euo pipefail + rm -f site.zip + (cd dist && zip -r ../site.zip .) + echo "[package] Created site.zip" + + - name: Upload site.zip artifact + if: success() + uses: actions/upload-artifact@v4 + with: + name: site + path: site.zip diff --git a/SvelteKit/BackSpace/package.json b/SvelteKit/BackSpace/package.json index 27642bee..fdd16a37 100644 --- a/SvelteKit/BackSpace/package.json +++ b/SvelteKit/BackSpace/package.json @@ -60,7 +60,8 @@ "unity:logs": "tsx scripts/unity-automation.js check-logs", "unity:start": "tsx scripts/unity-automation.js launch", "unity:openproject": "tsx scripts/unity-automation.js run openproject", - "unity:prebuild": "tsx scripts/unity-automation.js prebuild-webgl" + "unity:prebuild": "tsx scripts/unity-automation.js prebuild-webgl", + "trigger-build-unity-spacecraft": "tsx scripts/gh-trigger-build.ts" }, "dependencies": { "async-retry": "^1.3.3", diff --git a/SvelteKit/BackSpace/scripts/gh-trigger-build.ts b/SvelteKit/BackSpace/scripts/gh-trigger-build.ts new file mode 100644 index 00000000..d41357fd --- /dev/null +++ b/SvelteKit/BackSpace/scripts/gh-trigger-build.ts @@ -0,0 +1,79 @@ +/** + * Trigger Build Unity SpaceCraft GitHub Action + * + * This CLI fires the workflow_dispatch for .github/workflows/build-unity-spacecraft.yml + * and forwards parameters provided on the command line. + * + * Usage examples: + * npm run trigger-build-unity-spacecraft -- \ + * --unityVersion auto \ + * --projectPath "Unity/SpaceCraft" \ + * --targetPlatform WebGL \ + * --buildProfile WebGLProductionBuild \ + * --ref main + * + * npm run trigger-build-unity-spacecraft -- \ + * --unityVersion 2022.3.45f1 \ + * --buildMethod SpaceCraft.Editor.Builds.BuildWebGLDevelopment \ + * --ref my-feature-branch + */ + +import { Command } from 'commander'; +import { execSync } from 'node:child_process'; + +function ensureGhCliAvailable() { + try { + execSync('gh --version', { stdio: 'ignore' }); + } catch (err) { + console.error('gh CLI is required. Install from https://cli.github.com/'); + process.exit(1); + } +} + +const program = new Command(); +program + .option('--unityVersion ', 'Unity Editor version or "auto"', 'auto') + .option('--projectPath ', 'Unity project path', 'Unity/SpaceCraft') + .option('--targetPlatform ', 'Unity target platform', 'WebGL') + .option('--buildProfile ', 'Build profile token', 'WebGLProductionBuild') + .option('--buildMethod ', 'Explicit Unity build method (overrides buildProfile)', '') + .requiredOption('--ref ', 'Git ref to run the workflow on (branch, tag, or commit)') + .option('--repo ', 'Override GitHub repo in owner/name format'); + +program.parse(process.argv); +const opts = program.opts(); + +ensureGhCliAvailable(); + +const args: string[] = ['workflow', 'run', 'build-unity-spacecraft.yml']; + +if (opts.repo) { + args.push('--repo', opts.repo); +} +args.push('--ref', opts.ref); + +function pushField(name: string, value: string | undefined) { + if (typeof value === 'string') { + args.push('-f', `${name}=${value}`); + } +} + +pushField('unityVersion', opts.unityVersion); +pushField('projectPath', opts.projectPath); +pushField('targetPlatform', opts.targetPlatform); +pushField('buildProfile', opts.buildProfile); +if (opts.buildMethod) { + pushField('buildMethod', opts.buildMethod); +} + +try { + console.log('[gh] Running:', 'gh', args.join(' ')); + const out = execSync(`gh ${args.map(a => (a.includes(' ') ? `'${a.replace(/'/g, "'\\''")}'` : a)).join(' ')}`, { + stdio: 'inherit', + }); +} catch (err: any) { + const code = typeof err?.status === 'number' ? err.status : 1; + process.exit(code); +} + + diff --git a/Unity/SpaceCraft/Assets/Editor/Build.cs b/Unity/SpaceCraft/Assets/Editor/Build.cs index 74df9ad4..be623417 100644 --- a/Unity/SpaceCraft/Assets/Editor/Build.cs +++ b/Unity/SpaceCraft/Assets/Editor/Build.cs @@ -44,13 +44,18 @@ public static void WebGL_Dev() PlayerSettings.WebGL.template = templateName; } - //string buildPath = Path.Combine("Builds", "SpaceCraft"); - // Build directly into WebSites/SpaceCraft at top level of repo. + // Default dev output (existing behavior) string buildPath = Path.Combine("..", "..", "WebSites", "SpaceCraft"); + // Allow CI override via env var or CLI + buildPath = ResolveOutputPath(buildPath); + Debug.Log($"[Build] Output path: {buildPath}"); + + // Ensure scenes are configured + var scenes = EnsureScenesConfigured(); BuildPlayerOptions options = new BuildPlayerOptions { - scenes = GetBuildScenes(), + scenes = scenes, locationPathName = buildPath, target = BuildTarget.WebGL, options = BuildOptions.Development // Add development flag if needed @@ -87,11 +92,18 @@ public static void WebGL_Prod() } // --- End Force --- + // Default prod output (existing behavior) string buildPath = Path.Combine("Builds", "SpaceCraft"); + // Allow CI override via env var or CLI + buildPath = ResolveOutputPath(buildPath); + Debug.Log($"[Build] Output path: {buildPath}"); + + // Ensure scenes are configured + var scenes = EnsureScenesConfigured(); BuildPlayerOptions options = new BuildPlayerOptions { - scenes = GetBuildScenes(), + scenes = scenes, locationPathName = buildPath, target = BuildTarget.WebGL, options = BuildOptions.None @@ -106,8 +118,7 @@ private static void PerformBuild(BuildPlayerOptions options) { // Ensure build directory exists string buildPath = Path.GetFullPath(options.locationPathName); // Get full path - string buildDir = Path.GetDirectoryName(buildPath); - Directory.CreateDirectory(buildDir); + Directory.CreateDirectory(buildPath); // --- PRE-BUILD STEP: Remove symlinks from build target directory --- Debug.Log($"[Build Pre-Clean] Cleaning symlinks from: {buildPath}"); @@ -194,15 +205,66 @@ private static void RemoveSymlinksRecursive(string path) } } - private static string[] GetBuildScenes() + private static string[] EnsureScenesConfigured() { - // Get all enabled scenes from the build settings - var scenes = new string[EditorBuildSettings.scenes.Length]; - for (int i = 0; i < scenes.Length; i++) + // If there are enabled scenes already, return them + var enabled = System.Array.FindAll(EditorBuildSettings.scenes, s => s.enabled); + if (enabled.Length > 0) + { + string[] paths = new string[enabled.Length]; + for (int i = 0; i < enabled.Length; i++) paths[i] = enabled[i].path; + return paths; + } + + // Discover all .unity scenes under Assets and configure Build Settings + Debug.Log("[Build] No enabled scenes in Build Settings; falling back to discover all .unity scenes under Assets/"); + string[] discovered = new string[0]; + try + { + discovered = Directory.GetFiles("Assets", "*.unity", SearchOption.AllDirectories); + } + catch (Exception ex) + { + Debug.LogError($"[Build] Failed to enumerate scenes: {ex.Message}"); + } + + if (discovered.Length > 0) { - scenes[i] = EditorBuildSettings.scenes[i].path; + var buildScenes = new EditorBuildSettingsScene[discovered.Length]; + for (int i = 0; i < discovered.Length; i++) + { + buildScenes[i] = new EditorBuildSettingsScene(discovered[i], true); + Debug.Log($"[Build] Including scene: {discovered[i]}"); + } + EditorBuildSettings.scenes = buildScenes; + return discovered; } - return scenes; + + Debug.LogError("[Build] No scenes found to build. Configure Build Settings or add scenes under Assets/."); + return discovered; // may be empty; BuildPipeline will fail and surface error + } + + private static string ResolveOutputPath(string defaultPath) + { + // 1) Command line arg: -scOut or --scOut= + var args = Environment.GetCommandLineArgs(); + for (int i = 0; i < args.Length; i++) + { + if (args[i] == "-scOut" && i + 1 < args.Length) + { + return args[i + 1]; + } + if (args[i].StartsWith("--scOut=")) + { + var val = args[i].Substring("--scOut=".Length); + if (!string.IsNullOrEmpty(val)) return val; + } + } + + // 2) Environment variable fallback + var envOut = Environment.GetEnvironmentVariable("SC_BUILD_OUTPUT"); + if (!string.IsNullOrEmpty(envOut)) return envOut; + return defaultPath; } private static bool IsCommandLineBuild() diff --git a/Unity/SpaceCraft/Assets/Libraries/Bridge/BridgePlugin.cs b/Unity/SpaceCraft/Assets/Libraries/Bridge/BridgePlugin.cs index c89dc3ef..3c3d6dfb 100644 --- a/Unity/SpaceCraft/Assets/Libraries/Bridge/BridgePlugin.cs +++ b/Unity/SpaceCraft/Assets/Libraries/Bridge/BridgePlugin.cs @@ -60,7 +60,7 @@ public class BridgePlugin : MonoBehaviour { public bool pluginRenderEventIssued; public List messageQueue = new List(); -#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS IntPtr plugin; #elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN // TODO @@ -77,7 +77,7 @@ public bool IsKeyboardVisible { get { #if UNITY_ANDROID return isKeyboardVisible; -#elif UNITY_IPHONE +#elif UNITY_IOS return TouchScreenKeyboard.visible; #else return false; @@ -103,11 +103,11 @@ public void SetKeyboardVisible(string isVisible) private const string PLUGIN_DLL = "Bridge_Editor"; #elif UNITY_STANDALONE_WIN private const string PLUGIN_DLL = "Bridge"; -#elif UNITY_IPHONE +#elif UNITY_IOS private const string PLUGIN_DLL = "__Internal"; #endif -#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS [DllImport(PLUGIN_DLL)] private static extern void _CBridgePlugin_SetUnitySendMessageCallback(IntPtr unitySendMessageCallback); @@ -558,7 +558,7 @@ public void EvaluateJSReturnResult(string js) public bool CanGoBack() { -#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS if (plugin == IntPtr.Zero) { return false; @@ -579,6 +579,11 @@ public bool CanGoBack() return plugin.Get("canGoBack"); +#else + + // Fallback for Linux/WebGL/other platforms + return false; + #endif } @@ -608,6 +613,11 @@ public bool CanGoForward() return plugin.Get("canGoForward"); +#else + + // Fallback for Linux/WebGL/other platforms + return false; + #endif } @@ -729,6 +739,8 @@ public void CallOnTexture() long newTextureHandle = 0; #elif UNITY_ANDROID long newTextureHandle = plugin.Call("GetRenderTextureHandle"); +#elif UNITY_EDITOR_LINUX || UNITY_STANDALONE_LINUX || UNITY_WEBGL + long newTextureHandle = 0; #endif //Debug.Log("BridgePlugin: CallOnTexture: newTextureHandle: " + newTextureHandle + " textureHandle: " + textureHandle); @@ -750,6 +762,9 @@ public void CallOnTexture() #elif UNITY_ANDROID textureWidth = plugin.Call("GetRenderTextureWidth"); textureHeight = plugin.Call("GetRenderTextureHeight"); +#elif UNITY_EDITOR_LINUX || UNITY_STANDALONE_LINUX || UNITY_WEBGL + textureWidth = 0; + textureHeight = 0; #endif texture = Texture2D.CreateExternalTexture(textureWidth, textureHeight, TextureFormat.RGBA32, false, true, (IntPtr)textureHandle); @@ -770,12 +785,16 @@ private IntPtr GetRenderEventFunc() { if (renderEventFunc == (IntPtr)0) { renderEventFunc = -#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS (IntPtr)_CBridgePlugin_GetRenderEventFunc(); -#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN - (IntPtr)0; // TODO +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_EDITOR_LINUX || UNITY_STANDALONE_LINUX || UNITY_WEBGL + (IntPtr)0; // TODO (Windows) #elif UNITY_ANDROID (IntPtr)plugin.CallStatic("GetRenderEventFunc"); +#elif UNITY_EDITOR_LINUX || UNITY_STANDALONE_LINUX || UNITY_WEBGL + (IntPtr)0; // Fallback for Linux/WebGL where native plugin call is not available +#else + (IntPtr)0; // Other platforms fallback #endif //Debug.Log("BridgePlugin: GetRenderEventFunc: Got renderEventFunc: " + renderEventFunc); } @@ -788,12 +807,14 @@ public void RenderIntoTexture(int width, int height) { //Debug.Log("BridgePlugin: RenderIntoTexture: time: " + Time.time + " width: " + width + " height: " + height); -#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS _CBridgePlugin_RenderIntoTextureSetup(plugin, width, height); -#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_EDITOR_LINUX || UNITY_STANDALONE_LINUX || UNITY_WEBGL // TODO #elif UNITY_ANDROID plugin.Call("RenderIntoTextureSetup", width, height); +#else + // TODO (fallback) #endif } @@ -801,7 +822,7 @@ public void RenderIntoTexture(int width, int height) public void FlushCaches() { -#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS if (plugin == IntPtr.Zero) { return; @@ -809,7 +830,7 @@ public void FlushCaches() _CBridgePlugin_FlushCaches(plugin); -#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_EDITOR_LINUX || UNITY_STANDALONE_LINUX || UNITY_WEBGL // TODO @@ -853,7 +874,7 @@ public void IssuePluginRenderEvent() { //Debug.Log("BridgePlugin: IssuePluginRenderEvent: time: " + Time.time + " pluginID: " + pluginID + " this: " + this); -#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE || UNITY_ANDROID +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS || UNITY_ANDROID GL.IssuePluginEvent(GetRenderEventFunc(), 2); #endif } diff --git a/Unity/SpaceCraft/ProjectSettings/ProjectVersion.txt b/Unity/SpaceCraft/ProjectSettings/ProjectVersion.txt index 3c3dbfe7..7f0a5364 100644 --- a/Unity/SpaceCraft/ProjectSettings/ProjectVersion.txt +++ b/Unity/SpaceCraft/ProjectSettings/ProjectVersion.txt @@ -1,3 +1,2 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2152feb1c6edf0221070c3ab8f52d9de45be39c979b719ca4f7f3b612b69a4e7 -size 85 +m_EditorVersion: 6000.0.36f1 +m_EditorVersionWithRevision: 6000.0.36f1 (9fe3b5f71dbb) diff --git a/WebSites/SpaceCraft/Build/SpaceCraft.data b/WebSites/SpaceCraft/Build/SpaceCraft.data index ae7656fe..f24ef404 100644 --- a/WebSites/SpaceCraft/Build/SpaceCraft.data +++ b/WebSites/SpaceCraft/Build/SpaceCraft.data @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c72299555009649e126a47545753a9c1463962439e6c69ea8acb3386b0e7db45 +oid sha256:60188a18e076ba81338b864f7080e8eaa521cf0fb864223c2093d35f5bc4294a size 10230368 diff --git a/WebSites/controller/build/TabBase.d.ts b/WebSites/controller/build/TabBase.d.ts index 8ce4dac3..e8e7d5ce 100644 --- a/WebSites/controller/build/TabBase.d.ts +++ b/WebSites/controller/build/TabBase.d.ts @@ -1,4 +1,4 @@ -import { IoElement, IoElementProps, ListenerDefinition } from 'io-gui'; +import { IoElement, IoElementProps } from 'io-gui'; import { SpacetimeController } from './SpacetimeController.js'; import { SimulatorState } from './SimulatorState.js'; export type TabBaseProps = IoElementProps & { @@ -9,17 +9,7 @@ export declare class TabBase extends IoElement { static get Style(): string; controller: SpacetimeController; simulatorState: SimulatorState; - static get Listeners(): { - contextmenu: string; - pointerdown: string; - touchstart: ListenerDefinition; - touchmove: ListenerDefinition; - }; constructor(props: TabBaseProps); - preventDefault(event: Event): void; - onPointerdown(event: PointerEvent): void; - onPointermove(event: PointerEvent): void; - onPointerup(event: PointerEvent): void; ready(): void; simulatorStateMutated(): void; changed(): void; diff --git a/WebSites/controller/build/TabBase.d.ts.map b/WebSites/controller/build/TabBase.d.ts.map index 91ed93f0..1191e4de 100644 --- a/WebSites/controller/build/TabBase.d.ts.map +++ b/WebSites/controller/build/TabBase.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"TabBase.d.ts","sourceRoot":"","sources":["../src/TabBase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgB,cAAc,EAA8B,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACjH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG;IAC1C,UAAU,EAAE,mBAAmB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;CAChC,CAAC;AAEF,qBACa,OAAQ,SAAQ,SAAS;IAClC,MAAM,KAAK,KAAK,WAYf;IAGO,UAAU,EAAE,mBAAmB,CAAC;IAGhC,cAAc,EAAE,cAAc,CAAC;IAEvC,MAAM,KAAK,SAAS;;;oBAI0C,kBAAkB;mBACnB,kBAAkB;MAE9E;gBAEW,KAAK,EAAE,YAAY;IAI/B,cAAc,CAAC,KAAK,EAAE,KAAK;IAG3B,aAAa,CAAC,KAAK,EAAE,YAAY;IAMjC,aAAa,CAAC,KAAK,EAAE,YAAY;IACjC,WAAW,CAAC,KAAK,EAAE,YAAY;IAO/B,KAAK;IAIL,qBAAqB;IAIrB,OAAO;CAKV;AAED,eAAO,MAAM,OAAO,GAAY,MAAM,YAAY,iCAEjD,CAAC"} \ No newline at end of file +{"version":3,"file":"TabBase.d.ts","sourceRoot":"","sources":["../src/TabBase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgB,cAAc,EAAkD,MAAM,QAAQ,CAAC;AACjH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG;IAC1C,UAAU,EAAE,mBAAmB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;CAChC,CAAC;AAEF,qBACa,OAAQ,SAAQ,SAAS;IAClC,MAAM,KAAK,KAAK,WAYf;IAGO,UAAU,EAAE,mBAAmB,CAAC;IAGhC,cAAc,EAAE,cAAc,CAAC;gBAE3B,KAAK,EAAE,YAAY;IAI/B,KAAK;IAIL,qBAAqB;IAIrB,OAAO;CAKV;AAED,eAAO,MAAM,OAAO,GAAY,MAAM,YAAY,iCAEjD,CAAC"} \ No newline at end of file diff --git a/WebSites/controller/build/TabBase.js b/WebSites/controller/build/TabBase.js index f73bf27d..d82549c3 100644 --- a/WebSites/controller/build/TabBase.js +++ b/WebSites/controller/build/TabBase.js @@ -20,33 +20,9 @@ let TabBase = class TabBase extends IoElement { } `; } - static get Listeners() { - return { - 'contextmenu': 'preventDefault', - 'pointerdown': 'onPointerdown', - 'touchstart': ['preventDefault', { passive: false }], - 'touchmove': ['preventDefault', { passive: false }], - }; - } constructor(props) { super(props); } - preventDefault(event) { - event.preventDefault(); - } - onPointerdown(event) { - this.setPointerCapture(event.pointerId); - this.addEventListener('pointerup', this.onPointerup); - this.addEventListener('pointermove', this.onPointermove); - this.addEventListener('pointercancel', this.onPointerup); - } - onPointermove(event) { } - onPointerup(event) { - this.releasePointerCapture(event.pointerId); - this.removeEventListener('pointerup', this.onPointerup); - this.removeEventListener('pointermove', this.onPointermove); - this.removeEventListener('pointercancel', this.onPointerup); - } ready() { this.changed(); } diff --git a/WebSites/controller/build/TabBase.js.map b/WebSites/controller/build/TabBase.js.map index f1ecb418..2025b3a4 100644 --- a/WebSites/controller/build/TabBase.js.map +++ b/WebSites/controller/build/TabBase.js.map @@ -1 +1 @@ -{"version":3,"file":"TabBase.js","sourceRoot":"","sources":["../src/TabBase.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAkB,QAAQ,EAAE,gBAAgB,EAAsB,MAAM,QAAQ,CAAC;AAEjH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAQ9C,IAAM,OAAO,GAAb,MAAM,OAAQ,SAAQ,SAAS;IAClC,MAAM,KAAK,KAAK;QACZ,OAAO,SAAS,CAAA;;;;;;;;;;SAUf,CAAC;IACN,CAAC;IAQD,MAAM,KAAK,SAAS;QAChB,OAAO;YACH,aAAa,EAAE,gBAAgB;YAC/B,aAAa,EAAE,eAAe;YAC9B,YAAY,EAAE,CAAC,gBAAgB,EAAE,EAAC,OAAO,EAAE,KAAK,EAAC,CAAuB;YACxE,WAAW,EAAE,CAAC,gBAAgB,EAAE,EAAC,OAAO,EAAE,KAAK,EAAC,CAAuB;SAC1E,CAAC;IACN,CAAC;IAED,YAAY,KAAmB;QAC3B,KAAK,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IAED,cAAc,CAAC,KAAY;QACvB,KAAK,CAAC,cAAc,EAAE,CAAC;IAC3B,CAAC;IACD,aAAa,CAAC,KAAmB;QAC7B,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACxC,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACrD,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QACzD,IAAI,CAAC,gBAAgB,CAAC,eAAe,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAC7D,CAAC;IACD,aAAa,CAAC,KAAmB,IAAG,CAAC;IACrC,WAAW,CAAC,KAAmB;QAC3B,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACxD,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QAC5D,IAAI,CAAC,mBAAmB,CAAC,eAAe,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAChE,CAAC;IAED,KAAK;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;IAED,qBAAqB;QACjB,IAAI,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;IAED,OAAO;QACH,IAAI,CAAC,MAAM,CAAC;YACR,EAAE,CAAC,SAAS,CAAC;SAChB,CAAC,CAAC;IACP,CAAC;CACJ,CAAA;AAhDW;IADP,QAAQ,EAAE;2CAC6B;AAGhC;IADP,gBAAgB,CAAC,EAAC,IAAI,EAAE,cAAc,EAAC,CAAC;+CACF;AAnB9B,OAAO;IADnB,QAAQ;GACI,OAAO,CAgEnB;;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,UAAS,IAAkB;IAC9C,OAAO,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC,CAAC","sourcesContent":["import { IoElement, h2, Register, IoElementProps, Property, ReactiveProperty, ListenerDefinition } from 'io-gui';\nimport { SpacetimeController } from './SpacetimeController.js';\nimport { SimulatorState } from './SimulatorState.js';\n\nexport type TabBaseProps = IoElementProps & {\n controller: SpacetimeController;\n simulatorState: SimulatorState;\n};\n\n@Register\nexport class TabBase extends IoElement {\n static get Style() {\n return /* css */`\n :host {\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-start;\n padding: 2em;\n overflow-y: auto;\n }\n `;\n }\n\n @Property()\n declare controller: SpacetimeController;\n\n @ReactiveProperty({type: SimulatorState})\n declare simulatorState: SimulatorState;\n\n static get Listeners() {\n return {\n 'contextmenu': 'preventDefault',\n 'pointerdown': 'onPointerdown',\n 'touchstart': ['preventDefault', {passive: false}] as ListenerDefinition,\n 'touchmove': ['preventDefault', {passive: false}] as ListenerDefinition,\n };\n }\n\n constructor(props: TabBaseProps) {\n super(props);\n }\n\n preventDefault(event: Event) {\n event.preventDefault();\n }\n onPointerdown(event: PointerEvent) {\n this.setPointerCapture(event.pointerId);\n this.addEventListener('pointerup', this.onPointerup);\n this.addEventListener('pointermove', this.onPointermove);\n this.addEventListener('pointercancel', this.onPointerup);\n }\n onPointermove(event: PointerEvent) {}\n onPointerup(event: PointerEvent) {\n this.releasePointerCapture(event.pointerId);\n this.removeEventListener('pointerup', this.onPointerup);\n this.removeEventListener('pointermove', this.onPointermove);\n this.removeEventListener('pointercancel', this.onPointerup);\n }\n\n ready() {\n this.changed();\n }\n\n simulatorStateMutated() {\n this.changed();\n }\n\n changed() {\n this.render([\n h2('TabBase'),\n ]);\n }\n}\n\nexport const tabBase = function(arg0: TabBaseProps) {\n return TabBase.vConstructor(arg0);\n};"]} \ No newline at end of file +{"version":3,"file":"TabBase.js","sourceRoot":"","sources":["../src/TabBase.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAkB,QAAQ,EAAE,gBAAgB,EAAsB,MAAM,QAAQ,CAAC;AAEjH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAQ9C,IAAM,OAAO,GAAb,MAAM,OAAQ,SAAQ,SAAS;IAClC,MAAM,KAAK,KAAK;QACZ,OAAO,SAAS,CAAA;;;;;;;;;;SAUf,CAAC;IACN,CAAC;IAQD,YAAY,KAAmB;QAC3B,KAAK,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IAED,KAAK;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;IAED,qBAAqB;QACjB,IAAI,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;IAED,OAAO;QACH,IAAI,CAAC,MAAM,CAAC;YACR,EAAE,CAAC,SAAS,CAAC;SAChB,CAAC,CAAC;IACP,CAAC;CACJ,CAAA;AAtBW;IADP,QAAQ,EAAE;2CAC6B;AAGhC;IADP,gBAAgB,CAAC,EAAC,IAAI,EAAE,cAAc,EAAC,CAAC;+CACF;AAnB9B,OAAO;IADnB,QAAQ;GACI,OAAO,CAsCnB;;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,UAAS,IAAkB;IAC9C,OAAO,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC,CAAC","sourcesContent":["import { IoElement, h2, Register, IoElementProps, Property, ReactiveProperty, ListenerDefinition } from 'io-gui';\nimport { SpacetimeController } from './SpacetimeController.js';\nimport { SimulatorState } from './SimulatorState.js';\n\nexport type TabBaseProps = IoElementProps & {\n controller: SpacetimeController;\n simulatorState: SimulatorState;\n};\n\n@Register\nexport class TabBase extends IoElement {\n static get Style() {\n return /* css */`\n :host {\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-start;\n padding: 2em;\n overflow-y: auto;\n }\n `;\n }\n\n @Property()\n declare controller: SpacetimeController;\n\n @ReactiveProperty({type: SimulatorState})\n declare simulatorState: SimulatorState;\n\n constructor(props: TabBaseProps) {\n super(props);\n }\n\n ready() {\n this.changed();\n }\n\n simulatorStateMutated() {\n this.changed();\n }\n\n changed() {\n this.render([\n h2('TabBase'),\n ]);\n }\n}\n\nexport const tabBase = function(arg0: TabBaseProps) {\n return TabBase.vConstructor(arg0);\n};"]} \ No newline at end of file diff --git a/WebSites/controller/build/TabSelect.d.ts b/WebSites/controller/build/TabSelect.d.ts index f4e6a72d..84cf72e2 100644 --- a/WebSites/controller/build/TabSelect.d.ts +++ b/WebSites/controller/build/TabSelect.d.ts @@ -1,8 +1,16 @@ +import { ListenerDefinition } from 'io-gui'; import { TabBase, TabBaseProps } from './TabBase.js'; export declare class TabSelect extends TabBase { static get Style(): string; startX: number; startY: number; + static get Listeners(): { + pointerdown: string; + touchstart: ListenerDefinition; + touchmove: ListenerDefinition; + wheel: string; + }; + preventDefault(event: Event): void; onPointerdown(event: PointerEvent): void; onPointerup(event: PointerEvent): void; changed(): void; diff --git a/WebSites/controller/build/TabSelect.d.ts.map b/WebSites/controller/build/TabSelect.d.ts.map index 6b5743f8..3cd49a4e 100644 --- a/WebSites/controller/build/TabSelect.d.ts.map +++ b/WebSites/controller/build/TabSelect.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"TabSelect.d.ts","sourceRoot":"","sources":["../src/TabSelect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAKrD,qBACa,SAAU,SAAQ,OAAO;IAClC,MAAM,KAAK,KAAK,WAWf;IAEO,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IAEvB,aAAa,CAAC,KAAK,EAAE,YAAY;IAKjC,WAAW,CAAC,KAAK,EAAE,YAAY;IAyB/B,OAAO;CA0BV;AAED,eAAO,MAAM,SAAS,GAAY,MAAM,YAAY,iCAEnD,CAAC"} \ No newline at end of file +{"version":3,"file":"TabSelect.d.ts","sourceRoot":"","sources":["../src/TabSelect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAKrD,qBACa,SAAU,SAAQ,OAAO;IAClC,MAAM,KAAK,KAAK,WAWf;IAEO,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IAEvB,MAAM,KAAK,SAAS;;oBAG0C,kBAAkB;mBACnB,kBAAkB;;MAG9E;IAED,cAAc,CAAC,KAAK,EAAE,KAAK;IAG3B,aAAa,CAAC,KAAK,EAAE,YAAY;IAOjC,WAAW,CAAC,KAAK,EAAE,YAAY;IA0B/B,OAAO;CA0BV;AAED,eAAO,MAAM,SAAS,GAAY,MAAM,YAAY,iCAEnD,CAAC"} \ No newline at end of file diff --git a/WebSites/controller/build/TabSelect.js b/WebSites/controller/build/TabSelect.js index 74c35a59..3415702b 100644 --- a/WebSites/controller/build/TabSelect.js +++ b/WebSites/controller/build/TabSelect.js @@ -21,13 +21,28 @@ let TabSelect = class TabSelect extends TabBase { } `; } + static get Listeners() { + return { + 'pointerdown': 'onPointerdown', + 'touchstart': ['preventDefault', { passive: false }], + 'touchmove': ['preventDefault', { passive: false }], + 'wheel': 'onScroll', + }; + } + preventDefault(event) { + event.preventDefault(); + } onPointerdown(event) { - super.onPointerdown(event); + this.setPointerCapture(event.pointerId); + this.addEventListener('pointerup', this.onPointerup); + this.addEventListener('pointercancel', this.onPointerup); this.startX = event.clientX; this.startY = event.clientY; } onPointerup(event) { - super.onPointerup(event); + this.releasePointerCapture(event.pointerId); + this.removeEventListener('pointerup', this.onPointerup); + this.removeEventListener('pointercancel', this.onPointerup); const dx = event.clientX - this.startX; const dy = event.clientY - this.startY; const distance = Math.sqrt(dx * dx + dy * dy); diff --git a/WebSites/controller/build/TabSelect.js.map b/WebSites/controller/build/TabSelect.js.map index 6fe504a5..835b8e9e 100644 --- a/WebSites/controller/build/TabSelect.js.map +++ b/WebSites/controller/build/TabSelect.js.map @@ -1 +1 @@ -{"version":3,"file":"TabSelect.js","sourceRoot":"","sources":["../src/TabSelect.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAE,OAAO,EAAgB,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAGtB,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,OAAO;IAClC,MAAM,KAAK,KAAK;QACZ,OAAO,SAAS,CAAA;;;;;;;;;SASf,CAAC;IACN,CAAC;IAKD,aAAa,CAAC,KAAmB;QAC7B,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;IAChC,CAAC;IACD,WAAW,CAAC,KAAmB;QAC3B,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAEzB,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC;QACvC,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;QAE9C,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,QAAQ,GAAG,iBAAiB,EAAE,CAAC;YAC/B,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC9B,OAAO,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;YACvC,CAAC;iBAAM,CAAC;gBACJ,OAAO,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;YACzC,CAAC;QACL,CAAC;QAED,QAAQ,OAAO,EAAE,CAAC;YAClB,KAAK,KAAK;gBAAE,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;gBAAC,MAAM;YAC1D,KAAK,OAAO;gBAAE,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;gBAAC,MAAM;YAC9D,KAAK,OAAO;gBAAE,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;gBAAC,MAAM;YAC9D,KAAK,MAAM;gBAAE,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;gBAAC,MAAM;YAC5D,KAAK,MAAM;gBAAE,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;gBAAC,MAAM;QAC5D,CAAC;IACL,CAAC;IAED,OAAO;QACH,IAAI,WAAW,GAAG,0BAA0B,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC;QACnD,IAAI,QAAQ,EAAE,WAAW,EAAE,CAAC;YACxB,WAAW,GAAG,QAAQ,CAAC,WAAW;iBAC7B,KAAK,CAAC,KAAK,CAAC,CAAC,kCAAkC;iBAC/C,MAAM,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,qBAAqB;iBACtE,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,iCAAiC;iBAChF,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;QAED,oFAAoF;QACpF,MAAM,MAAM,GAAS,IAAI,CAAC,UAAkB,CAAC,cAAc,IAAI,EAAE,CAAC;QAClE,MAAM,UAAU,GAAI,MAAc,CAAC,UAAU,IAAK,MAAc,CAAC,eAAe,CAAC;QACjF,MAAM,UAAU,GAAG,YAAY,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACvD,MAAM,UAAU,GAAI,MAAc,CAAC,aAAa,IAAI,wCAAwC,CAAC;QAE7F,IAAI,CAAC,MAAM,CAAC;YACR,EAAE,CAAC,8BAA8B,CAAC;YAClC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;gBACX,GAAG,CAAC,EAAC,GAAG,EAAE,GAAG,UAAU,2BAA2B,QAAQ,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,aAAa,QAAQ,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,aAAa,EAAC,CAAC;gBACrI,EAAE,CAAC,QAAQ,CAAC,KAAK,IAAI,UAAU,CAAC;gBAChC,GAAG,CAAC,EAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAC,CAAC;aACtD,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC;SAC7B,CAAC,CAAC;IACP,CAAC;CACJ,CAAA;AAzEY,SAAS;IADrB,QAAQ;GACI,SAAS,CAyErB;;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,UAAS,IAAkB;IAChD,OAAO,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC,CAAC","sourcesContent":["import { h2, p, Register, div, img, h4 } from 'io-gui';\nimport { TabBase, TabBaseProps } from './TabBase.js';\nimport { contentStore } from './services/ContentStore.js';\n\nconst GESTURE_THRESHOLD = 20;\n\n@Register\nexport class TabSelect extends TabBase {\n static get Style() {\n return /* css */`\n :host {\n text-align: justify;\n }\n :host .cover-image {\n pointer-events: none;\n float: right;\n margin: 4.2em 0 0.5em 1em;\n }\n `;\n }\n\n declare startX: number;\n declare startY: number;\n\n onPointerdown(event: PointerEvent) {\n super.onPointerdown(event);\n this.startX = event.clientX;\n this.startY = event.clientY;\n }\n onPointerup(event: PointerEvent) {\n super.onPointerup(event);\n\n const dx = event.clientX - this.startX;\n const dy = event.clientY - this.startY;\n const distance = Math.sqrt(dx * dx + dy * dy);\n\n let gesture = 'tap';\n if (distance > GESTURE_THRESHOLD) {\n if (Math.abs(dx) > Math.abs(dy)) {\n gesture = dx > 0 ? 'east' : 'west';\n } else {\n gesture = dy > 0 ? 'south' : 'north';\n }\n }\n\n switch (gesture) {\n case 'tap': this.controller.sendSelectEvent('tap'); break;\n case 'north': this.controller.sendSelectEvent('north'); break;\n case 'south': this.controller.sendSelectEvent('south'); break;\n case 'east': this.controller.sendSelectEvent('east'); break;\n case 'west': this.controller.sendSelectEvent('west'); break;\n }\n }\n\n changed() {\n let description = 'No description available';\n const selected = this.simulatorState?.selectedItem;\n if (selected?.description) {\n description = selected.description\n .split(/\\n+/) // Split on any number of newlines\n .filter((line: string) => line.trim().length > 0) // Remove empty lines\n .map((line: string) => `

${line.trim()}

`) // Create paragraph for each line\n .join('');\n }\n\n // Resolve cover base from content store (built-in StreamingAssets path or external)\n const shared: any = (this.controller as any).simulatorState || {};\n const contentKey = (shared as any).contentKey || (shared as any).contentIndexUrl;\n const contentRec = contentStore.getContent(contentKey);\n const assetsBase = (shared as any).assetsBaseUrl || '../SpaceCraft/StreamingAssets/Content/';\n\n this.render([\n h2('TAP or SWIPE to select items'),\n selected ? div([\n img({src: `${assetsBase}collections/scifi/items/${selected.id}/cover.jpg`, alt: `Cover for ${selected.title}`, class: 'cover-image'}),\n h4(selected.title || 'Untitled'),\n div({class: 'description', innerHTML: description}),\n ]) : p('No item selected'),\n ]);\n }\n}\n\nexport const tabSelect = function(arg0: TabBaseProps) {\n return TabSelect.vConstructor(arg0);\n};"]} \ No newline at end of file +{"version":3,"file":"TabSelect.js","sourceRoot":"","sources":["../src/TabSelect.ts"],"names":[],"mappings":"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","sourcesContent":["import { h2, p, Register, div, img, h4, ListenerDefinition } from 'io-gui';\nimport { TabBase, TabBaseProps } from './TabBase.js';\nimport { contentStore } from './services/ContentStore.js';\n\nconst GESTURE_THRESHOLD = 20;\n\n@Register\nexport class TabSelect extends TabBase {\n static get Style() {\n return /* css */`\n :host {\n text-align: justify;\n }\n :host .cover-image {\n pointer-events: none;\n float: right;\n margin: 4.2em 0 0.5em 1em;\n }\n `;\n }\n\n declare startX: number;\n declare startY: number;\n\n static get Listeners() {\n return {\n 'pointerdown': 'onPointerdown',\n 'touchstart': ['preventDefault', {passive: false}] as ListenerDefinition,\n 'touchmove': ['preventDefault', {passive: false}] as ListenerDefinition,\n 'wheel': 'onScroll',\n };\n }\n\n preventDefault(event: Event) {\n event.preventDefault();\n }\n onPointerdown(event: PointerEvent) {\n this.setPointerCapture(event.pointerId);\n this.addEventListener('pointerup', this.onPointerup);\n this.addEventListener('pointercancel', this.onPointerup);\n this.startX = event.clientX;\n this.startY = event.clientY;\n }\n onPointerup(event: PointerEvent) {\n this.releasePointerCapture(event.pointerId);\n this.removeEventListener('pointerup', this.onPointerup);\n this.removeEventListener('pointercancel', this.onPointerup);\n const dx = event.clientX - this.startX;\n const dy = event.clientY - this.startY;\n const distance = Math.sqrt(dx * dx + dy * dy);\n\n let gesture = 'tap';\n if (distance > GESTURE_THRESHOLD) {\n if (Math.abs(dx) > Math.abs(dy)) {\n gesture = dx > 0 ? 'east' : 'west';\n } else {\n gesture = dy > 0 ? 'south' : 'north';\n }\n }\n\n switch (gesture) {\n case 'tap': this.controller.sendSelectEvent('tap'); break;\n case 'north': this.controller.sendSelectEvent('north'); break;\n case 'south': this.controller.sendSelectEvent('south'); break;\n case 'east': this.controller.sendSelectEvent('east'); break;\n case 'west': this.controller.sendSelectEvent('west'); break;\n }\n }\n\n changed() {\n let description = 'No description available';\n const selected = this.simulatorState?.selectedItem;\n if (selected?.description) {\n description = selected.description\n .split(/\\n+/) // Split on any number of newlines\n .filter((line: string) => line.trim().length > 0) // Remove empty lines\n .map((line: string) => `

${line.trim()}

`) // Create paragraph for each line\n .join('');\n }\n\n // Resolve cover base from content store (built-in StreamingAssets path or external)\n const shared: any = (this.controller as any).simulatorState || {};\n const contentKey = (shared as any).contentKey || (shared as any).contentIndexUrl;\n const contentRec = contentStore.getContent(contentKey);\n const assetsBase = (shared as any).assetsBaseUrl || '../SpaceCraft/StreamingAssets/Content/';\n\n this.render([\n h2('TAP or SWIPE to select items'),\n selected ? div([\n img({src: `${assetsBase}collections/scifi/items/${selected.id}/cover.jpg`, alt: `Cover for ${selected.title}`, class: 'cover-image'}),\n h4(selected.title || 'Untitled'),\n div({class: 'description', innerHTML: description}),\n ]) : p('No item selected'),\n ]);\n }\n}\n\nexport const tabSelect = function(arg0: TabBaseProps) {\n return TabSelect.vConstructor(arg0);\n};"]} \ No newline at end of file diff --git a/WebSites/controller/build/TabView.d.ts b/WebSites/controller/build/TabView.d.ts index 66189b71..984ff5df 100644 --- a/WebSites/controller/build/TabView.d.ts +++ b/WebSites/controller/build/TabView.d.ts @@ -1,7 +1,18 @@ +import { ListenerDefinition } from 'io-gui'; import { TabBase, TabBaseProps } from './TabBase.js'; export declare class TabView extends TabBase { static get Style(): string; + static get Listeners(): { + pointerdown: string; + touchstart: ListenerDefinition; + touchmove: ListenerDefinition; + wheel: string; + }; + preventDefault(event: Event): void; + onPointerdown(event: PointerEvent): void; onPointermove(event: PointerEvent): void; + onPointerup(event: PointerEvent): void; + onWheel(event: WheelEvent): void; onViewModeChange(event: CustomEvent): void; changed(): void; } diff --git a/WebSites/controller/build/TabView.d.ts.map b/WebSites/controller/build/TabView.d.ts.map index 8886efd6..33bcb12b 100644 --- a/WebSites/controller/build/TabView.d.ts.map +++ b/WebSites/controller/build/TabView.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"TabView.d.ts","sourceRoot":"","sources":["../src/TabView.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAErD,qBACa,OAAQ,SAAQ,OAAO;IAChC,MAAM,KAAK,KAAK,WAuBf;IAED,aAAa,CAAC,KAAK,EAAE,YAAY;IAUjC,gBAAgB,CAAC,KAAK,EAAE,WAAW;IAUnC,OAAO;CAcV;AAED,eAAO,MAAM,OAAO,GAAY,MAAM,YAAY,iCAEjD,CAAC"} \ No newline at end of file +{"version":3,"file":"TabView.d.ts","sourceRoot":"","sources":["../src/TabView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEhH,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAErD,qBACa,OAAQ,SAAQ,OAAO;IAChC,MAAM,KAAK,KAAK,WAuBf;IAED,MAAM,KAAK,SAAS;;oBAG0C,kBAAkB;mBACnB,kBAAkB;;MAG9E;IAED,cAAc,CAAC,KAAK,EAAE,KAAK;IAG3B,aAAa,CAAC,KAAK,EAAE,YAAY;IAMjC,aAAa,CAAC,KAAK,EAAE,YAAY;IAQjC,WAAW,CAAC,KAAK,EAAE,YAAY;IAO/B,OAAO,CAAC,KAAK,EAAE,UAAU;IAIzB,gBAAgB,CAAC,KAAK,EAAE,WAAW;IAUnC,OAAO;CAcV;AAED,eAAO,MAAM,OAAO,GAAY,MAAM,YAAY,iCAEjD,CAAC"} \ No newline at end of file diff --git a/WebSites/controller/build/TabView.js b/WebSites/controller/build/TabView.js index 762362f8..7415924e 100644 --- a/WebSites/controller/build/TabView.js +++ b/WebSites/controller/build/TabView.js @@ -32,12 +32,37 @@ let TabView = class TabView extends TabBase { } `; } + static get Listeners() { + return { + 'pointerdown': 'onPointerdown', + 'touchstart': ['preventDefault', { passive: false }], + 'touchmove': ['preventDefault', { passive: false }], + 'wheel': 'onWheel', + }; + } + preventDefault(event) { + event.preventDefault(); + } + onPointerdown(event) { + this.setPointerCapture(event.pointerId); + this.addEventListener('pointerup', this.onPointerup); + this.addEventListener('pointermove', this.onPointermove); + this.addEventListener('pointercancel', this.onPointerup); + } onPointermove(event) { - super.onPointermove(event); if (event.movementX || event.movementY) { this.controller.sendPanEvent(event.movementX * 0.03, event.movementY * 0.03); } } + onPointerup(event) { + this.releasePointerCapture(event.pointerId); + this.removeEventListener('pointerup', this.onPointerup); + this.removeEventListener('pointermove', this.onPointermove); + this.removeEventListener('pointercancel', this.onPointerup); + } + onWheel(event) { + this.controller.sendZoomEvent(event.deltaY * 0.01); + } onViewModeChange(event) { const newMode = event.detail?.value; if (!newMode || !this.controller?.clientChannel) diff --git a/WebSites/controller/build/TabView.js.map b/WebSites/controller/build/TabView.js.map index e5b1d71e..e83f0b0e 100644 --- a/WebSites/controller/build/TabView.js.map +++ b/WebSites/controller/build/TabView.js.map @@ -1 +1 @@ -{"version":3,"file":"TabView.js","sourceRoot":"","sources":["../src/TabView.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAuB,MAAM,QAAQ,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAgB,MAAM,cAAc,CAAC;AAG9C,IAAM,OAAO,GAAb,MAAM,OAAQ,SAAQ,OAAO;IAChC,MAAM,KAAK,KAAK;QACZ,OAAO,SAAS,CAAA;;;;;;;;;;;;;;;;;;;;;SAqBf,CAAC;IACN,CAAC;IAED,aAAa,CAAC,KAAmB;QAC7B,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrC,IAAI,CAAC,UAAU,CAAC,YAAY,CACxB,KAAK,CAAC,SAAS,GAAG,IAAI,EACtB,KAAK,CAAC,SAAS,GAAG,IAAI,CACzB,CAAC;QACN,CAAC;IACL,CAAC;IAED,gBAAgB,CAAC,KAAkB;QAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC;QACpC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa;YAAE,OAAO;QACxD,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC;YAC/B,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,iBAAiB,EAAE,IAAI,CAAC,UAAU,CAAC,kBAAkB,EAAE;SACpF,CAAC,CAAC,KAAK,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAC,CAAC;IACxF,CAAC;IAED,OAAO;QACH,MAAM,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE,QAAQ,IAAI,iBAAiB,CAAC;QAC9D,IAAI,CAAC,MAAM,CAAC;YACR,GAAG,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE;gBAC5B,EAAE,CAAC,YAAY,CAAC;gBAChB,cAAc,CAAC,YAAY,CAAC;oBACxB,KAAK,EAAE,EAAE;oBACT,MAAM,EAAE,IAAI,UAAU,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;oBACtD,cAAc,EAAE,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;iBACxC,CAAC;aAC5B,CAAC;YACF,EAAE,CAAC,8BAA8B,CAAC;SACrC,CAAC,CAAC;IACP,CAAC;CACJ,CAAA;AA5DY,OAAO;IADnB,QAAQ;GACI,OAAO,CA4DnB;;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,UAAS,IAAkB;IAC9C,OAAO,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC,CAAC","sourcesContent":["import { h2, div, Register, IoOptionSelect, MenuOption, IoOptionSelectProps } from 'io-gui';\nimport { VIEW_MODE_OPTIONS, DEFAULT_VIEW_MODE } from './types/ViewMode.js';\nimport { TabBase, TabBaseProps } from './TabBase.js';\n\n@Register\nexport class TabView extends TabBase {\n static get Style() {\n return /* css */`\n :host {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n }\n :host > h2 {\n pointer-events: none;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n margin: 0 0 6px 0;\n }\n :host .view-controls {\n align-self: flex-start;\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 2px 8px 4px 8px;\n }\n `;\n }\n\n onPointermove(event: PointerEvent) {\n super.onPointermove(event);\n if (event.movementX || event.movementY) {\n this.controller.sendPanEvent(\n event.movementX * 0.03,\n event.movementY * 0.03\n );\n }\n }\n\n onViewModeChange(event: CustomEvent) {\n const newMode = event.detail?.value;\n if (!newMode || !this.controller?.clientChannel) return;\n this.controller.clientChannel.send({\n type: 'broadcast',\n event: 'setViewMode',\n payload: { mode: newMode, targetSimulatorId: this.controller.currentSimulatorId }\n }).catch((err: any) => console.error('[Controller] setViewMode send failed:', err));\n }\n\n changed() {\n const vm = this.simulatorState?.viewMode || DEFAULT_VIEW_MODE;\n this.render([\n div({ class: 'view-controls' }, [\n h2('View Mode:'),\n IoOptionSelect.vConstructor({\n value: vm,\n option: new MenuOption({ options: VIEW_MODE_OPTIONS }),\n '@value-input': (e: CustomEvent) => this.onViewModeChange(e)\n } as IoOptionSelectProps),\n ]),\n h2('DRAG to pan • SCROLL to zoom'),\n ]);\n }\n}\n\nexport const tabView = function(arg0: TabBaseProps) {\n return TabView.vConstructor(arg0);\n};\n"]} \ No newline at end of file +{"version":3,"file":"TabView.js","sourceRoot":"","sources":["../src/TabView.ts"],"names":[],"mappings":"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","sourcesContent":["import { h2, div, Register, IoOptionSelect, MenuOption, IoOptionSelectProps, ListenerDefinition } from 'io-gui';\nimport { VIEW_MODE_OPTIONS, DEFAULT_VIEW_MODE } from './types/ViewMode.js';\nimport { TabBase, TabBaseProps } from './TabBase.js';\n\n@Register\nexport class TabView extends TabBase {\n static get Style() {\n return /* css */`\n :host {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n }\n :host > h2 {\n pointer-events: none;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n margin: 0 0 6px 0;\n }\n :host .view-controls {\n align-self: flex-start;\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 2px 8px 4px 8px;\n }\n `;\n }\n\n static get Listeners() {\n return {\n 'pointerdown': 'onPointerdown',\n 'touchstart': ['preventDefault', {passive: false}] as ListenerDefinition,\n 'touchmove': ['preventDefault', {passive: false}] as ListenerDefinition,\n 'wheel': 'onWheel',\n };\n }\n\n preventDefault(event: Event) {\n event.preventDefault();\n }\n onPointerdown(event: PointerEvent) {\n this.setPointerCapture(event.pointerId);\n this.addEventListener('pointerup', this.onPointerup);\n this.addEventListener('pointermove', this.onPointermove);\n this.addEventListener('pointercancel', this.onPointerup);\n }\n onPointermove(event: PointerEvent) {\n if (event.movementX || event.movementY) {\n this.controller.sendPanEvent(\n event.movementX * 0.03,\n event.movementY * 0.03\n );\n }\n }\n onPointerup(event: PointerEvent) {\n this.releasePointerCapture(event.pointerId);\n this.removeEventListener('pointerup', this.onPointerup);\n this.removeEventListener('pointermove', this.onPointermove);\n this.removeEventListener('pointercancel', this.onPointerup);\n }\n\n onWheel(event: WheelEvent) {\n this.controller.sendZoomEvent(event.deltaY * 0.01);\n }\n\n onViewModeChange(event: CustomEvent) {\n const newMode = event.detail?.value;\n if (!newMode || !this.controller?.clientChannel) return;\n this.controller.clientChannel.send({\n type: 'broadcast',\n event: 'setViewMode',\n payload: { mode: newMode, targetSimulatorId: this.controller.currentSimulatorId }\n }).catch((err: any) => console.error('[Controller] setViewMode send failed:', err));\n }\n\n changed() {\n const vm = this.simulatorState?.viewMode || DEFAULT_VIEW_MODE;\n this.render([\n div({ class: 'view-controls' }, [\n h2('View Mode:'),\n IoOptionSelect.vConstructor({\n value: vm,\n option: new MenuOption({ options: VIEW_MODE_OPTIONS }),\n '@value-input': (e: CustomEvent) => this.onViewModeChange(e)\n } as IoOptionSelectProps),\n ]),\n h2('DRAG to pan • SCROLL to zoom'),\n ]);\n }\n}\n\nexport const tabView = function(arg0: TabBaseProps) {\n return TabView.vConstructor(arg0);\n};\n"]} \ No newline at end of file