diff --git a/.agents/skills/agent-tool-catalog/SKILL.md b/.agents/skills/agent-tool-catalog/SKILL.md new file mode 100644 index 00000000000..42c47be8d94 --- /dev/null +++ b/.agents/skills/agent-tool-catalog/SKILL.md @@ -0,0 +1,47 @@ +--- +name: agent-tool-catalog +description: >- + Choose external agent skills, MCP servers, and self-host utilities from + ebluffy/agent-tool-catalog instead of inventing tooling from memory. Use when + picking UI/design skills, MCP, token compression, search, Windows computer-use, + OCR, or security tooling for this workspace. +--- + +# Agent tool catalog + +Source of truth (fetch live, do not rely on memory): + +https://github.com/ebluffy/agent-tool-catalog + +Raw README: + +https://raw.githubusercontent.com/ebluffy/agent-tool-catalog/main/README.md + +## When to use + +Before installing or recommending a new skill, MCP, UI anti-slop pack, Windows desktop MCP, OCR stack, search/archive utility, or security scanner — **read that README** and prefer rows listed there over inventing alternatives. + +## Owyx defaults (subset) + +For this monorepo (`apps/app*`, `owyxsite/`, `brand/`), prefer: + +| Need | Catalog pick | +|------|----------------| +| UI / anti-AI-slop | [hallmark](https://github.com/Nutlope/hallmark) + [ui-ux-pro-max-skill](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill) — still respect `brand/DESIGN.md` and Owyx tokens first | +| Engineering process skills | [mattpocock/skills](https://github.com/mattpocock/skills), [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills), [spec-kit](https://github.com/github/spec-kit) | +| Token / log compression | [headroom](https://github.com/headroomlabs-ai/headroom), [rtk](https://github.com/rtk-ai/rtk) | +| Code graph MCP | [code-review-graph](https://github.com/tirth8205/code-review-graph) or [codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp) | +| Windows desktop MCP | [Windows-MCP](https://github.com/CursorTouch/Windows-MCP) (default) | +| Skill install safety | scan with [SkillSpector](https://github.com/NVIDIA/SkillSpector) before adding third-party skills | + +Do **not** pull Unity, trading, manga, or unrelated companion stacks into Owyx work unless the task explicitly needs them. + +## Repo skills stay primary + +Existing `.agents/skills/` (api-module, cross-platform-pages, figma-mcp, i18n-pass, review-changelog, tanstack-query) and root/`owyxsite` `AGENTS.md` win for Modrinth-fork and Owyx product conventions. The catalog only fills gaps for **external** tooling. + +## Do not + +- Commit secrets, private project names, or live keys into the catalog or this skill. +- Vendor entire third-party skill trees into this fork unless the user asks — link + install command is enough. +- Run pentest tools (`shannon`, `pentagi`) except against **our own** staging with explicit approval. diff --git a/.cursor/rules/semver.mdc b/.cursor/rules/semver.mdc new file mode 100644 index 00000000000..af6e87d9127 --- /dev/null +++ b/.cursor/rules/semver.mdc @@ -0,0 +1,34 @@ +--- +description: Semantic Versioning (SemVer) for Owyx releases and any versioned software in this workspace +alwaysApply: true +--- + +# Semantic Versioning (SemVer) + +Canonical scheme: **MAJOR.MINOR.PATCH** (example `2.5.1`). Spec: https://semver.org/ + +## Meaning + +| Part | When to bump | +|------|----------------| +| **MAJOR** | Breaking / incompatible changes | +| **MINOR** | New features, backward compatible | +| **PATCH** | Bug fixes only, no new features | + +## Pre-release and build metadata + +- Pre-release: hyphen suffix — `1.0.0-alpha`, `2.1.0-beta.2`, `1.2.0-rc.1` +- Build metadata: plus suffix — `1.0.0+20260916` (informational; does not affect precedence) + +Do **not** use ad-hoc suffixes like `-local` for public GitHub releases. + +## Owyx launcher baseline + +- Public release baseline: **`0.2.0`** (first OBT line). +- Next releases: choose bump from the table above (`0.2.1` for fixes, `0.3.0` for features, `1.0.0` when stable API/UX promises begin). +- Tag as `vMAJOR.MINOR.PATCH` (e.g. `v0.2.1`). +- Drive builds via `owyx-github-release` workflow with that version; keep Cargo / frontend / `latest.json` in sync through `scripts/set-app-version.js`. + +## General (other tools in this workspace) + +Same SemVer rules apply whenever you cut versions for site packages, scripts, or side projects unless a file explicitly documents a different scheme. diff --git a/.github/ISSUE_TEMPLATE/1-app-bug.yml b/.github/ISSUE_TEMPLATE/1-app-bug.yml index fad0c15ca1f..f94e93c7ffe 100644 --- a/.github/ISSUE_TEMPLATE/1-app-bug.yml +++ b/.github/ISSUE_TEMPLATE/1-app-bug.yml @@ -1,60 +1,59 @@ -name: 🎮 Bug with Modrinth App -description: For issues with Modrinth App. +name: 🎮 Bug with Owyx launcher +description: Bugs in the Owyx desktop launcher (this fork — not Modrinth App). labels: [app] type: 'bug' body: + - type: markdown + attributes: + value: | + This tracker is for **Owyx** ([ebluffy/Owyx](https://github.com/ebluffy/Owyx)), a rebranded fork of [modrinth/code](https://github.com/modrinth/code). + Upstream Modrinth App issues belong on the Modrinth repo. - type: checkboxes attributes: label: Please confirm the following. options: - - label: I checked the [existing issues](https://github.com/modrinth/code/issues?q=is%3Aissue) for duplicate problems + - label: I checked existing issues on this fork for duplicates required: true - - label: I have tried resolving the issue using the [support portal](https://support.modrinth.com) + - label: I am reporting an Owyx build (not the official Modrinth App) required: true - - label: I have ensured my Modrinth App installation is up to date + - label: My Owyx installation is up to date (or I note the commit/tag) required: true - type: input id: version attributes: - label: What version of the Modrinth App are you using? - description: Find this in ⚙️ Settings (bottom right) -> After Modrinth App (bottom left) + label: What version / commit of Owyx are you using? + description: Settings → about, or `git rev-parse --short HEAD` if building from source validations: required: true - type: dropdown id: oses attributes: - label: What operating systems are you seeing the problem on? + label: Operating system multiple: true options: - Windows - - MacOS + - macOS - Linux + validations: + required: true - type: textarea + id: description attributes: label: Describe the bug - description: A clear and concise description of what the bug is. Include screenshots if applicable. + description: What happened? What did you expect? validations: required: true - type: textarea + id: steps attributes: label: Steps to reproduce - description: Steps to reproduce the behavior. - placeholder: | - 1. Go to '...' - 2. Click on '...' - 3. Scroll down to '...' - 4. See error + description: Minimal steps validations: - required: false - - type: textarea - attributes: - label: Expected behavior - description: A clear and concise description of what you expected to happen. - validations: - required: false + required: true - type: textarea + id: logs attributes: - label: Additional context - description: Add any other context about the problem here. + label: Logs / screenshots + description: Paste relevant logs if you have them validations: required: false diff --git a/.github/ISSUE_TEMPLATE/2-site-bug.yml b/.github/ISSUE_TEMPLATE/2-site-bug.yml new file mode 100644 index 00000000000..f184eb79234 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2-site-bug.yml @@ -0,0 +1,44 @@ +name: 🌐 Bug with owyx.site +description: Bugs on the Owyx website / account / API (control plane). +labels: [bug] +type: 'bug' +body: + - type: markdown + attributes: + value: | + For **owyx.site** / **api.owyx.site** (account, profile, admin, friends API, catalog). + Launcher bugs → use **Bug with Owyx launcher**. Upstream Modrinth → [modrinth/code](https://github.com/modrinth/code). + - type: checkboxes + attributes: + label: Please confirm the following. + options: + - label: I checked existing issues for duplicates + required: true + - label: This is about Owyx site/API, not Modrinth.com / Hosting + required: true + - type: dropdown + id: area + attributes: + label: Area + options: + - Website UI + - Auth / account + - API (api.owyx.site) + - Admin + - Other + validations: + required: true + - type: textarea + id: description + attributes: + label: What happened? + description: Steps to reproduce, expected vs actual, screenshots if useful + validations: + required: true + - type: input + id: url + attributes: + label: Page / endpoint URL + placeholder: https://owyx.site/... + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/2-web-bug.yml b/.github/ISSUE_TEMPLATE/2-web-bug.yml deleted file mode 100644 index 983279c1471..00000000000 --- a/.github/ISSUE_TEMPLATE/2-web-bug.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: 🌐 Bug with Modrinth.com -description: For issues with the Modrinth website. -labels: [website] -type: 'bug' -body: - - type: checkboxes - attributes: - label: Please confirm the following. - options: - - label: I checked the [existing issues](https://github.com/modrinth/code/issues?q=is%3Aissue) for duplicate problems - required: true - - label: I have tried resolving the issue using the [support portal](https://support.modrinth.com) - required: true - - type: dropdown - id: browsers - attributes: - label: What browsers are you seeing the problem on? - multiple: true - options: - - Chrome (including Arc, Brave, Opera, Vivaldi) - - Microsoft Edge - - Firefox - - Safari - - Other (please specify) - - type: textarea - attributes: - label: Describe the bug - description: A clear and concise description of what the bug is. Include screenshots if applicable. - validations: - required: false - - type: textarea - attributes: - label: Steps to reproduce - description: Steps to reproduce the behavior. - placeholder: | - 1. Go to '...' - 2. Click on '...' - 3. Scroll down to '...' - 4. See error - validations: - required: false - - type: textarea - attributes: - label: Expected behavior - description: A clear and concise description of what you expected to happen. - validations: - required: false - - type: textarea - attributes: - label: Additional context - description: Add any other context about the problem here. - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/3-hosting-bug.yml b/.github/ISSUE_TEMPLATE/3-hosting-bug.yml deleted file mode 100644 index dbb4dca0bbc..00000000000 --- a/.github/ISSUE_TEMPLATE/3-hosting-bug.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: 👥 Bug with Modrinth Hosting -description: For issues with a Modrinth Hosting product. -labels: [hosting] -type: 'bug' -body: - - type: checkboxes - attributes: - label: Please confirm the following. - options: - - label: I checked the [existing issues](https://github.com/modrinth/code/issues?q=is%3Aissue) for duplicate problems - required: true - - label: I have tried resolving the issue using the [support portal](https://support.modrinth.com) - required: true - - type: dropdown - id: issue-location - attributes: - label: Is this an issue in the control panel or with the Minecraft server itself? - options: - - Control panel (on Modrinth.com) - - Minecraft server - validations: - required: true - - type: dropdown - id: browsers - attributes: - label: What browsers are you seeing the problem on? (if a panel issue) - multiple: true - options: - - N/A - - Chrome (including Arc, Brave, Opera, Vivaldi) - - Microsoft Edge - - Firefox - - Safari - - Other (please specify) - - type: textarea - attributes: - label: Describe the bug - description: A clear and concise description of what the bug is. Include screenshots if applicable. - validations: - required: false - - type: textarea - attributes: - label: Steps to reproduce - description: Steps to reproduce the behavior. - placeholder: | - 1. Go to '...' - 2. Click on '...' - 3. Scroll down to '...' - 4. See error - validations: - required: false - - type: textarea - attributes: - label: Expected behavior - description: A clear and concise description of what you expected to happen. - validations: - required: false - - type: textarea - attributes: - label: Additional context - description: Add any other context about the problem here. - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/4-api-bug.yml b/.github/ISSUE_TEMPLATE/4-api-bug.yml deleted file mode 100644 index 00fac9bac59..00000000000 --- a/.github/ISSUE_TEMPLATE/4-api-bug.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: 🛠 Bug with Modrinth API -description: For issues with the Modrinth API for developers. -labels: [api, backend] -type: 'bug' -body: - - type: checkboxes - attributes: - label: Please confirm the following. - options: - - label: I checked the [existing issues](https://github.com/modrinth/code/issues?q=is%3Aissue) for duplicate problems - required: true - - label: I have tried resolving the issue using the [support portal](https://support.modrinth.com) - required: true - - type: textarea - attributes: - label: Describe the bug - description: A clear and concise description of what the bug is. Include screenshots if applicable. - validations: - required: false - - type: textarea - attributes: - label: Steps to reproduce - description: Steps to reproduce the behavior. - placeholder: | - 1. Go to '...' - 2. Click on '...' - 3. Scroll down to '...' - 4. See error - validations: - required: false - - type: textarea - attributes: - label: Expected behavior - description: A clear and concise description of what you expected to happen. - validations: - required: false - - type: textarea - attributes: - label: Additional context - description: Add any other context about the problem here. - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/5-feature-request.yml b/.github/ISSUE_TEMPLATE/5-feature-request.yml index f821235efa0..94303b261d4 100644 --- a/.github/ISSUE_TEMPLATE/5-feature-request.yml +++ b/.github/ISSUE_TEMPLATE/5-feature-request.yml @@ -1,45 +1,37 @@ name: 💡 Feature request -description: Suggest an idea +description: Suggest an idea for Owyx type: 'feature' body: + - type: markdown + attributes: + value: | + This tracker is for **Owyx** ([ebluffy/Owyx](https://github.com/ebluffy/Owyx)), a fork/rebrand of [modrinth/code](https://github.com/modrinth/code). - type: checkboxes attributes: label: Please confirm the following. options: - - label: I checked the [existing issues](https://github.com/modrinth/code/issues?q=is%3Aissue) for duplicate feature requests + - label: I checked existing issues on this fork for duplicates required: true - type: dropdown id: projects attributes: - label: What parts of Modrinth is your feature request related too? + label: What part of Owyx is this about? multiple: true options: - - Modrinth App - - Modrinth.com website - - Modrinth Hosting - - Modrinth API for developers - - type: textarea - attributes: - label: Is your suggested feature related to a problem? Please describe. - description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + - Owyx launcher + - owyxsite (control plane) + - Docs / packaging validations: - required: false + required: true - type: textarea attributes: - label: Describe the solution you'd like - description: A clear and concise description of what you want to happen. + label: Problem + description: What is frustrating today? validations: required: false - type: textarea attributes: - label: Describe alternatives you've considered - description: A clear and concise description of any alternative solutions or features you've considered. + label: Proposed solution validations: - required: false - - type: textarea - attributes: - label: Additional context - description: Add any other context or screenshots about the suggested enhancement here. - validations: - required: false + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 0540a64ec71..808ef8be7a5 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ -blank_issues_enabled: false +blank_issues_enabled: true contact_links: - - name: 🫶 Support portal - about: Get support using through our support website. - url: https://support.modrinth.com - - name: 💬 Chat on Discord - about: Join our Discord server to chat about Modrinth. - url: https://discord.modrinth.com + - name: Owyx website + about: Product site / account (owyx.site) + url: https://owyx.site + - name: Upstream Modrinth (not this fork) + about: Issues about Modrinth itself belong on modrinth/code — this repo is an Owyx fork/rebrand. + url: https://github.com/modrinth/code diff --git a/.github/assets/api_cover.png b/.github/assets/api_cover.png deleted file mode 100644 index 5de90400836..00000000000 Binary files a/.github/assets/api_cover.png and /dev/null differ diff --git a/.github/assets/app_cover.png b/.github/assets/app_cover.png deleted file mode 100644 index 5f98efe30a7..00000000000 Binary files a/.github/assets/app_cover.png and /dev/null differ diff --git a/.github/assets/monorepo_cover.png b/.github/assets/monorepo_cover.png deleted file mode 100644 index f768e4ddb66..00000000000 Binary files a/.github/assets/monorepo_cover.png and /dev/null differ diff --git a/.github/assets/web_cover.png b/.github/assets/web_cover.png deleted file mode 100644 index 5c922277c10..00000000000 Binary files a/.github/assets/web_cover.png and /dev/null differ diff --git a/.github/workflows/daedalus-docker.yml b/.github/workflows/daedalus-docker.yml index ecc000fb55d..9f90f63f242 100644 --- a/.github/workflows/daedalus-docker.yml +++ b/.github/workflows/daedalus-docker.yml @@ -44,7 +44,10 @@ jobs: HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} REPO: ${{ github.repository }} run: | - if [ "$EVENT_NAME" != "pull_request" ] || [ "$HEAD_REPO" = "$REPO" ]; then + # Namespace runners exist only on modrinth/code — forks always use ubuntu-latest. + if [ "${{ github.repository }}" != "modrinth/code" ]; then + echo "internal=false" >> $GITHUB_OUTPUT + elif [ "$EVENT_NAME" != "pull_request" ] || [ "$HEAD_REPO" = "$REPO" ]; then echo "internal=true" >> $GITHUB_OUTPUT else echo "internal=false" >> $GITHUB_OUTPUT diff --git a/.github/workflows/frontend-deploy.yml b/.github/workflows/frontend-deploy.yml index 79b73c57ef0..658b8341af5 100644 --- a/.github/workflows/frontend-deploy.yml +++ b/.github/workflows/frontend-deploy.yml @@ -40,6 +40,7 @@ concurrency: jobs: deploy: + if: github.repository_owner == 'modrinth' runs-on: namespace-profile-modrinth-frontend permissions: contents: read diff --git a/.github/workflows/frontend-docker.yml b/.github/workflows/frontend-docker.yml index 48c5f0c76b3..13d66c9cb46 100644 --- a/.github/workflows/frontend-docker.yml +++ b/.github/workflows/frontend-docker.yml @@ -23,6 +23,7 @@ concurrency: jobs: build: name: Build frontend + if: github.repository_owner == 'modrinth' runs-on: namespace-profile-modrinth-frontend permissions: contents: read diff --git a/.github/workflows/labrinth-build.yml b/.github/workflows/labrinth-build.yml index dd19280d2d8..0f916dfa9a4 100644 --- a/.github/workflows/labrinth-build.yml +++ b/.github/workflows/labrinth-build.yml @@ -49,7 +49,10 @@ jobs: HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} REPO: ${{ github.repository }} run: | - if [ "$EVENT_NAME" != "pull_request" ] || [ "$HEAD_REPO" = "$REPO" ]; then + # Namespace runners exist only on modrinth/code — forks always use ubuntu-latest. + if [ "${{ github.repository }}" != "modrinth/code" ]; then + echo "internal=false" >> $GITHUB_OUTPUT + elif [ "$EVENT_NAME" != "pull_request" ] || [ "$HEAD_REPO" = "$REPO" ]; then echo "internal=true" >> $GITHUB_OUTPUT else echo "internal=false" >> $GITHUB_OUTPUT diff --git a/.github/workflows/owyx-github-release.yml b/.github/workflows/owyx-github-release.yml new file mode 100644 index 00000000000..175ba032231 --- /dev/null +++ b/.github/workflows/owyx-github-release.yml @@ -0,0 +1,281 @@ +# Build Owyx installers and publish a GitHub Release (with updater artifacts). +name: Owyx GitHub Release + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + version: + description: 'Semver without v prefix (e.g. 0.2.0)' + required: true + default: '0.2.0' + +permissions: + contents: write + +concurrency: + group: owyx-github-release-${{ github.ref }}-${{ github.event_name }} + cancel-in-progress: true + +jobs: + build-windows: + name: Windows NSIS + runs-on: windows-latest + outputs: + version: ${{ steps.ver.outputs.version }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - id: ver + shell: bash + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + V="${{ inputs.version }}"; V="${V#v}" + else + V="${GITHUB_REF_NAME#v}" + fi + echo "version=$V" >> "$GITHUB_OUTPUT" + echo "version=$V" + + - uses: dtolnay/rust-toolchain@stable + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + - run: corepack enable + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '17' + + - name: Install dependencies + shell: bash + run: pnpm install --frozen-lockfile + + - name: Set Cargo package version + shell: bash + env: + V: ${{ steps.ver.outputs.version }} + run: node scripts/set-app-version.js "$V" + + - name: Prepare app-lib compile-time env (prod API hosts) + shell: bash + run: cp packages/app-lib/.env.prod packages/app-lib/.env + + - name: Build app frontend (release) + shell: bash + env: + NODE_OPTIONS: --max-old-space-size=8192 + VITE_OWYX_CLIENT_KEY: ${{ secrets.OWYX_CLIENT_KEY }} + VITE_OWYX_API_BASE_URL: https://api.owyx.site + run: pnpm turbo run build --filter=@modrinth/app-frontend + + - name: Build NSIS installer + updater + shell: bash + working-directory: apps/app + env: + NODE_OPTIONS: --max-old-space-size=8192 + VITE_OWYX_CLIENT_KEY: ${{ secrets.OWYX_CLIENT_KEY }} + VITE_OWYX_API_BASE_URL: https://api.owyx.site + OWYX_DISCORD_APP_ID: ${{ secrets.OWYX_DISCORD_APP_ID }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + run: pnpm exec tauri build --config tauri-owyx-release.conf.json --bundles nsis + + - uses: actions/upload-artifact@v4 + with: + name: owyx-windows-nsis + path: | + target/release/bundle/nsis/* + apps/app/target/release/bundle/nsis/* + if-no-files-found: error + + build-linux: + name: Linux AppImage + runs-on: ubuntu-22.04 + outputs: + version: ${{ steps.ver.outputs.version }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - id: ver + shell: bash + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + V="${{ inputs.version }}"; V="${V#v}" + else + V="${GITHUB_REF_NAME#v}" + fi + echo "version=$V" >> "$GITHUB_OUTPUT" + + - name: Install WebKit deps + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf + + - uses: dtolnay/rust-toolchain@stable + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + - run: corepack enable + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '17' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Set Cargo package version + env: + V: ${{ steps.ver.outputs.version }} + run: node scripts/set-app-version.js "$V" + + - name: Prepare app-lib compile-time env (prod API hosts) + run: cp packages/app-lib/.env.prod packages/app-lib/.env + + - name: Build app frontend (release) + env: + NODE_OPTIONS: --max-old-space-size=8192 + VITE_OWYX_CLIENT_KEY: ${{ secrets.OWYX_CLIENT_KEY }} + VITE_OWYX_API_BASE_URL: https://api.owyx.site + run: pnpm turbo run build --filter=@modrinth/app-frontend + + - name: Build AppImage + updater + working-directory: apps/app + env: + NODE_OPTIONS: --max-old-space-size=8192 + VITE_OWYX_CLIENT_KEY: ${{ secrets.OWYX_CLIENT_KEY }} + VITE_OWYX_API_BASE_URL: https://api.owyx.site + OWYX_DISCORD_APP_ID: ${{ secrets.OWYX_DISCORD_APP_ID }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + run: pnpm exec tauri build --config tauri-owyx-release.conf.json --bundles appimage + + - uses: actions/upload-artifact@v4 + with: + name: owyx-linux-appimage + path: | + target/release/bundle/appimage/* + apps/app/target/release/bundle/appimage/* + if-no-files-found: error + + release: + name: Publish GitHub Release + needs: [build-windows, build-linux] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - id: ver + shell: bash + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + V="${{ inputs.version }}"; V="${V#v}"; TAG="v$V" + else + TAG="${GITHUB_REF_NAME}"; V="${TAG#v}" + fi + echo "version=$V" >> "$GITHUB_OUTPUT" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + + - uses: actions/download-artifact@v4 + with: + path: dist + + - name: Create tag + release + latest.json + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.ver.outputs.tag }} + VERSION: ${{ steps.ver.outputs.version }} + run: | + set -euo pipefail + # Prefer real commit subjects since previous tag (not a stale hardcoded blurb). + PREV="$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || true)" + if [ -n "${PREV}" ]; then + CHANGELOG="$(git log "${PREV}..${TAG}" --pretty=format:'- %s' --no-merges | head -n 40)" + else + CHANGELOG="$(git log -n 20 --pretty=format:'- %s' --no-merges)" + fi + if [ -z "${CHANGELOG}" ]; then + CHANGELOG="- Build ${VERSION}" + fi + NOTES=$(cat </dev/null 2>&1; then + git tag -a "$TAG" -m "Owyx $VERSION" + git push origin "refs/tags/$TAG" + fi + fi + + mapfile -t FILES < <(find dist -type f \( -name '*.exe' -o -name '*.AppImage' -o -name '*.sig' -o -name '*.zip' \) | sort) + echo "Files:"; printf '%s\n' "${FILES[@]}" + test "${#FILES[@]}" -gt 0 + + WIN_URL=""; WIN_SIG=""; LINUX_URL=""; LINUX_SIG="" + while IFS= read -r -d '' sigf; do + art="${sigf%.sig}" + [[ -f "$art" ]] || continue + b=$(basename "$art") + case "$b" in + *.nsis.zip|*-setup.exe) + WIN_URL="https://github.com/ebluffy/Owyx/releases/download/${TAG}/${b}" + WIN_SIG=$(cat "$sigf") + ;; + *.AppImage) + LINUX_URL="https://github.com/ebluffy/Owyx/releases/download/${TAG}/${b}" + LINUX_SIG=$(cat "$sigf") + ;; + esac + done < <(find dist -type f -name '*.sig' -print0) + + export WIN_URL WIN_SIG LINUX_URL LINUX_SIG VERSION PUB_DATE + PUB_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + export PUB_DATE + python3 - <<'PY' > latest.json + import json, os, sys + platforms = {} + win_url, win_sig = os.environ.get("WIN_URL",""), os.environ.get("WIN_SIG","") + lin_url, lin_sig = os.environ.get("LINUX_URL",""), os.environ.get("LINUX_SIG","") + if win_url and win_sig: + platforms["windows-x86_64"] = {"url": win_url, "signature": win_sig.strip()} + if lin_url and lin_sig: + platforms["linux-x86_64"] = {"url": lin_url, "signature": lin_sig.strip()} + if not platforms: + print("WARNING: no signed updater artifacts; publishing installers only", file=sys.stderr) + print(json.dumps({ + "version": os.environ["VERSION"], + "notes": f"Owyx {os.environ['VERSION']}", + "pub_date": os.environ.get("PUB_DATE",""), + "platforms": platforms, + }, indent=2)) + PY + echo "latest.json:"; cat latest.json + if [[ -s latest.json ]] && grep -q '"platforms"' latest.json; then + FILES+=("$(pwd)/latest.json") + fi + + if gh release view "$TAG" >/dev/null 2>&1; then + gh release upload "$TAG" "${FILES[@]}" --clobber + gh release edit "$TAG" --notes "$NOTES" + else + gh release create "$TAG" "${FILES[@]}" --title "Owyx ${VERSION}" --notes "$NOTES" + fi diff --git a/.github/workflows/slash-cmds.yml b/.github/workflows/slash-cmds.yml index e35babeddff..964a347e8a6 100644 --- a/.github/workflows/slash-cmds.yml +++ b/.github/workflows/slash-cmds.yml @@ -8,7 +8,7 @@ permissions: {} jobs: dispatch-command: - if: ${{ github.event.sender.type == 'User' && contains(fromJSON('["OWNER", "MEMBER"]'), github.event.comment.author_association) }} + if: ${{ github.repository_owner == 'modrinth' && github.event.sender.type == 'User' && contains(fromJSON('["OWNER", "MEMBER"]'), github.event.comment.author_association) }} runs-on: namespace-profile-tiny-arm64 steps: - name: Slash Command Dispatch diff --git a/.github/workflows/theseus-build.yml b/.github/workflows/theseus-build.yml index 6b83d5bfde1..b929158415e 100644 --- a/.github/workflows/theseus-build.yml +++ b/.github/workflows/theseus-build.yml @@ -1,4 +1,4 @@ -name: Modrinth App build +name: Owyx App build on: push: branches: @@ -42,6 +42,8 @@ concurrency: jobs: build: name: Build + # Upstream Namespace runners only — Owyx releases use owyx-github-release.yml. + if: github.repository == 'modrinth/code' env: VITE_STRIPE_PUBLISHABLE_KEY: pk_live_51JbFxJJygY5LJFfKLVVldb10HlLt24p421OWRsTOWc5sXYFOnFUXWieSc6HD3PHo25ktx8db1WcHr36XGFvZFVUz00V9ixrCs5 RUSTC_WRAPPER: 'sccache' @@ -107,9 +109,9 @@ jobs: GIT_HASH=$(git rev-parse --short HEAD) cat > apps/app/tauri-dev.conf.json <- + github.repository == 'modrinth/code' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && startsWith(github.event.workflow_run.head_branch, 'v') diff --git a/.github/workflows/turbo-ci.yml b/.github/workflows/turbo-ci.yml index 768b7a64b5e..54ceee4faf5 100644 --- a/.github/workflows/turbo-ci.yml +++ b/.github/workflows/turbo-ci.yml @@ -62,7 +62,9 @@ jobs: build: name: Lint and Test - runs-on: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'namespace-profile-modrinth-turbo' || 'ubuntu-latest' }} + # Namespace runners exist only on modrinth/code. Same-repo PRs on forks set + # internal=true but have no Namespace capacity — use ubuntu-latest there. + runs-on: ${{ needs.skip-if-clean.outputs.internal == 'true' && github.repository == 'modrinth/code' && 'namespace-profile-modrinth-turbo' || 'ubuntu-latest' }} needs: [skip-if-clean] if: ${{ needs.skip-if-clean.outputs.skip != 'true' }} @@ -98,7 +100,7 @@ jobs: run: corepack enable - name: Set up caches - if: needs.skip-if-clean.outputs.internal == 'true' + if: needs.skip-if-clean.outputs.internal == 'true' && github.repository == 'modrinth/code' uses: namespacelabs/nscloud-cache-action@c5f8dab7560444c4bf8dbc64f1b203431873c547 # v1.6.1 with: cache: | @@ -107,7 +109,7 @@ jobs: apt - name: Set up Turborepo caching - if: needs.skip-if-clean.outputs.internal == 'true' + if: needs.skip-if-clean.outputs.internal == 'true' && github.repository == 'modrinth/code' uses: namespace-actions/setup-turbocache@f96bd6befa13ae74b2858a5be49573640d4636f5 # v0.0.2 - name: Install apt dependencies diff --git a/.gitignore b/.gitignore index d442251099c..9cad92b1666 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,12 @@ apps/frontend/src/public/robots.txt /test_result.md packages/tooling-config/script-utils/import-sort.js + + + +legacy/**/frpc.toml +!legacy/**/frpc.toml.example + + +promt.md + diff --git a/.idea/icon.svg b/.idea/icon.svg deleted file mode 100644 index 834085367eb..00000000000 --- a/.idea/icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/AGENTS.md b/AGENTS.md index 4e46b24039b..fc768be5f0b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,96 +1,47 @@ -# Modrinth Monorepo +# Agent notes — Owyx launcher -This is the Modrinth monorepo — it contains all Modrinth projects, both frontend and backend. When entering a project, either to edit or analyse, you should read its AGENTS.md. +Canonical product: **Owyx** = site (`owyxsite/`) + launcher (`apps/app` + `apps/app-frontend` + `packages/app-lib`), Modrinth-App-like shell without their brand/Hosting. Not a single-game-server product; plugin is out of scope (history in OwyxOld). -## Architecture +This repo is a **fork of modrinth/code** — same package licenses (GPL-3 for the app); no Modrinth trademarks. See root `README.md`, `LICENSE`, `COPYING.md`, `TRADEMARK.md`. -- **Monorepo tooling:** [Turborepo](https://turbo.build/) (`turbo.jsonc`) + [pnpm workspaces](https://pnpm.io/workspaces) (`pnpm-workspace.yaml`) -- **Frontend:** Vue 3 / Nuxt 3, Tailwind CSS v3 -- **Backend:** Rust (Labrinth API), Postgres, Clickhouse -- **Indentation:** Use TAB everywhere, never spaces +## Do -### Apps (`apps/`) +- Follow upstream `modrinth/code` for features/fixes; **keep the GitHub fork relationship**. +- Brand from **`brand/`** — `brand/DESIGN.md`, prefer `brand/v2/` (Sora/Onest/Unbounded, cyan `#00e5ff`, bg `#050508`). Owyx™ marks: `TRADEMARK.md`. +- Control-plane site: **`owyxsite/`** (Next frontend + Node API + deploy). Not Modrinth `apps/frontend` / `labrinth`. +- API: `https://api.owyx.site` + header `X-Owyx-Client-Key` (placeholder / settings only — **never commit real keys**). Contract: `owyxsite/LAUNCHER_SITE_CONTRACT.md`. +- User-facing accounts: **Owyx site login** and **Microsoft** (`packages/app-lib` auth); see `docs/ms-oauth.md`. +- Old stack archive: **https://github.com/ebluffy/OwyxOld** only (plugin history lives there — no plugin in this repo). -| App | Description | -| ----------------- | ------------------------------ | -| `frontend` | Main Modrinth website (Nuxt 3) | -| `app-frontend` | Desktop/app frontend (Vue 3) | -| `app` | Desktop/app shell (Tauri) | -| `app-playground` | Testing playground for app | -| `labrinth` | Backend API service | -| `daedalus_client` | Daedalus client implementation | -| `docs` | Documentation site (Astro) | +## Do not -### Packages (`packages/`) +- Ship Modrinth logos, wrench-in-labyrinth marks, green-as-primary, Hosting/Medal / Modrinth+ upsell chrome, or claim to be Modrinth. +- Re-add a `legacy/` folder into this repo. +- Mass-rename `@modrinth/*` packages (upstream merge hygiene) — document as a follow-up. +- Commit `.env`, real client keys, `*.exe`, `frpc.toml` secrets, or other secrets. +- Unfork / delete the upstream remote / rewrite history to hide origin. +- Touch `apps/frontend` / `apps/labrinth` for Owyx product work. +- Reuse **Owyx™** branding in forks (see `TRADEMARK.md` / `COPYING.md`). +- Revive Minecraft **plugin** / game-token / Hosting panel as product surface (410 / redirects only). -| Package | Description | -| ------------------ | ----------------------------------------------------- | -| `ui` | Shared Vue component library (`@modrinth/ui`) | -| `assets` | Styling and auto-generated icons (`@modrinth/assets`) | -| `api-client` | API client for Nuxt, Tauri, and Node/browser | -| `app-lib` | Shared app library | -| `blog` | Blog system and changelog data | -| `utils` | Shared utility functions (mostly deprecated) | -| `moderation` | Moderation utilities | -| `daedalus` | Daedalus protocol | -| `tooling-config` | ESLint, Prettier, TypeScript configs | -| `ariadne` | Analytics library | -| `modrinth-log` | Logging utilities | -| `modrinth-maxmind` | MaxMind GeoIP | -| `modrinth-util` | General utilities | -| `muralpay` | Payment processing | -| `path-util` | Path utilities | -| `sqlx-tracing` | SQLx query tracing | +## Hosting / upstream UI note -## Pre-PR Commands +`packages/ui` still contains Modrinth Hosting layouts for **upstream merge hygiene**. +The Owyx launcher does **not** mount them: `/hosting/manage*` redirects to +`/owyx-servers`, and dead hosting pages under `apps/app-frontend` are removed. -Run these from the **root** folder before opening a pull request - do not run these after each prompt the user gives you, only run when asked, ask the user a question if they want to run it if the user indicates that they are about to create a pull request. +## Pointers -- **Website:** `pnpm prepr:frontend:web` -- **App frontend:** `pnpm prepr:frontend:app` -- **Frontend libs:** `pnpm prepr:frontend:lib` -- **All frontend (app+web):** `pnpm prepr` -- **Labrinth (backend):** See `apps/labrinth/AGENTS.md` +| Topic | Where | +|-------|--------| +| Design tokens | `brand/DESIGN.md` | +| Logos / icons | `brand/v2/` | +| Brand / trademarks | `TRADEMARK.md`, `COPYING.md` | +| MS OAuth | `docs/ms-oauth.md` | +| Site ↔ launcher contract | `owyxsite/LAUNCHER_SITE_CONTRACT.md` | +| Owyx Servers client | `apps/app-frontend/src/helpers/owyx-api.ts` | +| Owyx / MS auth | `packages/app-lib/src/state/minecraft_auth.rs` (+ site JWT) | +| SemVer releases | `.cursor/rules/semver.mdc` — baseline `0.2.0`, bump MAJOR/MINOR/PATCH per https://semver.org/ | +| External agent skills / MCP | [ebluffy/agent-tool-catalog](https://github.com/ebluffy/agent-tool-catalog) · skill `.agents/skills/agent-tool-catalog/` | -The website and app `prepr` commands - -## Dev Commands - -- **Website:** `pnpm web:dev` (copy `.env` template in `apps/frontend/` first) -- **App:** `pnpm app:dev` (copy `.env` template in `packages/app-lib/` first) -- **Storybook (packages/ui):** `pnpm storybook` - -## Project-Specific Instructions - -Each project may have its own file with detailed instructions: - -- [`apps/labrinth/AGENTS.md`](apps/labrinth/AGENTS.md) — Backend API -- [`apps/frontend/AGENTS.md`](apps/frontend/AGENTS.md) - Frontend Website - -## Code Guidelines - -### Comments - -- DO NOT use "heading" comments like: `=== Helper methods ===`. -- Use doc comments, but avoid inline comments unless ABSOLUTELY necessary for clarity. Code should aim to be self documenting! - -## Bash Guidelines - -### Output handling - -- DO NOT pipe output through `head`, `tail`, `less`, or `more` -- NEVER use `| head -n X` or `| tail -n X` to truncate output -- IMPORTANT: Run commands directly without pipes when possible -- IMPORTANT: If you need to limit output, use command-specific flags (e.g. `git log -n 10` instead of `git log | head -10`) -- ALWAYS read the full output — never pipe through filters - -### General - -- Do not create new non-source code files (e.g. Bash scripts, SQL scripts) unless explicitly prompted to -- For Frontend, when doing lint checks, only use the `prepr` commands, do not use `typecheck` or `tsc` etc. -- Types in `@modrinth/utils` are considered highly outdated, if a component needs them, check if you can switch said component to use types from `packages/api-client` -- When provided problems, do not say "I didn't introduce these problems" (shifting the blame/effort) - just fix them. - -## Standards - -Standards available at the @standards/ folder. +Cloud task paste (local only): `promt.md` (gitignored). diff --git a/COPYING.md b/COPYING.md index 1095384f854..0c15c73cb05 100644 --- a/COPYING.md +++ b/COPYING.md @@ -1,8 +1,12 @@ # Copying Guidelines -All packages in this repository are licensed under their respective licenses. For more information, refer to the LICENSE file in each package. +This repository is a **fork of [modrinth/code](https://github.com/modrinth/code)** rebranded and extended as **Owyx**. Package source licenses are **unchanged** from upstream — see each package’s `LICENSE` / `LICENSE.txt` (the desktop app and related packages are **GNU GPL v3**). A copy of GPL-3 is also at the repository root [`LICENSE`](./LICENSE). -For detailed information, consult each package's COPYING.md, LICENSE.txt, or LICENSE file, if available. +For detailed information, consult each package's `COPYING.md`, `LICENSE.txt`, or `LICENSE` file, if available. + +You may fork, modify, and redistribute the **source code** under the applicable package licenses (typically GPL-3 for the launcher). That license covers the software — **not** the product brands below. + +--- ## Modrinth Branding @@ -10,12 +14,49 @@ The use of Modrinth branding elements, including but not limited to the wrench-i > All rights reserved. © 2020-2025 Rinth, Inc. -This includes, but may not be limited to, the following files: +Upstream listed (among others) these trademark assets; **they are removed from this fork** and must not be restored: + +- `.idea/icon.svg` +- `.github/assets/api_cover.png` +- `.github/assets/app_cover.png` +- `.github/assets/monorepo_cover.png` +- `.github/assets/web_cover.png` + +If you fork this repository further, you must continue to omit Modrinth branding assets. + +See also [`TRADEMARK.md`](./TRADEMARK.md) for Owyx™ brand rules. + +--- + +## Owyx™ Branding + +**Owyx™** product branding is **not** licensed under GPL-3 for reuse in forks, derivatives, or redistributed builds. Source code may be used under GPL-3; the Owyx™ name, marks, and assets may not. + +### Where branding lives + +| Location | Contents | +|----------|----------| +| [`brand/`](./brand/) | Canonical design system, logos, icons, wordmarks, hero art (`DESIGN.md`, `v1/`, `v2/`) | +| [`apps/app/icons/`](./apps/app/icons/) | Launcher install icons (Windows/macOS/Linux) | +| [`apps/app/dmg/`](./apps/app/dmg/) | macOS DMG artwork | +| [`owyxsite/frontend/public/`](./owyxsite/frontend/public/) | Site favicons and public brand files | +| Launcher / site strings & themes | Product name **Owyx™**, cyan accent `#00e5ff`, dark cosmic palette, Sora wordmark usage | + +### What you must not do without written permission from Owyx Team + +- Use the name **Owyx™**, **Owyx Team™**, **owyx.site**, **api.owyx.site**, or confusingly similar names as the product name of your fork, binary, installer, website, or Discord/community. +- Ship or redistribute files from [`brand/`](./brand/) (or copies of those marks) in a product that is not an official Owyx™ build. +- Use Owyx™ logos, crystal icons, wordmarks, Discord RPC art, installer icons, or site favicons as your own brand. +- Imply affiliation with, endorsement by, or identity as the official Owyx™ project or Owyx Team™. + +### What forks should do + +1. Replace product name, window titles, installer metadata, update endpoints, and API hosts with your own. +2. Replace icons under `apps/app/icons/` and any site favicons with your own artwork. +3. Do **not** copy or redistribute [`brand/`](./brand/) — leave it out of your public product, or delete it from your fork’s release artifacts. +4. Keep omitting Modrinth branding as required above. +5. Keep GPL-3 notices and upstream attribution for code you redistribute. -- .idea/icon.svg -- .github/api_cover.png -- .github/app_cover.png -- .github/monorepo_cover.png -- .github/web_cover.png +> Owyx™ branding: all rights reserved. © Owyx Team. Code: see package licenses / root [`LICENSE`](./LICENSE). -If you fork this repository, you must remove all Modrinth branding assets from your fork. +Official design tokens and asset inventory: [`brand/DESIGN.md`](./brand/DESIGN.md), [`brand/README.md`](./brand/README.md). \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 5e055de1c86..dc66d51b6c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11535,6 +11535,7 @@ dependencies = [ "indicatif", "itertools 0.14.0", "json5", + "md5", "modrinth-content-management", "notify", "notify-debouncer-mini", @@ -11588,7 +11589,7 @@ dependencies = [ [[package]] name = "theseus_gui" -version = "1.0.0-local" +version = "0.5.2" dependencies = [ "async_zip", "chrono", @@ -11596,6 +11597,7 @@ dependencies = [ "core-graphics 0.24.0", "daedalus", "dashmap", + "dirs", "either", "enumset", "hyper 1.7.0", diff --git a/Cargo.toml b/Cargo.toml index a1a4a5b2a50..d633922c6e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ members = [ [workspace.package] edition = "2024" rust-version = "1.90.0" -repository = "https://github.com/modrinth/code" +repository = "https://github.com/ebluffy/Owyx" [workspace.dependencies] actix-cors = "0.7.1" diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..f288702d2fa --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index 0def9bf509f..ad88fab4ac9 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,81 @@ -# ![Modrinth Monorepo Cover](/.github/assets/monorepo_cover.png) +# Owyx -![Issues](https://img.shields.io/github/issues-raw/Modrinth/code?color=c78aff&label=issues&style=for-the-badge) -![Pull Requests](https://img.shields.io/github/issues-pr-raw/Modrinth/code?color=c78aff&label=PRs&style=for-the-badge) -![Contributors](https://img.shields.io/github/contributors/Modrinth/code?color=c78aff&label=contributors&style=for-the-badge) -![Lines of Code](https://img.shields.io/endpoint?url=https://loctopus.creeperkatze.dev/github/modrinth/code/badge?style=flat&logoColor=white&color=c78aff&style=for-the-badge) -![Commit Activity](https://img.shields.io/github/commit-activity/m/Modrinth/code?color=c78aff&label=commits&style=for-the-badge) -![Last Commit](https://img.shields.io/github/last-commit/Modrinth/code?color=c78aff&label=last%20commit&style=for-the-badge) +**Owyx** is a desktop Minecraft launcher (and small control-plane site) for private / friends servers: dark UI, cyan brand, curated **Owyx Servers**, offline nickname login, and Microsoft login for licensed play. -## Modrinth Monorepo +## Fork notice (please read) -Welcome to the Modrinth Monorepo, the primary codebase for the Modrinth web interface and app. It contains ![Lines of code](https://img.shields.io/endpoint?url=https://loctopus.creeperkatze.dev/github/modrinth/code/badge%3Fformat%3Dhuman&logoColor=white&color=black&label=) lines of code and has ![Contributors](https://img.shields.io/github/contributors/Modrinth/code?color=black&label=) contributors! +This repository is a **public GitHub fork of [modrinth/code](https://github.com/modrinth/code)** (Modrinth / Rinth, Inc.). -If you're not a developer and you've stumbled upon this repository, you can access the web interface on the [Modrinth website](https://modrinth.com) and download the latest release of the app [here](https://modrinth.com/app). +We keep the **fork relationship** so we can pull upstream fixes. We **rebrand and extend** the tree for the Owyx product — we are **not** Modrinth, not affiliated with Rinth, Inc., and we do **not** use Modrinth trademarks, logos, or product name in the shipping Owyx UI. -## Development +| | | +|--|--| +| Upstream | https://github.com/modrinth/code | +| This fork | https://github.com/ebluffy/Owyx | +| Website | https://owyx.site | +| Control plane | https://api.owyx.site | +| Brand kit | [`brand/`](./brand/) · [`brand/DESIGN.md`](./brand/DESIGN.md) · `brand/v2/` | +| Tokens | bg `#050508` · accent `#00e5ff` · font **Sora** | -This repository contains two primary packages. For detailed development information, please refer to their respective guides: +Older private Owyx experiments (previous launcher / plugin archive): **https://github.com/ebluffy/OwyxOld** — do **not** re-add a `legacy/` tree here. -- [Website frontend](https://docs.modrinth.com/contributing/knossos/) -- [Desktop app](https://docs.modrinth.com/contributing/theseus/) +## Layout -## Contributing +| Path | What | +|------|------| +| `apps/app` | Tauri shell → **Owyx** binary (`owyx://` deep links) | +| `apps/app-frontend` | Launcher UI (Vue) | +| `packages/app-lib` | Launcher core (Rust) | +| `brand/` | Owyx logos, hero art, design tokens | +| `owyxsite/` | Owyx control-plane (site + API + deploy) — our code, not upstream | +| `docs/ms-oauth.md` | Microsoft OAuth notes for owners | +| `apps/frontend`, `apps/labrinth`, … | Upstream packages kept for sync | -We welcome contributions! Before submitting any contributions, please read our [contributing guidelines](https://docs.modrinth.com/contributing/getting-started/). +## Features (launcher) -If you plan to fork this repository for your own purposes, please review our [copying guidelines](COPYING.md). +- **Owyx chrome** — logos, window title, Discord RPC, cyan theme +- **Owyx Servers** — catalog from `api.owyx.site` (`X-Owyx-Client-Key`; settings + optional demo seed) +- **Offline nickname** — deterministic offline UUID for offline-mode servers +- **Microsoft login** — see `docs/ms-oauth.md` -## Security +`api.modrinth.com` may still be used as a **content catalog API host**. That is API infrastructure, not Modrinth product branding. -If you discover a security vulnerability within our codebase, please follow our [responsible disclosure guidelines](https://modrinth.com/legal/security). +## Dev -## Support +```bash +pnpm install +pnpm app:dev +``` -If you need help with the Modrinth web interface or app, please visit our [support page](https://support.modrinth.com). For general inquiries, you can also join our [Discord server](https://discord.modrinth.com). +Optional env (never commit secrets): + +```bash +export OWYX_API_BASE_URL=http://127.0.0.1:3001 +# VITE_OWYX_CLIENT_KEY=… # frontend build-time placeholder only +``` + +Site / API (see `owyxsite/README.md` and `.env.example`): + +```bash +cd owyxsite && docker compose up +``` + +## Upstream sync + +```bash +git remote add upstream https://github.com/modrinth/code.git # if missing +git fetch upstream +git merge upstream/main # resolve brand / Owyx feature conflicts carefully +``` + +Do not mass-rename `@modrinth/*` packages in drive-by PRs (keeps merges sane). Do not unfork. ## License -All packages in this repository are licensed under their respective licenses. Refer to the LICENSE file in each package for more information. +**Same licenses as upstream [modrinth/code](https://github.com/modrinth/code).** + +- Per-package terms apply (see each package’s `LICENSE` / `COPYING.md`). The Modrinth App / Theseus-derived launcher packages are **GNU General Public License v3**. +- A GPL-3 copy is at the repo root: [`LICENSE`](./LICENSE). +- Trademark / branding rules from upstream: [`COPYING.md`](./COPYING.md) — Modrinth marks stay with Rinth, Inc.; this fork ships **Owyx** branding only. + +We redistribute and modify under those terms in good faith: attribution via the fork link above, no Modrinth trademark use, source available in this public repository. diff --git a/TRADEMARK.md b/TRADEMARK.md new file mode 100644 index 00000000000..65308825daf --- /dev/null +++ b/TRADEMARK.md @@ -0,0 +1,28 @@ +# Owyx™ branding & trademarks + +The **source code** in this repository is licensed primarily under **GNU GPL v3** (see [`LICENSE`](./LICENSE) and per-package licenses). That license covers the software — **not** the product brand. + +## What is reserved + +The name **Owyx™**, **Owyx Team™**, **owyx.site**, related logos, the crystal mark, wordmarks, installer icons, Discord RPC art, and design-system assets under [`brand/`](./brand/) are **commercial designations and unregistered trademarks** of **Owyx Team**. They are exclusive intellectual property of Owyx Team. + +**Using the name Owyx™ (or confusingly similar marks) for other forks, redistributed builds, websites, or communities is prohibited** without prior written permission from Owyx Team. + +## What forks may do + +- Fork, modify, and redistribute **code** under the applicable GPL / package licenses. +- Replace product name, icons, update endpoints, API hosts, and AppData / bundle identifiers with their own branding. +- Omit or delete [`brand/`](./brand/) from public release artifacts. + +## What forks must not do + +- Ship binaries, installers, or sites under the name **Owyx™** / **Owyx Team™**. +- Reuse logos, crystal icons, wordmarks, or `brand/` assets as their own identity. +- Imply affiliation with or endorsement by the official Owyx™ project. + +Further inventory and rules: [`COPYING.md`](./COPYING.md), [`brand/README.md`](./brand/README.md), [`brand/DESIGN.md`](./brand/DESIGN.md). + +> Owyx™ branding: all rights reserved. © Owyx Team. +> Code: see package licenses / root [`LICENSE`](./LICENSE). + +The **™** symbol asserts a trademark claim and does **not** require registration (unlike **®**). Registration may be pursued separately. diff --git a/_typos.toml b/_typos.toml index 33c5b1a27d0..066bc384722 100644 --- a/_typos.toml +++ b/_typos.toml @@ -30,6 +30,8 @@ gam = "gam" consts = "consts" # short for "Copy" Cpy = "Cpy" +# Intentional local filename for cloud task paste (gitignored); not "prompt.md" +promt = "promt" [default.extend-identifiers] # Constant from the `zip` crate diff --git a/apps/app-frontend/index.html b/apps/app-frontend/index.html index 50867a419b9..b6bad69de4c 100644 --- a/apps/app-frontend/index.html +++ b/apps/app-frontend/index.html @@ -2,10 +2,16 @@ - + + - Modrinth App - + Owyx + + + diff --git a/apps/app-frontend/package.json b/apps/app-frontend/package.json index 4f67741f59f..ac16cdd1628 100644 --- a/apps/app-frontend/package.json +++ b/apps/app-frontend/package.json @@ -1,7 +1,7 @@ { "name": "@modrinth/app-frontend", "private": true, - "version": "1.0.0-local", + "version": "0.5.2", "type": "module", "scripts": { "dev": "vite", @@ -69,6 +69,5 @@ "vue-component-type-helpers": "^3.1.8", "vue-tsc": "^2.1.6" }, - "packageManager": "pnpm@9.4.0", "web-types": "../../web-types.json" } diff --git a/apps/app-frontend/public/favicon.png b/apps/app-frontend/public/favicon.png new file mode 100644 index 00000000000..ce94f7faa3e Binary files /dev/null and b/apps/app-frontend/public/favicon.png differ diff --git a/apps/app-frontend/public/favicon.svg b/apps/app-frontend/public/favicon.svg new file mode 100644 index 00000000000..a7d44067749 --- /dev/null +++ b/apps/app-frontend/public/favicon.svg @@ -0,0 +1,20 @@ + + Owyx icon — cyan favicon (16-24px) + + + + + + + + + + + + diff --git a/apps/app-frontend/public/icon-192.png b/apps/app-frontend/public/icon-192.png new file mode 100644 index 00000000000..3bc82275801 Binary files /dev/null and b/apps/app-frontend/public/icon-192.png differ diff --git a/apps/app-frontend/public/icon-512.png b/apps/app-frontend/public/icon-512.png new file mode 100644 index 00000000000..3c9be32759d Binary files /dev/null and b/apps/app-frontend/public/icon-512.png differ diff --git a/apps/app-frontend/public/owyx-hero-bg.jpg b/apps/app-frontend/public/owyx-hero-bg.jpg new file mode 100644 index 00000000000..8c56938bc4a Binary files /dev/null and b/apps/app-frontend/public/owyx-hero-bg.jpg differ diff --git a/apps/app-frontend/public/owyx-icon.svg b/apps/app-frontend/public/owyx-icon.svg new file mode 100644 index 00000000000..becc8e2072a --- /dev/null +++ b/apps/app-frontend/public/owyx-icon.svg @@ -0,0 +1,43 @@ + + Owyx icon — cyan gradient + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/app-frontend/public/owyx-logo.svg b/apps/app-frontend/public/owyx-logo.svg new file mode 100644 index 00000000000..50fc1358a0d --- /dev/null +++ b/apps/app-frontend/public/owyx-logo.svg @@ -0,0 +1,62 @@ + + Owyx — Brand cyan — on dark backgrounds · MD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + owyx + + diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index 5faea7c84a0..6f8fe6bb658 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -9,15 +9,12 @@ import { VerboseLoggingFeature, } from '@modrinth/api-client' import { - ArrowBigUpDashIcon, - ArrowLeftRightIcon, ChevronLeftIcon, ChevronRightIcon, CompassIcon, ImageIcon, LogInIcon, LogOutIcon, - NewspaperIcon, PlayIcon, PlusIcon, RefreshCwIcon, @@ -26,18 +23,12 @@ import { SettingsIcon, ShirtIcon, SpinnerIcon, - ToggleRightIcon, UserIcon, - UserPlusIcon, - XIcon, } from '@modrinth/assets' import { - AccountSwitchOverlay, Admonition, Avatar, - ButtonLink, commonMessages, - commonSettingsMessages, ContentInstallModal, ContentUpdaterModal, CreationFlowModal, @@ -45,7 +36,6 @@ import { I18nDebugPanel, IconButton, LoadingBar, - NewsArticleCard, NotificationPanel, PopupNotificationPanel, provideModalBehavior, @@ -58,8 +48,6 @@ import { TooltipDirective, useDebugLogger, useFormatBytes, - useHostingIntercom, - UserRoleIcon, useVIntl, } from '@modrinth/ui' import { renderString } from '@modrinth/utils/parse' @@ -80,7 +68,6 @@ import AppActionBar from '@/components/ui/AppActionBar.vue' import Breadcrumbs from '@/components/ui/Breadcrumbs.vue' import ErrorModal from '@/components/ui/ErrorModal.vue' import FriendsList from '@/components/ui/friends/FriendsList.vue' -import HostingUpdateRequired from '@/components/ui/HostingUpdateRequired.vue' import AddServerToInstanceModal from '@/components/ui/install_flow/AddServerToInstanceModal.vue' import UnknownPackWarningModal from '@/components/ui/install_flow/UnknownPackWarningModal.vue' import IconEditorModal from '@/components/ui/instance_settings/icon-editor-modal/index.vue' @@ -93,8 +80,6 @@ import ModrinthAccountRequiredModal from '@/components/ui/modal/ModrinthAccountR import UpdateToPlayModal from '@/components/ui/modal/UpdateToPlayModal.vue' import NavButton from '@/components/ui/NavButton.vue' import OnboardingChecklist from '@/components/ui/onboarding-checklist/index.vue' -import PrideFundraiserBanner from '@/components/ui/PrideFundraiserBanner.vue' -import PromotionWrapper from '@/components/ui/PromotionWrapper.vue' import QuickInstanceSwitcher from '@/components/ui/QuickInstanceSwitcher.vue' import SharedInstanceInviteHandler from '@/components/ui/shared-instances/shared-instance-invite-handler/index.vue' import SplashScreen from '@/components/ui/SplashScreen.vue' @@ -113,7 +98,7 @@ import { useInstanceMetadataRefresh } from '@/composables/use-instance-metadata- import { useQuickInstanceLimit } from '@/composables/use-quick-instance-limit.ts' import { isDarkTheme, useTheme } from '@/composables/use-theme.ts' import { config } from '@/config' -import { getAccountAppearance, rememberAccountAppearance } from '@/helpers/account-appearance.ts' +import { rememberAccountAppearance } from '@/helpers/account-appearance.ts' import { hide_ads_window, init_ads_window, @@ -123,8 +108,8 @@ import { take_ads_window_hold, } from '@/helpers/ads.js' import { debugAnalytics, initAnalytics, trackEvent } from '@/helpers/analytics' -import { check_reachable } from '@/helpers/auth.js' -import { get_user, get_user_many, get_version } from '@/helpers/cache.js' +import { check_reachable, login_offline, remove_user } from '@/helpers/auth.js' +import { get_user, get_version } from '@/helpers/cache.js' import { gameSettingsQueryOptions } from '@/helpers/game-options' import { install_create_modpack_instance, install_get_modpack_preview } from '@/helpers/install' import { @@ -134,14 +119,22 @@ import { set_global_synced_option, } from '@/helpers/instance' import { maxMemoryQueryOptions } from '@/helpers/jre.js' +import { get as getCreds, login, removeUser } from '@/helpers/mr_auth.ts' +import { resolveOwyxAvatarUrl } from '@/helpers/owyx-avatar' import { - get as getCreds, - getAll as getAllCreds, - login, - logout, - removeUser, - setActive, -} from '@/helpers/mr_auth.ts' + setOwyxPresenceOnline, + setOwyxPresencePlaying, + startOwyxPresenceHeartbeat, + stopOwyxPresenceHeartbeat, +} from '@/helpers/owyx-presence' +import { + fetchOwyxSiteMe, + getStoredOwyxSiteSession, + hydrateOwyxSiteSession, + logoutOwyxSite, + OWYX_SITE_CHANGELOG_URL, + OWYX_SITE_PROFILE_URL, +} from '@/helpers/owyx-site-auth' import { mergeUrlQuery, parseModrinthLink } from '@/helpers/project-links.ts' import { appSettingsKeys, @@ -160,7 +153,6 @@ import { } from '@/helpers/synced-options' import { syncedPackQueryOptions } from '@/helpers/synced-packs' import { hasActivePride26Midas, hasMidasBadge } from '@/helpers/user-campaigns.ts' -import { get_user_preferences } from '@/helpers/user-preferences.ts' import { parse_modrinth_user_link } from '@/helpers/users' import { areUpdatesEnabled, @@ -194,6 +186,7 @@ import { provideAppUpdateDownloadProgress, subscribeToDownloadProgress, } from '@/providers/download-progress.ts' +import { setupOwyxSiteSessionProvider } from '@/providers/owyx-site-session' import { createServerInstall, provideServerInstall } from '@/providers/server-install' import { setupProviders } from '@/providers/setup' import { setupAppEventsProvider } from '@/providers/setup/app-events' @@ -252,9 +245,11 @@ updateHistoryNavigationState() const APP_LEFT_NAV_WIDTH = '4rem' const APP_SIDEBAR_WIDTH = 300 const INTERCOM_BUBBLE_DEFAULT_PADDING = 20 -const PRIDE_FUNDRAISER_END_DATE = new Date('2026-07-01T00:00:00Z').getTime() const credentials = ref() -const storedModrinthAccounts = ref([]) +/** Bottom-left = Owyx site account (email), not Modrinth. */ +const owyxSiteSession = ref( + /** @type {import('@/helpers/owyx-site-auth').OwyxSiteSession | null | undefined} */ (undefined), +) let credentialsRefreshId = 0 const sidebarToggled = ref(true) watch( @@ -270,35 +265,12 @@ const forceSidebar = computed( route.path.startsWith('/user'), ) const sidebarVisible = computed(() => sidebarToggled.value || forceSidebar.value) -const hostingRouteActive = computed(() => route.path.startsWith('/hosting')) -const hostingUpdateRequired = computed( - () => - hostingRouteActive.value && - !!appUpdateState.availableUpdate.value && - appUpdateState.updatesEnabled.value, -) -const prideFundraiserEnabled = computed( - () => appSettings.getFeatureFlag('pride_fundraiser') && Date.now() < PRIDE_FUNDRAISER_END_DATE, -) -const hostingIntercomIdentityKey = computed(() => { - const rawServerId = route.params.id - const serverId = Array.isArray(rawServerId) ? rawServerId[0] : rawServerId - const userId = credentials.value?.user_id ?? credentials.value?.user?.id ?? 'anonymous' - return `${userId}:${serverId ?? 'hosting'}` -}) -const hostingIntercom = useHostingIntercom({ - enabled: computed( - () => hostingRouteActive.value && !hostingUpdateRequired.value && !!credentials.value?.session, - ), - appId: 'ykeritl9', - fetchToken: fetchIntercomToken, - identityKey: hostingIntercomIdentityKey, - horizontalPadding: computed(() => - sidebarVisible.value - ? APP_SIDEBAR_WIDTH + INTERCOM_BUBBLE_DEFAULT_PADDING - : INTERCOM_BUBBLE_DEFAULT_PADDING, - ), -}) +const intercomBubble = { + width: ref(0), + horizontalPadding: ref(INTERCOM_BUBBLE_DEFAULT_PADDING), + requestHorizontalPadding: () => {}, + requestVerticalClearance: () => {}, +} const notificationManager = new AppNotificationManager() provideNotificationManager(notificationManager) @@ -321,9 +293,42 @@ const { addPopupNotification } = popupNotificationManager let adsConsentPopupId = null useAppEvent('ads_consent_required', handleAdsConsentRequired, appEvents) +useAppEvent( + 'process', + async (event) => { + if (!owyxSiteSession.value?.token) return + const kind = typeof event.event === 'string' ? event.event : event.event?.tag + if (kind === 'launched') { + let name = 'Minecraft' + try { + const { get } = await import('@/helpers/instance') + const inst = event.instance_id ? await get(event.instance_id) : null + if (inst?.name) name = inst.name + } catch { + /* ignore */ + } + setOwyxPresencePlaying(name) + if (event.instance_id) { + try { + const { writeOwyxCslConfigForInstance, mirrorLocalOwyxSkinToInstance } = + await import('@/helpers/owyx-csl') + await writeOwyxCslConfigForInstance(event.instance_id) + const nick = owyxSiteSession.value?.user?.nickname + if (nick) await mirrorLocalOwyxSkinToInstance(event.instance_id, nick) + } catch { + /* best-effort skin wiring */ + } + } + } else if (kind === 'finished') { + setOwyxPresenceOnline() + } + }, + appEvents, +) + const appVersion = getVersion() const tauriApiClient = new TauriModrinthClient({ - userAgent: async () => `modrinth/theseus/${await appVersion} (support@modrinth.com)`, + userAgent: async () => `owyx/theseus/${await appVersion} (support@owyx.site)`, labrinthBaseUrl: config.labrinthBaseUrl, archonBaseUrl: config.archonBaseUrl, sharedInstancesBaseUrl: config.sharedInstancesBaseUrl, @@ -366,10 +371,8 @@ const hasPlus = computed( (hasMidasBadge(credentials.value.user) || hasActivePride26Midas(authenticatedModrinthUser.value?.campaigns?.pride_26)), ) -const showAd = computed( - () => sidebarVisible.value && !hasPlus.value && credentials.value !== undefined, -) -const adConsentAvailable = computed(() => credentials.value !== undefined && !hasPlus.value) +const showAd = computed(() => false) +const adConsentAvailable = computed(() => false) providePageContext({ hierarchicalSidebarAvailable: ref(true), showAds: showAd, @@ -378,7 +381,7 @@ providePageContext({ left: ref(APP_LEFT_NAV_WIDTH), right: computed(() => (sidebarVisible.value ? `${APP_SIDEBAR_WIDTH}px` : '0px')), }, - intercomBubble: hostingIntercom.intercomBubble, + intercomBubble, featureFlags: { serverRamAsBytesAlwaysOn: computed(() => appSettings.getFeatureFlag('server_ram_as_bytes_always_on'), @@ -417,8 +420,14 @@ const { (iconPath) => creationGeneratedIcon.value?.path === iconPath ? creationGeneratedIcon.value.config : null, ) -const { hasLoggedIntoMinecraft, hasLoggedIntoModrinth, showChecklist } = onboardingChecklist -const showFriendsList = computed(() => !showChecklist.value || hasLoggedIntoModrinth.value) +const { hasLoggedIntoMinecraft, showChecklist } = onboardingChecklist +const showFriendsList = computed( + () => !showChecklist.value || !!owyxSiteSession.value?.token || hasLoggedIntoMinecraft.value, +) +const isOwyxSiteAdmin = computed(() => { + const role = owyxSiteSession.value?.user?.role + return role === 'admin' || role === 'moderator' +}) async function randomizeCreationIcon() { const generated = await creationIconEditorModal.value?.randomizeAndSave() @@ -456,7 +465,6 @@ function onCreationIconSaved(iconPath, config) { context.instanceIconPath.value = iconPath } -const news = ref([]) const displayedServerInviteNotifications = new Set() const serverInvitePopupNotificationIds = new Set() let liveNotificationGeneration = 0 @@ -623,12 +631,12 @@ const messages = defineMessages({ }, adsConsentTitle: { id: 'app.ads-consent.title', - defaultMessage: 'Your privacy and how ads support Modrinth', + defaultMessage: 'Your privacy and ads', }, adsConsentBody: { id: 'app.ads-consent.body', defaultMessage: - 'Ads make Modrinth possible and fund creator payouts. Our partners may store or access cookies in the app to personalize ads and measure performance.', + 'Ads were part of upstream Modrinth; Owyx does not rely on them. Partners may store or access cookies to personalize ads and measure performance.', }, adsConsentManage: { id: 'app.ads-consent.manage', @@ -646,9 +654,9 @@ const messages = defineMessages({ id: 'app.nav.home', defaultMessage: 'Home', }, - modrinthHosting: { - id: 'app.nav.modrinth-hosting', - defaultMessage: 'Modrinth Hosting', + owyxServers: { + id: 'app.nav.owyx-servers', + defaultMessage: 'Owyx Servers', }, screenshots: { id: 'app.nav.screenshots', @@ -659,8 +667,8 @@ const messages = defineMessages({ defaultMessage: 'Create new instance', }, modrinthAccount: { - id: 'app.nav.modrinth-account', - defaultMessage: 'Modrinth account', + id: 'app.nav.owyx-account', + defaultMessage: 'Owyx account', }, viewProfile: { id: 'app.nav.view-profile', @@ -671,8 +679,8 @@ const messages = defineMessages({ defaultMessage: 'Add a friend', }, signInToModrinthAccount: { - id: 'app.nav.sign-in-to-modrinth-account', - defaultMessage: 'Sign into Modrinth', + id: 'app.nav.sign-in-to-owyx-account', + defaultMessage: 'Sign into Owyx', }, loadingProfile: { id: 'app.nav.loading-profile', @@ -690,14 +698,18 @@ const messages = defineMessages({ id: 'app.nav.remove-account', defaultMessage: 'Remove account', }, + signOutOwyx: { + id: 'app.nav.sign-out-owyx', + defaultMessage: 'Sign out of Owyx', + }, + adminPanel: { + id: 'app.nav.admin-panel', + defaultMessage: 'Admin panel', + }, restarting: { id: 'app.restarting', defaultMessage: 'Restarting...', }, - upgradeToModrinthPlus: { - id: 'app.nav.upgrade-to-modrinth-plus', - defaultMessage: 'Upgrade to Modrinth+', - }, news: { id: 'app.news.title', defaultMessage: 'News', @@ -851,6 +863,19 @@ async function setupApp() { initAnalytics() if (dev) debugAnalytics() trackEvent('Launched', { version, dev }) + void Promise.all([ + import('@/helpers/owyx-telemetry'), + import('@/helpers/owyx-site-auth'), + ]) + .then(([{ reportOwyxLauncherSession }, { getStoredOwyxSiteSession }]) => + reportOwyxLauncherSession({ + dev, + authToken: getStoredOwyxSiteSession()?.token ?? null, + }), + ) + .catch(() => { + /* telemetry must never block startup */ + }) } const osType = await traceStartupStep('Read operating system type', async () => type()) @@ -873,24 +898,14 @@ async function setupApp() { ) }) - fetch(`https://modrinth.com/news/feed/articles.json`) - .then((response) => response.json()) - .then((res) => { - if (res && res.articles) { - news.value = res.articles - .map((article) => ({ - ...article, - path: article.link, - })) - .slice(0, 4) - } - }) - .catch((error) => { - console.error('Failed to fetch news articles', error) - }) + // Owyx: Modrinth news feed removed from sidebar traceStartupStep('Read opening command', get_opening_command).then(handleCommand) traceStartupStep('Refresh startup credentials', fetchCredentials) + traceStartupStep('Hydrate Owyx site session', async () => { + await hydrateOwyxSiteSession() + await refreshOwyxSiteSession() + }) if (pending_update_toast_for_version !== null) { const settings = await traceStartupStep( @@ -1010,38 +1025,7 @@ watch(stateInitialized, (ready) => { routerToken = null } - queryClient.prefetchQuery({ - queryKey: ['servers'], - queryFn: async () => { - const response = await tauriApiClient.archon.servers_v0.list({ limit: 100 }) - const hasMedalServers = response.servers.some((s) => s.is_medal) - if (hasMedalServers) { - const subscriptions = await tauriApiClient.labrinth.billing_internal.getSubscriptions() - for (const server of response.servers) { - if (server.is_medal) { - const sub = subscriptions.find((s) => s.metadata?.id === server.server_id) - if (sub) { - server.medal_expires = new Date( - new Date(sub.created).getTime() + 5 * 86400000, - ).toISOString() - } - } - } - } - return response - }, - staleTime: 30_000, - }) - queryClient.prefetchQuery({ - queryKey: ['billing', 'subscriptions'], - queryFn: () => tauriApiClient.labrinth.billing_internal.getSubscriptions(), - staleTime: 30_000, - }) - queryClient.prefetchQuery({ - queryKey: ['billing', 'payments'], - queryFn: () => tauriApiClient.labrinth.billing_internal.getPayments(), - staleTime: 30_000, - }) + // Hosting/Medal/billing prefetch removed — Owyx does not use Modrinth Hosting. } }) @@ -1364,7 +1348,6 @@ async function fetchCredentials() { credentials.value = null liveNotificationsEnabled = false - await fetchStoredModrinthAccounts() return } creds.user = await traceStartupStep('Fetch signed-in user', () => @@ -1374,7 +1357,6 @@ async function fetchCredentials() { } credentials.value = creds ?? null liveNotificationsEnabled = !!creds?.session - await traceStartupStep('Load stored account profiles', fetchStoredModrinthAccounts) } async function signIn(flow = 'sign-in', addAccount = false) { @@ -1395,208 +1377,113 @@ async function signIn(flow = 'sign-in', addAccount = false) { } async function requestSignIn(flow = 'sign-in', addAccount = false) { - await modrinthLoginModal.value?.showSigningIn(flow, addAccount) + const signedIn = await modrinthLoginModal.value?.showSigningIn(flow, addAccount) + if (signedIn) await refreshOwyxSiteSession() } -async function requestModrinthAuth(flow = 'sign-in', addAccount = false) { - await signIn(flow, addAccount) - return !!credentials.value?.session -} - -async function logOut() { - if (!credentials.value?.user) return - await completeAccountSwitch(() => logout()) +async function refreshOwyxSiteSession() { + await hydrateOwyxSiteSession() + const cached = getStoredOwyxSiteSession() + if (!cached) { + owyxSiteSession.value = null + stopOwyxPresenceHeartbeat() + return + } + owyxSiteSession.value = cached + const fresh = await fetchOwyxSiteMe(cached.token) + owyxSiteSession.value = fresh + if (fresh?.token) { + startOwyxPresenceHeartbeat() + try { + const { markLoggedIntoOwyxSite } = await import('@/helpers/onboarding-checklist') + await markLoggedIntoOwyxSite() + } catch { + /* checklist mark is best-effort */ + } + // Sync site display nickname → offline play profile (rename replaces stale offline UUID) + const nick = (fresh.user?.displayNickname || fresh.user?.nickname)?.trim() ?? '' + const nickOk = /^[A-Za-z0-9_]{3,16}$/.test(nick) + if (nickOk) { + try { + const { users } = await import('@/helpers/auth') + const list = await users() + const accounts = Array.isArray(list) ? list : [] + const isOwyxOffline = (a) => + a?.is_offline === true || + a?.refresh_token === 'owyx-offline' || + ((a?.refresh_token ?? '') === '' && (a?.access_token === '' || a?.access_token === '0')) + const offlineAccounts = accounts.filter(isOwyxOffline) + const match = offlineAccounts.find((a) => a?.profile?.name === nick) + if (!match) { + for (const stale of offlineAccounts) { + const id = stale?.profile?.id + if (id) { + try { + await remove_user(id) + } catch (e) { + console.warn('Could not remove stale offline nick', e) + } + } + } + await login_offline(nick, false) + } + } catch (e) { + console.warn('Could not sync Owyx display nickname to play profile', e) + } + } + } } -async function fetchStoredModrinthAccounts() { - const all = (await getAllCreds().catch(handleError)) ?? [] - const ids = all.map((account) => account.user_id) - const users = ids.length ? ((await get_user_many(ids).catch(handleError)) ?? []) : [] - const usersById = new Map(users.map((user) => [user.id, user])) - - storedModrinthAccounts.value = all.map((account) => ({ - ...account, - user: usersById.get(account.user_id) ?? { - id: account.user_id, - username: account.user_id, - avatar_url: null, - role: 'developer', - }, - })) +async function signOutOwyxSiteAccount() { + stopOwyxPresenceHeartbeat() + await logoutOwyxSite() + owyxSiteSession.value = null } -const accountSwitcherAccounts = computed(() => { - const currentId = credentials.value?.session ? credentials.value.user_id : null - - return storedModrinthAccounts.value.map((account) => ({ - ...account, - optionId: `account-${account.user_id}`, - current: account.user_id === currentId, - })) -}) +setupOwyxSiteSessionProvider( + owyxSiteSession, + refreshOwyxSiteSession, + async () => { + await requestSignIn() + }, + signOutOwyxSiteAccount, +) const profileButtonTooltip = computed(() => { - if (credentials.value === undefined) return formatMessage(messages.loadingProfile) - if (credentials.value?.user) return formatMessage(messages.modrinthAccount) + if (owyxSiteSession.value === undefined) return formatMessage(messages.loadingProfile) + if (owyxSiteSession.value?.user) return formatMessage(messages.modrinthAccount) return formatMessage(messages.signInToModrinthAccount) }) -const accountSwitcherOptions = computed(() => [ - ...accountSwitcherAccounts.value.map((account) => ({ - id: account.optionId, - label: account.user.username, - selected: account.current, - action: () => switchModrinthAccount(account), - trailingAction: { - label: formatMessage(messages.removeAccount), - icon: XIcon, - color: 'red', - action: () => forgetModrinthAccount(account.user_id), - }, - })), - { - type: 'divider', - }, - { - id: 'add-account', - label: formatMessage(messages.addAccount), - icon: PlusIcon, - action: () => requestSignIn('sign-in', true), - }, -]) - -const isSwitchingAccount = ref(false) - -async function persistAppearanceTheme(appearance) { - const selectedTheme = appearance.auto ? 'system' : appearance.theme - appTheme.applyAccountAppearance(appearance) - const settings = await getSettings() - if (settings.theme !== selectedTheme) { - settings.theme = selectedTheme - await setSettings(settings) - } -} - -async function completeAccountSwitch(task) { - isSwitchingAccount.value = true - await nextTick() - try { - await task() - window.location.reload() - } catch (error) { - isSwitchingAccount.value = false - handleError(error) - } -} - -async function switchModrinthAccount(account) { - if (account.current) return - - const cached = getAccountAppearance(account.user_id) - if (cached) await persistAppearanceTheme(cached) - await nextTick() - - await completeAccountSwitch(async () => { - await setActive(account.user_id) - if (cached) return - - try { - const preferences = await get_user_preferences(account.user_id) - rememberAccountAppearance(account.user_id, preferences.appearance) - await persistAppearanceTheme(preferences.appearance) - await nextTick() - } catch { - // no saved appearance for this account - } - }) -} - -async function forgetModrinthAccount(userId) { - const isCurrent = credentials.value?.user_id === userId && !!credentials.value?.session - if (isCurrent) { - await completeAccountSwitch(() => removeUser(userId)) - return - } - - await removeUser(userId).catch(handleError) - await fetchStoredModrinthAccounts() -} - -const modrinthAccountMenuOptions = computed(() => [ +const owyxAccountMenuOptions = computed(() => [ { id: 'view-profile', label: formatMessage(messages.viewProfile), icon: UserIcon, - action: () => router.push(`/user/${encodeURIComponent(credentials.value.user.username)}`), - }, - { - id: 'plus', - label: formatMessage(messages.upgradeToModrinthPlus), - icon: ArrowBigUpDashIcon, type: 'link', - href: 'https://modrinth.plus?app', + href: OWYX_SITE_PROFILE_URL, target: '_blank', - tone: 'purple', - shown: !hasPlus.value, - }, - { - id: 'add-friend', - label: formatMessage(messages.addFriend), - icon: UserPlusIcon, - action: () => friendsList.value?.showAddFriendModal(), - }, - { - id: 'flags', - label: formatMessage(commonSettingsMessages.featureFlags), - icon: ToggleRightIcon, - shown: appSettings.devMode, - action: () => appSettingsModal.value?.showFeatureFlags(), - }, - { - type: 'divider', - }, - { - id: 'switch-account', - label: formatMessage(messages.switchAccount), - icon: ArrowLeftRightIcon, - type: 'submenu', - options: accountSwitcherOptions.value, }, + ...(isOwyxSiteAdmin.value + ? [ + { + id: 'admin-panel', + label: formatMessage(messages.adminPanel), + icon: SettingsIcon, + type: 'link', + to: '/owyx-admin', + }, + ] + : []), { id: 'sign-out', - label: formatMessage(commonMessages.signOutButton), + label: formatMessage(messages.signOutOwyx), icon: LogOutIcon, - tone: 'red', - action: () => logOut(), + color: 'red', + action: () => signOutOwyxSiteAccount(), }, ]) -async function fetchIntercomToken() { - const creds = await getCreds() - if (!creds?.session) { - throw new Error('Not authenticated') - } - - const params = new URLSearchParams() - const rawServerId = route.params.id - const serverId = Array.isArray(rawServerId) ? rawServerId[0] : rawServerId - if (route.path.startsWith('/hosting/manage/') && typeof serverId === 'string') { - params.set('server_id', serverId) - } - const query = params.size > 0 ? `?${params.toString()}` : '' - - const response = await tauriFetch(`${config.siteUrl}/api/intercom/messenger-jwt${query}`, { - method: 'GET', - headers: { - Authorization: `Bearer ${creds.session}`, - }, - }) - if (!response.ok) { - throw new Error(`Failed to fetch Intercom token: ${response.status}`) - } - return await response.json() -} - watch( [stateInitialized, showAd, adConsentAvailable], async ([ready, showAds, canManageConsent]) => { @@ -1668,8 +1555,8 @@ async function respondToServerInvite(notification, action) { async function acceptServerInviteNotification(notification) { try { - const serverId = await respondToServerInvite(notification, 'accept') - await router.push(`/hosting/manage/${encodeURIComponent(serverId)}`) + await respondToServerInvite(notification, 'accept') + await router.push('/owyx-servers') queryClient.invalidateQueries({ queryKey: ['servers'] }) } catch (error) { handleError(error) @@ -1816,16 +1703,16 @@ const updatePopupMessages = defineMessages({ }, meteredBody: { id: 'app.update-popup.body.metered', - defaultMessage: `Modrinth App v{version} is available now! Since you're on a metered network, we didn't automatically download it.`, + defaultMessage: `Owyx v{version} is available now! Since you're on a metered network, we didn't automatically download it.`, }, downloadedBody: { id: 'app.update-popup.body.download-complete', - defaultMessage: `Modrinth App v{version} has finished downloading. Reload to update now, or automatically when you close Modrinth App.`, + defaultMessage: `Owyx v{version} has finished downloading. Reload to update now, or automatically when you close Owyx.`, }, linuxBody: { id: 'app.update-popup.body.linux', defaultMessage: - 'Modrinth App v{version} is available. Use your package manager to update for the latest features and fixes!', + 'Owyx v{version} is available. Use your package manager to update for the latest features and fixes!', }, reload: { id: 'app.update-popup.reload', @@ -2001,31 +1888,7 @@ async function checkUpdates() { } async function checkLinuxUpdates() { - try { - const [response, currentVersion] = await Promise.all([ - fetch('https://launcher-files.modrinth.com/updates.json'), - getVersion(), - ]) - const updates = await response.json() - const latestVersion = updates?.version - - if (latestVersion && latestVersion !== currentVersion) { - markAppUpdateActionable(latestVersion) - const nextPopupTime = getNextAppUpdatePopupTime(latestVersion) - if (nextPopupTime !== null && Date.now() >= nextPopupTime) { - addPopupNotification({ - contentType: 'standard', - title: formatMessage(updatePopupMessages.updateAvailable), - text: formatMessage(updatePopupMessages.linuxBody, { version: latestVersion }), - type: 'info', - autoCloseMs: null, - }) - markAppUpdatePopupShown(latestVersion) - } - } - } catch (e) { - console.error('Failed to check for updates:', e) - } + // Owyx ships updates via GitHub Releases — do not poll Modrinth launcher-files CDN. } async function downloadAvailableUpdate() { @@ -2092,7 +1955,7 @@ async function installUpdate() { setAppUpdateActions({ download: downloadAvailableUpdate, install: installUpdate, - changelog: () => openUrl('https://modrinth.com/news/changelog?filter=app'), + changelog: () => openUrl(OWYX_SITE_CHANGELOG_URL), }) async function openModrinthProjectLinkInApp(parsed) { @@ -2193,7 +2056,6 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
-
- + + @@ -2307,7 +2167,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload) - - - - - @@ -2474,7 +2304,6 @@ provideAppUpdateDownloadProgress(appUpdateDownload) > {{ formatMessage(messages.authUnreachableBody) }} - +
+
+ {{ formatMessage(messages.playingAs) }} + · + {{ displayNickLabel }} + + {{ formatMessage(messages.loginAs, { login: loginNickLabel }) }} + +
+
+ +

