{"id":51335712,"url":"https://github.com/kernalpanic/caveviewer","last_synced_at":"2026-07-11T04:00:35.552Z","repository":{"id":368534524,"uuid":"1285599500","full_name":"KernalPanic/CaveViewer","owner":"KernalPanic","description":"This is the repo for multi platform support for CaveViewer","archived":false,"fork":false,"pushed_at":"2026-07-05T01:11:10.000Z","size":4127,"stargazers_count":1,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-07-05T01:12:39.012Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://kernalpanic.github.io/CaveViewer/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KernalPanic.png","metadata":{"files":{"readme":"README-developer.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-01T01:11:53.000Z","updated_at":"2026-07-05T01:11:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/KernalPanic/CaveViewer","commit_stats":null,"previous_names":["kernalpanic/caveviewerplus","kernalpanic/caveviewer"],"tags_count":43,"template":false,"template_full_name":null,"purl":"pkg:github/KernalPanic/CaveViewer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KernalPanic%2FCaveViewer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KernalPanic%2FCaveViewer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KernalPanic%2FCaveViewer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KernalPanic%2FCaveViewer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KernalPanic","download_url":"https://codeload.github.com/KernalPanic/CaveViewer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KernalPanic%2FCaveViewer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35350133,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-11T02:00:05.354Z","response_time":104,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3dmap","3dviewer","cave","cavemap","map"],"created_at":"2026-07-02T02:08:12.911Z","updated_at":"2026-07-11T04:00:35.544Z","avatar_url":"https://github.com/KernalPanic.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CaveViewer Development From Source\n\nThis guide is for users who want to run CaveViewer from source.\n\nScope:\n\n- This document is intentionally focused on source-based development and local runs.\n\nContributor workflow, architecture, repository layout, coding, testing, and\nAI-assistant guidance are indexed in\n[`docs/development/`](docs/development/README.md). See\n[`CONTRIBUTING.md`](CONTRIBUTING.md) before preparing a change.\n\n## Get Source Files\n\nYou can start in either of these ways:\n\n- Clone the repository with Git (recommended for contributors).\n- Download source files from GitHub and unpack them locally.\n\nThe repository's source archive format is:\n\n- `CaveViewer-\u003cversion\u003e-source.tar.gz`\n\nThis format is produced by the existing source packaging flow in `scripts/common/package_source.sh`.\n\nRelease packages should include:\n\n- `LICENSE`\n- `THIRD_PARTY_NOTICES.md`\n\nThe application About text should identify CaveViewer as licensed under the GNU General Public License version 3.0.\n\n## Requirements\n\n- Git\n- Python 3.10+\n\nYou also need to run a typical workstation setup with C++ and other compilers if you desired to compile from source.\n\nUbuntu should work out of the box. Fedora 44 is special, so you have to install additional packages\n```bash\nsudo dnf install gcc gcc-c++ make python3-devel \\\n    mesa-libGL-devel mesa-libEGL-devel libX11-devel sudo python3.14-tkinter\n```\n\n## Clone the Repository\n\n```bash\ngit clone https://github.com/KernalPanic/CaveViewer.git\ncd CaveViewer\n```\n\nOptional: check out the latest version tag.\n\n```bash\ngit fetch --tags\nlatest=$(git tag -l \"v*\" --sort=-version:refname | head -n 1)\ngit checkout \"$latest\"\n```\n\n## Optional: Make This Local Clone Read-Only (No Push)\n\nIf you only want to pull and run from source, you can disable push behavior in this clone.\n\n```bash\n# 1) Disable default pushes from this repo\ngit config --local push.default nothing\n\n# 2) Set an intentionally invalid push URL for origin\ngit remote set-url --push origin DISABLED\n\n# 3) Verify read-only push setup\ngit config --local --get push.default\ngit remote -v\n```\n\nExpected result:\n\n- `push.default` shows `nothing`\n- `origin (push)` shows `DISABLED`\n\nWith this setup, normal pull/fetch operations continue to work, but push attempts fail immediately.\n\n## macOS and Linux: Run From Source\n\nUse the project bootstrap script:\n\n```bash\n./scripts/dev/install.sh\n```\n\nWhat it does:\n\n- Creates a development virtual environment at `.venv-dev` (or `CAVEVIEWER_DEV_VENV` if set)\n- Installs dependencies from `requirements.txt`\n- Installs CaveViewer in editable mode from `src/`\n- Generates `run_caveviewer.sh`\n\nRun the app:\n\n```bash\n./run_caveviewer.sh\n```\n\nAlternatively:\n\n```bash\n# If you activated the virtual environment, run the 'source' line below\nsource .venv-dev/bin/activate\n.venv-dev/bin/python -m caveviewer\n```\n\n## Windows: Run From Source\n\nOption A (recommended for technical users): manual venv flow.\n\n```powershell\npy -3 -m venv .venv-dev\n.\\.venv-dev\\Scripts\\python -m pip install --upgrade pip\n.\\.venv-dev\\Scripts\\python -m pip install -r requirements.txt\n.\\.venv-dev\\Scripts\\python -m pip install --no-deps -e .\n.\\.venv-dev\\Scripts\\python -m caveviewer\n```\n\nOption B (guided setup script in this repo):\n\n```powershell\npowershell -NoProfile -ExecutionPolicy Bypass -File .\\scripts\\windows\\setup.ps1\n```\n\nNotes:\n\n- `scripts/windows/setup.ps1` is designed to install prerequisites and set up a runnable local source environment.\n- `scripts/windows/launch.bat` is a launcher for the setup script.\n\n## Run Automated Tests\n\nInstall the development-only test tools after the runtime dependencies:\n\n```bash\n.venv-dev/bin/python -m pip install -r requirements-dev.txt\n.venv-dev/bin/python -m pytest\n```\n\nOn Windows, use `.venv-dev\\Scripts\\python` in place of `.venv-dev/bin/python`.\n\nThe suite isolates the home/preferences directory, blocks uncontrolled network\nconnections, and uses temporary directories for all generated files. The same\nessential suite and branch-coverage gate run automatically for pull requests\nand before every GitHub release workflow. Direct `scripts/release.sh` runs also\nexecute the complete pytest suite before changing the application version or\ncreating artifacts. It uses `.venv-dev` when available, then falls back to\n`python3`/`python`; set `CAVEVIEWER_TEST_PYTHON=/path/to/python` to select\nanother prepared interpreter. The interpreter must have `requirements.txt` and\n`requirements-dev.txt` installed.\n\nGitHub platform jobs pass `--skip-tests` because their required\n`essential-tests` job has already tested the same commit with coverage. Do not\nuse `--skip-tests` for an ordinary local release unless an equivalent external\ngate has completed successfully. Tests and development dependencies are not\nincluded in release archives.\n\n## Sample Map Source Overrides\n\nBy default, the built-in sample maps dialog reads release assets from:\n\n- Repository: `KernalPanic/CaveViewer`\n- Release tag: `sample-data`\n\nFor local development, you can point the sample maps dialog at a different source before launching the program. These settings are environment variables only; they are not exposed in the app UI.\n\nPrecedence:\n\n1. `CAVEVIEWER_SAMPLE_MAPS_API_URL` uses a full release API URL directly.\n2. Otherwise, CaveViewer builds the GitHub release API URL from `CAVEVIEWER_SAMPLE_MAPS_REPO` and `CAVEVIEWER_SAMPLE_DATA_TAG`.\n3. If none are set, the defaults above are used.\n\nmacOS/Linux example:\n\n```bash\nCAVEVIEWER_SAMPLE_MAPS_REPO=\"MyOrg/MyMaps\" \\\nCAVEVIEWER_SAMPLE_DATA_TAG=\"public-samples\" \\\n./run_caveviewer.sh\n```\n\nWindows PowerShell example:\n\n```powershell\n$env:CAVEVIEWER_SAMPLE_MAPS_REPO = \"MyOrg/MyMaps\"\n$env:CAVEVIEWER_SAMPLE_DATA_TAG = \"public-samples\"\n.\\.venv-dev\\Scripts\\python -m caveviewer\n```\n\nAdvanced direct API override:\n\n```bash\nCAVEVIEWER_SAMPLE_MAPS_API_URL=\"https://api.github.com/repos/MyOrg/MyMaps/releases/tags/public-samples\" \\\n./run_caveviewer.sh\n```\n\nThe API response must be compatible with GitHub's release API shape, including an `assets` list with asset `name`, `browser_download_url`, and `size` fields.\n\n## Updating Your Local Source Environment\n\nWhen the repository changes:\n\n```bash\ngit pull --ff-only\n```\n\nThen refresh dependencies in your active dev venv:\n\n```bash\n.venv-dev/bin/python -m pip install -r requirements.txt\n```\n\nOn Windows (PowerShell):\n\n```powershell\n.\\.venv-dev\\Scripts\\python -m pip install -r requirements.txt\n```\n\n## Troubleshooting\n\n- `python3 not found` (macOS/Linux): install Python 3.10+ and rerun setup.\n- Broken `.venv-dev`: remove it and rerun `./scripts/dev/install.sh`.\n- Windows PowerShell policy blocks setup script: run with `-ExecutionPolicy Bypass` as shown above.\n\n### Virtual Machine Runs\n\nWhen running CaveViewer inside Parallels or another VM, launch with vsync\ndisabled. Some virtual GPU drivers can hang or crash when vsync is enabled.\n\n```bash\nCAVEVIEWER_VSYNC=0 ./run_caveviewer.sh\n```\n\nIf the VM or GPU driver still hangs or crashes, force software OpenGL rendering:\n\n```bash\nLIBGL_ALWAYS_SOFTWARE=1 CAVEVIEWER_VSYNC=0 ./run_caveviewer.sh\n```\n\n`LIBGL_ALWAYS_SOFTWARE=1` bypasses the GPU driver and asks Mesa to render in\nsoftware. It may be slower, but it is useful on VMs or machines with unreliable\nOpenGL drivers.\n\nFor large maps in a VM, also reduce per-frame GPU upload pressure:\n\n```bash\nLIBGL_ALWAYS_SOFTWARE=1 \\\nCAVEVIEWER_VSYNC=0 \\\nCAVEVIEWER_UPLOAD_CHUNKS_PER_FRAME=1 \\\nCAVEVIEWER_UPLOAD_TIME_BUDGET_MS=1 \\\n./run_caveviewer.sh\n```\n\nDo not rely on VM auto-detection; set these variables explicitly.\n\n---\n\n## Environment Variables\n\nAll variables are optional. Set them in your shell before launching or prefix them inline:\n\n```bash\nCAVEVIEWER_LOG_LEVEL=DEBUG ./run_caveviewer.sh\n```\n\n### Development \u0026 Launch\n\n| Variable | Default | Description |\n|---|---|---|\n| `CAVEVIEWER_DEV_VENV` | `.venv-dev` | Path to the development virtual environment used by `run_caveviewer.sh` and `scripts/dev/install.sh`. |\n| `CAVEVIEWER_MACOS_BUILD_VENV` | _(none)_ | Path to the venv used by the macOS build scripts. |\n| `CAVEVIEWER_LINUX_BUILD_VENV` | _(none)_ | Path to the venv used by the Linux build scripts. |\n| `CAVEVIEWER_HOME` | _(none)_ | Override the home directory CaveViewer uses for preferences and cache files. |\n| `CAVEVIEWER_APP_ICON` | _(bundled icon)_ | Path to a custom application icon file. |\n| `CAVEVIEWER_FORCE_STARTUP_FOCUS` | `0` | Set to `1` to force the main window to the front on startup. Disabled by default on frozen macOS builds to avoid window-placement jumps. |\n| `CAVEVIEWER_LOG_LEVEL` | `INFO` | Logging verbosity. Accepted values: `DEBUG`, `INFO`, `WARNING`, `ERROR`. |\n\n### Update Checking\n\n| Variable | Default | Description |\n|---|---|---|\n| `CAVEVIEWER_GITHUB_REPO` | `KernalPanic/CaveViewer` | The GitHub `owner/repo` used to build the default update manifest URL and sample-maps API URL. Override when running a fork or testing a package from Terminal. |\n| `CAVEVIEWER_UPDATE_BRANCH` | `main` | Git branch used when deriving the default `raw.githubusercontent.com` update manifest URL. Also available as `--update-branch \u003cbranch\u003e` for updater testing from a non-`main` branch. Ignored when `CAVEVIEWER_UPDATE_MANIFEST_URL` is set. |\n| `CAVEVIEWER_UPDATE_CHANNEL` | `stable` | Update manifest channel used when deriving the default manifest URL. Accepted values: `stable`, `prerelease`. Ignored when `CAVEVIEWER_UPDATE_MANIFEST_URL` is set. |\n| `CAVEVIEWER_UPDATE_MANIFEST_URL` | _(derived from repo)_ | Full URL to the JSON update manifest. Overrides the default `raw.githubusercontent.com` path. Useful for pointing at a staging manifest or a custom server. |\n| `CAVEVIEWER_UPDATE_MANIFEST_SIGNATURE_URL` | `\u003cmanifest-url\u003e.sig` | Full URL to the base64 Ed25519 signature for the update manifest. |\n| `CAVEVIEWER_FORCE_UPDATE` | `0` | Set to `1` (or `true`/`yes`) to always show the \"Download Update\" prompt regardless of the manifest version. Also available as `--force-update`. For testing the update UI without waiting for the CDN cache or changing version numbers. |\n| `CAVEVIEWER_MACOS_ARCH` | _(auto)_ | Low-level macOS packaging override. The top-level release dispatcher uses `--target=macos-arm64` or `--target=macos-x86_64`; normal app update checks detect the running process architecture automatically. |\n| `CAVEVIEWER_LINUX_UPDATE_ARCH` | _(auto)_ | Linux publish helper only. Set to `arm64` or `x86_64` to choose which AppImage is written to the Linux update manifest. |\n\nUpdate manifests are signed with the release Ed25519 private key. The bundled\npublic key lives at\n`src/caveviewer/resources/release_signing_public_key.pem`. Startup update\nchecks read the branch/channel manifest first; if it advertises a newer version,\nthe app verifies the manifest signature before offering the download. Missing or\ninvalid signatures are logged as errors and do not change the splash interface.\n\nDefault update checks read committed, architecture-specific main-branch\nmanifests, not GitHub's latest-release or prerelease metadata. macOS uses\n`updates/macos/\u003carm64|x86_64\u003e/stable.json`; Linux follows the same architecture\nsplit. macOS selects the running process architecture, so a Rosetta-launched\nx86_64 build continues on the Intel update channel. Stable publish runs update\nand sign `stable.json`.\nPrerelease publish runs mark the GitHub release as a prerelease and update the\nseparate `prerelease.json` channel, leaving `stable.json` unchanged. For\ndebugging, explicit environment variables can point a source run or packaged\napp launched from Terminal at another branch or manifest URL.\n\nPrerelease branch testing can use the derived prerelease manifest URL after the\nselected branch contains the matching platform manifest and signature. For\nmacOS, confirm the branch contains both files for the process architecture:\n\n```bash\ngit ls-tree -r release/\u003cversion\u003e updates/macos\n```\n\nThe output must include:\n\n```text\nupdates/macos/\u003carm64|x86_64\u003e/prerelease.json\nupdates/macos/\u003carm64|x86_64\u003e/prerelease.json.sig\n```\n\nThen test from a source checkout with `--update-branch` and `--force-update`:\n\n```bash\nCAVEVIEWER_UPDATE_CHANNEL=prerelease \\\n./run_caveviewer.sh --update-branch release/\u003cversion\u003e --force-update\n```\n\nFor a packaged app launched from Terminal, use environment variables instead:\n\n```bash\nCAVEVIEWER_FORCE_UPDATE=1 \\\nCAVEVIEWER_UPDATE_BRANCH=release/\u003cversion\u003e \\\nCAVEVIEWER_UPDATE_CHANNEL=prerelease \\\n./CaveViewer-\u003cversion\u003e-aarch64.AppImage\n```\n\nIf the update checker logs `Update manifest fetch failed with HTTP 404`, the\nderived branch/channel/platform manifest URL does not exist. Either publish that\nplatform's prerelease manifest to the selected branch, switch to a branch that\nhas it, or use `CAVEVIEWER_UPDATE_CHANNEL=stable` if you meant to test the\nstable manifest.\n\nIf the update checker logs `Update manifest fetch failed with HTTP 429`, GitHub\nhas rate-limited the unauthenticated `raw.githubusercontent.com` request. The\nsplash interface stays unchanged. Wait for the limit to clear, switch networks,\nor set `CAVEVIEWER_UPDATE_MANIFEST_URL` to a staging/custom-hosted copy of the\nmanifest; the signature URL defaults to `\u003cmanifest-url\u003e.sig` unless\n`CAVEVIEWER_UPDATE_MANIFEST_SIGNATURE_URL` is set explicitly.\n\nLinux manifests are architecture-specific:\n\n```text\nupdates/linux/arm64/stable.json\nupdates/linux/arm64/prerelease.json\nupdates/linux/x86_64/stable.json\nupdates/linux/x86_64/prerelease.json\n```\n\nmacOS manifests are also architecture-specific:\n\n```text\nupdates/macos/arm64/stable.json\nupdates/macos/arm64/prerelease.json\nupdates/macos/x86_64/stable.json\nupdates/macos/x86_64/prerelease.json\n```\n\nThe `x86_64` files appear after the corresponding Intel channel is first\npublished. Top-level `updates/macos/stable.json` and `prerelease.json` files are\nlegacy ARM64 aliases. Keep each alias and signature byte-for-byte identical to\nits `arm64/` counterpart so older installations continue receiving updates.\n\nmacOS DMG assets include their architecture to prevent uploads from replacing\none another on a shared GitHub release:\n\n```text\nCaveViewer-\u003cversion\u003e-macos-arm64.dmg\nCaveViewer-\u003cversion\u003e-macos-x86_64.dmg\n```\n\nSign a manifest:\n\n```bash\npython3 scripts/sign_update_manifest.py \\\n  updates/macos/arm64/stable.json \\\n  --private-key /path/to/release_signing_private_key.pem\n```\n\nThis writes `updates/macos/arm64/stable.json.sig`. An ARM64 publish copies the\nsigned manifest and signature to the top-level legacy aliases. Release publish\nscripts do not use a default private-key path; set\n`CAVEVIEWER_RELEASE_SIGNING_PRIVATE_KEY` before running stable releases. When\nsigning manually, either set that variable or pass `--private-key`.\n\n### UI \u0026 Rendering\n\n#### OpenGL UI scaling\n\nThe OpenGL viewer renders its overlay text directly with FreeType in screen\npixels. It does not automatically inherit GNOME, KDE, X11, or Wayland desktop\nscaling. On a high-DPI display, set `CAVEVIEWER_UI_TEXT_SCALE` when starting\nCaveViewer. The built-in default is `1.28`; for example, applying an additional\n150% scale gives `1.28 * 1.5 = 1.92`:\n\n```bash\nCAVEVIEWER_UI_TEXT_SCALE=1.92 ./run_caveviewer.sh\n```\n\nThe accepted range is `0.5` through `3.0`. The controls/help overlay derives\nits row height from the resulting FreeType line metrics, so increasing the text\nscale also reserves enough vertical space for each line and its keycap. Text\ninside the fixed-size right-side control panel (steppers and action buttons)\nstays at its designed size because that panel's geometry does not scale.\n\nTo keep a development-machine override, export it in the shell profile or add\nthe following before the final `exec` in `run_caveviewer.sh`:\n\n```bash\nexport CAVEVIEWER_UI_TEXT_SCALE=\"${CAVEVIEWER_UI_TEXT_SCALE:-1.92}\"\n```\n\n`scripts/dev/install.sh` regenerates `run_caveviewer.sh`, so edits made only to\nthe generated launcher are replaced the next time the installer runs. For a\nrepeatable project-specific default, add the same export to the launcher\ntemplate in `scripts/dev/install.sh`; keep the `${...:-...}` form so callers can\nstill override it for an individual run.\n\n| Variable | Default | Description |\n|---|---|---|\n| `CAVEVIEWER_UI_TEXT_SCALE` | `1.28` | Scale multiplier for adaptable in-app overlay text (loading screens, controls/help overlay, and HUD readouts). Text inside fixed-size control geometry is intentionally excluded. `1.0` is the base size. |\n| `CAVEVIEWER_UI_FONT` | _(platform default)_ | Absolute path to a `.ttf`/`.otf`/`.ttc` font file for the in-app FreeType renderer. Overrides the platform font search order. |\n| `CAVEVIEWER_TEXT_AA_MODE` | `light` (macOS), `normal` (others) | FreeType anti-aliasing mode for in-app text. `normal` = standard hinting; `light` = smooth light anti-aliasing (matches macOS CoreText style); `lcd` = LCD sub-pixel rendering. |\n| `CAVEVIEWER_VSYNC` | `1` | Set to `0` to disable vertical sync. Recommended for virtual machines where the virtual display driver can block `swap_buffers()` long enough to freeze the render thread during heavy imports, making the window appear hung. |\n| `LIBGL_ALWAYS_SOFTWARE` | _(unset)_ | Linux OpenGL/Mesa setting. Set to `1` to force software rendering when a VM or GPU driver crashes, freezes, or leaves the app stuck in the graphics driver. |\n| `CAVEVIEWER_NAVIGATION_GUARD` | `1` | Set to `0` to disable the navigation boundary that keeps free-fly movement near occupied map chunks. |\n| `CAVEVIEWER_NAVIGATION_GUARD_RADIUS_CELLS` | `2` | Number of chunk cells around occupied map chunks that remain navigable. Larger values allow more free space around the cave; smaller values keep users closer to rendered chunks. |\n| `CAVEVIEWER_FFMPEG` | _(auto)_ | Path to an `ffmpeg` executable for MP4 recording. If unset, CaveViewer tries system `ffmpeg`, then the bundled `imageio-ffmpeg` executable. |\n| `CAVEVIEWER_RECORDING_DIR` | `~/Movies/CaveViewer` | Directory where clean MP4 flight recordings are saved. The Advanced Settings panel saves this value. |\n| `CAVEVIEWER_RECORDING_FPS` | `30` | Target MP4 recording frame rate. Range: 1–60. Frames are streamed to `ffmpeg`; they are not buffered in memory. |\n| `CAVEVIEWER_RECORDING_MAX_HEIGHT` | `1080` | Maximum output video height. The framebuffer is downscaled before encoding to keep MP4 playback smooth. |\n| `CAVEVIEWER_RECORDING_CRF` | `23` | H.264 quality value passed to `ffmpeg`. Lower is larger/higher quality; higher is smaller/lower quality. Range: 0–51. |\n\n### Streaming Performance\n\nAdvanced Settings opens numeric fields with their effective defaults. Numeric\ninputs use a compact, consistent 12-character width. If a numeric value is\ncleared, only its accepted range immediately appears inside the input as\nmuted, unit-free `minimum-maximum` placeholder text without comparison\noperators; the placeholder itself is never applied or saved as a value.\nEvery field is validated as it changes. An invalid value is highlighted and\nkeeps the shared validation message visible while the other inputs become\ntemporarily read-only and the Apply button is disabled. Read-only inputs retain\ntheir normal dark appearance. Correcting the value immediately unlocks the\nform; valid values are normalized when focus leaves the field. A focused\nrequired field may remain temporarily empty while the user replaces its value;\nApply is disabled immediately while any required value is blank, while the\nrequired message and read-only form lock appear only after focus leaves that\nempty field. Cancel, Escape, and window close remain available and discard\nunapplied edits. Advisory worker-thread warnings do not lock the form.\n\nThe Advanced Settings implementation is split by responsibility:\n`src/caveviewer/gui/advanced_settings.py` owns the typed `SettingSpec` schema,\nvalidation, persistence, and environment mapping;\n`src/caveviewer/gui/advanced_settings_form.py` owns focus/change/blur/apply\nstate transitions; `src/caveviewer/gui/advanced_settings_dialog.py` only\nrenders that state into Tk widgets; and\n`src/caveviewer/core/worker_config.py` resolves the effective streaming/import\nworker counts while honoring reserved logical CPUs. Only immutable, validated\n`AdvancedSettings` snapshots may cross into\npersistence or the runtime environment. Invalid saved or environment values\nfall back independently to that field's valid default, so one stale value does\nnot discard the rest of the configuration. Settings are saved through an\natomic temporary-file replacement; a write failure remains visible in the\ndialog and does not close it or alter the previous settings file.\n\nThe splash screen, Advanced Settings, and Sample Maps dialogs share their Tk\ncolor and control tokens through `src/caveviewer/gui/tk_theme.py`. Map-folder\nvalidation lives in `src/caveviewer/gui/map_selection.py`, allowing both\nmap-selection dialogs to reuse it without importing private splash-screen\nimplementation details.\n\nRuntime chunk streaming is also split by policy boundary:\n`src/caveviewer/core/hardware_memory.py` detects system/GPU memory and parses\ntarget fractions; `src/caveviewer/core/streaming_budget.py` contains pure\nchunk-size estimation and residency-cap calculation;\n`src/caveviewer/core/streaming_scheduler.py` owns the bounded ready backlog,\nspatial selection, and eviction policy; and\n`src/caveviewer/core/streaming_world.py` coordinates worker lifecycle and\nrender-thread callbacks. Map imports now write only the cache artifacts used by\nruntime streaming and the minimap. Existing caches containing retired auxiliary\nartifacts remain readable; those extra files are ignored.\n\n| Variable | Default | Accepted range | Description |\n|---|---|---|---|\n| `CAVEVIEWER_MEMORY_UTILIZATION_TARGET` | `8` | 1-80% | Percentage of system RAM the chunk streaming system targets for loaded chunk data. |\n| `CAVEVIEWER_GPU_MEMORY_GB` | _(auto-detect)_ | 0.5-50 GB (optional) | Override the GPU memory size used by the streaming budget. Linux AMD GPUs are detected through DRM sysfs and NVIDIA GPUs through `nvidia-smi`; use this when detection is unavailable or inaccurate. |\n| `CAVEVIEWER_GPU_MEMORY_UTILIZATION_TARGET` | `70` | 1-80% | Percentage of GPU memory the chunk streaming system targets. |\n| `CAVEVIEWER_IO_WORKERS` | `2` | Integer 1-32 | Requested maximum number of background threads for loading chunk files from disk. The runtime reduces this when necessary to honor `CAVEVIEWER_IO_RESERVED_CPUS`. Advanced Settings warns above 5 because high thread counts may reduce performance and cause out of memory errors on machines with less than 16 GB of RAM. |\n| `CAVEVIEWER_IO_RESERVED_CPUS` | `3` | Integer 2-32 | Logical CPUs kept out of the loading worker pool. Effective workers are capped at `logical CPUs - reserved CPUs`, with at least one worker. |\n| `CAVEVIEWER_UPLOAD_CHUNKS_PER_FRAME` | `1` | 1-16 | Maximum number of chunk GPU uploads per render frame. Increase to load geometry faster at the cost of brief frame-time spikes. |\n| `CAVEVIEWER_UPLOAD_TIME_BUDGET_MS` | `3.0` | 0.5-50 ms | Soft per-frame time budget for GPU uploads. |\n\n### Map Import (First-Time Parsing)\n\n| Variable | Default | Accepted range | Description |\n|---|---|---|---|\n| `CAVEVIEWER_CHUNK_SIZE_METERS` | `8` | 0.01-512 m | Spatial chunk size used when building a new chunk cache. Does not affect already-cached maps. |\n| `CAVEVIEWER_OBJ_SCAN_THROTTLE_MS` | `1` (Windows), `0` (others) | 0-50 ms | Time yielded between OBJ scanning steps. A small value keeps the UI responsive during large imports on Windows; `0` disables throttling. |\n| `CAVEVIEWER_CHUNK_BUILD_WORKERS` | `1` | Integer 1-32 | Requested maximum threads used while writing chunk files during import. The runtime reduces this when necessary to honor `CAVEVIEWER_CHUNK_BUILD_RESERVED_CPUS`. Advanced Settings warns above 5 because high thread counts may reduce performance and cause out of memory errors on machines with less than 16 GB of RAM. |\n| `CAVEVIEWER_CHUNK_BUILD_RESERVED_CPUS` | `2` | Integer 2-32 | Logical CPUs kept out of the cache-building worker pool. Effective workers are capped at `logical CPUs - reserved CPUs`, with at least one worker. |\n\n### Sample Maps\n\n| Variable | Default | Description |\n|---|---|---|\n| `CAVEVIEWER_SAMPLE_MAPS_REPO` | `KernalPanic/CaveViewer` | GitHub `owner/repo` for the sample maps release. |\n| `CAVEVIEWER_SAMPLE_DATA_TAG` | `sample-data` | Release tag to fetch sample map assets from. |\n| `CAVEVIEWER_SAMPLE_MAPS_API_URL` | _(derived from repo + tag)_ | Full GitHub release API URL. Overrides the repo/tag variables when set. |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkernalpanic%2Fcaveviewer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkernalpanic%2Fcaveviewer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkernalpanic%2Fcaveviewer/lists"}