+ {{ formatMessage(messages.accountHint) }} +

+ + +
+

+ {{ formatMessage(messages.editDisplayNickHint) }} +

+ +

{{ displayNickError }}

+
+ + +
+
+
diff --git a/apps/app-frontend/src/components/ui/ErrorModal.vue b/apps/app-frontend/src/components/ui/ErrorModal.vue index d2232d1f36f..837652031a0 100644 --- a/apps/app-frontend/src/components/ui/ErrorModal.vue +++ b/apps/app-frontend/src/components/ui/ErrorModal.vue @@ -47,7 +47,7 @@ const errorCollapsed = ref(false) const title = ref('An error occurred') const errorType = ref('unknown') -const supportLink = ref('https://support.modrinth.com') +const supportLink = ref('https://owyx.site') const metadata = ref({}) defineExpose({ @@ -58,8 +58,7 @@ defineExpose({ if (errorVal.message && errorVal.message.includes('Minecraft authentication error:')) { title.value = 'Unable to sign in to Minecraft' errorType.value = 'minecraft_auth' - supportLink.value = - 'https://support.modrinth.com/en/articles/9038231-minecraft-sign-in-issues' + supportLink.value = 'https://owyx.site' if ( errorVal.message.includes('existing connection was forcibly closed') || @@ -73,7 +72,7 @@ defineExpose({ } else if (errorVal.message && errorVal.message.includes('Move directory error:')) { title.value = 'Could not change app directory' errorType.value = 'directory_move' - supportLink.value = 'https://support.modrinth.com' + supportLink.value = 'https://owyx.site' if (errorVal.message.includes('directory is not writable')) { metadata.value.readOnly = true @@ -85,21 +84,35 @@ defineExpose({ } else if (errorVal.message && errorVal.message.includes('No loader version selected for')) { title.value = 'No loader selected' errorType.value = 'no_loader_version' - supportLink.value = 'https://support.modrinth.com' + supportLink.value = 'https://owyx.site' metadata.value.instanceId = context.instanceId } else if (source === 'state_init') { - title.value = 'Error initializing Modrinth App' + title.value = 'Error initializing Owyx' errorType.value = 'state_init' - supportLink.value = 'https://support.modrinth.com' + supportLink.value = 'https://owyx.site' } else { title.value = 'An error occurred' errorType.value = 'unknown' - supportLink.value = 'https://support.modrinth.com' + supportLink.value = 'https://owyx.site' metadata.value = {} } error.value = errorVal errorModal.value.show() + + const msg = errorVal?.message || String(errorVal || '') + if (msg) { + void import('@/helpers/owyx-telemetry') + .then(({ reportOwyxLauncherError }) => + reportOwyxLauncherError(msg, { + metadata: { + errorType: errorType.value, + source: source || null, + }, + }), + ) + .catch(() => {}) + } }, }) @@ -180,29 +193,21 @@ async function copyToClipboard(text) {