feat: [MAJOR] Rebrand (#101)

## Description

**What issue are you solving (or what feature are you adding) and how
are you doing it?**

1. Netris has been renamed and rebranded to Nestri.
2. New Logo and colors, plus a new Philosophy and mission(more on that
later)
3. We are moving all different repos into this one - which means API,
Docker, Website, Docs etc will be moved here
This commit is contained in:
Wanjohi
2024-08-20 15:12:00 +03:00
committed by GitHub
parent 3df53e7e38
commit 19923845c0
111 changed files with 1115 additions and 14702 deletions

1
.github/CODEOWNERS vendored
View File

@@ -1 +0,0 @@
* @wanjohiryan

View File

@@ -1,18 +0,0 @@
version: 2
updates:
- package-ecosystem: 'gitsubmodule'
directory: '/'
schedule:
interval: 'daily'
open-pull-requests-limit: 10
labels:
- 'type/build'
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'weekly'
labels:
- 'type/build'
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]

119
.github/labeler.yml vendored
View File

@@ -1,119 +0,0 @@
version: 2
labels:
- label: 'type/chore'
branch: '^chore/.*'
title: '^\s*.*?\schore(?:(.+))?!?:'
- label: 'type/fix'
branch: '^fix/.*'
title: '^\s*.*?\sfix(?:(.+))?!?:'
- label: 'type/feat'
branch: '^feat/.*'
title: '^\s*.*?\sfeat(?:(.+))?!?:'
- label: 'type/breaking'
body: '(?i)breaking.*'
- label: 'type/docs'
branch: '^docs/.*'
title: '^\s*.*?\sdocs(?:(.+))?!?:'
branch: '^docs/.*'
files:
- '**/*.md$'
- '**/*.mdx$'
- label: 'type/ci'
branch: '^ci/.*'
title: '^\s*.*?\sci(?:(.+))?!?:'
files:
- '.github/.+'
- label: 'type/build'
branch: '^dependabot/.*'
title: '^\s*.*?\sbuild(?:(.+))?!?:'
- label: 'type/perf'
title: '^\s*.*?\sperf(?:(.+))?!?:'
- label: 'mergeable/false'
mergeable: False
- label: 'usr/dependabot'
branch: '^dependabot/.*'
authors:
- 'dependabot[bot]'
- label: 'scope/back-end'
files:
- 'server.Dockerfile'
- label: 'scope/base'
files:
- 'base.Dockerfile'
- label: 'scope/ffmpeg'
files:
- 'ffmpeg.Dockerfile'
- label: 'scope/recorder'
files:
- 'recorder.Dockerfile'
- label: 'scope/relay'
files:
- 'relay.Dockerfile'
- label: 'scope/front-end'
files:
- 'apps/www/.+'
- label: 'pkg/www'
files:
- 'apps/www/.+'
- label: 'scope/git'
files:
- '**/.gitignore$'
- '.github/.+'
- label: "size/xs"
size:
below: 10
exclude-files: ["pnpm-lock.yml","yarn.lock"]
- label: "size/s"
size:
above: 9
below: 100
exclude-files: ["pnpm-lock.yml","yarn.lock"]
- label: "size/m"
size:
above: 49
below: 200
exclude-files: ["pnpm-lock.yml","yarn.lock"]
- label: "size/l"
size:
above: 199
below: 500
exclude-files: ["pnpm-lock.yml","yarn.lock"]
- label: "size/xl"
size:
above: 499
below: 1000
exclude-files: ["pnpm-lock.yml","yarn.lock"]
- label: "size/xxl"
size:
above: 999
exclude-files: ["pnpm-lock.yml","yarn.lock"]
- label: "usr/wanjohi"
authors: ['wanjohiryan']
- label: "usr/unknown"
negate: True
authors: ['wanjohiryan','apps/dependabot','dependabot', 'dependabot[bot]']

108
.github/labels.yml vendored
View File

@@ -1,108 +0,0 @@
# Default labels
- name: "type/fix"
color: "B60205"
- name: "type/build"
color: "6C55D7"
- name: "type/feat"
color: "0ADE12"
- name: "type/docs"
color: "891059"
- name: "type/refactor"
color: "8D44DF"
- name: "type/revert"
color: "5319e7"
- name: "type/style"
color: "D71964"
- name: "type/test"
color: "0B9EE8"
- name: "type/breaking"
color: "0590CC"
- name: "type/chore"
color: "B44A63"
- name: "type/ci"
color: "4FE1A6"
- name: "type/perf"
color: "4FE1A6"
- name: "mergeable/false"
color: "B60205"
- name: "priority/high"
color: "D4E734"
- name: "priority/mid"
color: "D4E734"
- name: "priority/low"
color: "D4E734"
- name: "reg/docker"
color: "5319e7"
- name: "reg/npm"
color: "5319e7"
- name: "pkg/www"
color: "5319e7"
- name: "pkg/lib"
color: "5319e7"
- name: "pkg/aws"
color: "5319e7"
- name: "scope/git"
color: "B61B66"
- name: "scope/infra"
color: "B61B66"
- name: "scope/front-end"
color: "B61B66"
- name: "scope/relay"
color: "B61B66"
- name: "scope/base"
color: "B61B66"
- name: "scope/back-end"
color: "B61B66"
- name: "size/xs"
color: "AD4322"
- name: "size/s"
color: "AD4322"
- name: "size/m"
color: "AD4322"
- name: "size/l"
color: "AD4322"
- name: "size/xl"
color: "AD4322"
- name: "size/xxl"
color: "AD4322"
- name: "usr/wanjohi"
color: "09469C"
- name: "usr/dependabot"
color: "09469C"
- name: "usr/unknown"
color: "09469C"

View File

@@ -1,3 +0,0 @@
## Description
**What issue are you solving (or what feature are you adding) and how are you doing it?**

View File

@@ -1,45 +0,0 @@
name-template: 'v$RESOLVED_VERSION'
tag-template: 'v$RESOLVED_VERSION'
template: |
# What's Changed
$CHANGES
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
categories:
- title: '⚠ Breaking Changes'
label: 'type/breaking'
- title: '🚀 New Features'
label: 'type/feat'
- title: '🐜 Bug Fixes'
label: 'type/fix'
- title: '🧰 Maintenance'
label: 'type/chore'
- title: '📖 Documentation'
label: 'type/docs'
- title: '⬆ Version Upgrades'
label: 'type/build'
collapse-after: 10
- title: 'Other changes'
collapse-after: 10
version-resolver:
major:
labels:
- 'type/breaking'
minor:
labels:
- 'type/feat'
patch:
labels:
- 'type/fix'
- 'type/build'
- 'type/docs'
- 'type/chore'
- 'type/refactor'
- 'type/ci'
- 'type/style'
- 'type/test'
exclude-labels:
- 'skip-changelog'

View File

@@ -1,93 +0,0 @@
name: Pull request auto-labeller
on:
pull_request:
types:
- labeled
- opened
- synchronize
- reopened
- ready_for_review
- edited
concurrency:
group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
validate:
name: Validate PR title
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: amannn/action-semantic-pull-request@cfb60706e18bc85e8aec535e3c577abe8f70378e
id: lint_pr_title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |-
build
chore
ci
deprecate
docs
feat
fix
perf
refactor
remove
revert
security
style
test
requireScope: false
# Ensures the subject start with an uppercase character.
subjectPattern: ^([A-Z]).+$
headerPattern: '^\s*.*?\s(\w*)(?:\(([\w$.\-*/ ]*)\))?: (.*)$'
headerPatternCorrespondence: type, scope, subject
subjectPatternError: |
The subject "{subject}" found in the pull request title "{title}"
didn't match the configured pattern. Please ensure that the subject
starts with an uppercase character
- uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31
# When the previous steps fails, the workflow would stop. By adding this
# condition you can continue the execution with the populated error message.
if: always() && (steps.lint_pr_title.outputs.error_message != null)
with:
header: pr-title-lint-error
message: |
Hey there and thank you for opening this pull request! 👋🏼
We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted.
Additionally, the subject of the title must start with an uppercase character (e.g. feat: New `search` component).
```
${{ steps.lint_pr_title.outputs.error_message }}
```
# Delete a previous comment when the issue has been resolved
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31
with:
header: pr-title-lint-error
delete: true
label:
needs: [ validate ]
runs-on: ubuntu-latest
name: Add labels
permissions:
contents: read
pull-requests: write
issues: write
steps:
- name: Checkout your code
uses: actions/checkout@v4
- uses: srvaroa/labeler@v1
with:
config_path: .github/labeler.yml
fail_on_error: true
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -1,135 +0,0 @@
#Tabs not spaces, you moron :)
name: CI for netris:base
on:
pull_request:
paths:
- "base.Dockerfile"
- ".github/workflows/base.yml"
schedule:
- cron: 0 0 * * * # At the end of everyday
push:
branches: [main]
paths:
- "base.Dockerfile"
- ".github/workflows/base.yml"
tags:
- v*.*.*
release:
types: [created]
# concurrency:
# group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }}
# cancel-in-progress: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: nestriness/nestri
BASE_TAG_PREFIX: base
jobs:
build-docker-pr:
name: Build image on pr
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }}
steps:
-
name: Checkout repo
uses: actions/checkout@v4
-
name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: base.Dockerfile
context: ./
push: false
load: true
tags: netris:base
build-docker-main:
name: Build image on merge
if: ${{github.ref == 'refs/heads/main'}}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
-
name: Checkout repo
uses: actions/checkout@v4
-
name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GIT_MASTER_TOKEN }}
-
name: Extract Container metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }}
#
#tag on release, and a nightly build for 'dev'
tags: |
type=raw,value=nightly,enable={{is_default_branch}}
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: base.Dockerfile
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-docker-release:
name: Build image on release
if: ${{ github.event_name == 'release' }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
-
name: Checkout repo
uses: actions/checkout@v4
-
name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GIT_MASTER_TOKEN }}
-
name: Extract Container metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }}
#
#tag on release, and a nightly build for 'dev'
tags: |
type=raw,value=nightly,enable={{is_default_branch}}
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: base.Dockerfile
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@@ -1,33 +0,0 @@
name: GH labels maintainer
on:
push:
branches:
- 'main'
paths:
- '.github/labels.yml'
- '.github/workflows/labels.yml'
pull_request:
paths:
- '.github/labels.yml'
- '.github/workflows/labels.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
labeler:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Run Labeler
uses: crazy-max/ghaction-github-labeler@v5
env:
GITHUB_TOKEN: "${{ secrets.GIT_MASTER_TOKEN }}"
with:
dry-run: ${{ github.event_name == 'pull_request' }}
github-token: ${{ secrets.GIT_MASTER_TOKEN }}

View File

@@ -1,135 +0,0 @@
#Tabs not spaces, you moron :)
name: CI for netris:relay
on:
pull_request:
paths:
- "relay.Dockerfile"
- ".github/workflows/relay.yml"
schedule:
- cron: 0 0 * * * # At the end of everyday
push:
branches: [main]
paths:
- "relay.Dockerfile"
- ".github/workflows/relay.yml"
tags:
- v*.*.*
release:
types: [created]
# concurrency:
# group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }}
# cancel-in-progress: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: netrisdotme/netris
BASE_TAG_PREFIX: relay
jobs:
build-docker-pr:
name: Build image on pr
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }}
steps:
-
name: Checkout repo
uses: actions/checkout@v4
-
name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: relay.Dockerfile
context: ./
push: false
load: true
tags: netris:relay
build-docker-main:
name: Build image on merge
if: ${{github.ref == 'refs/heads/main'}}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
-
name: Checkout repo
uses: actions/checkout@v4
-
name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GIT_MASTER_TOKEN }}
-
name: Extract Container metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }}
#
#tag on release, and a nightly build for 'dev'
tags: |
type=raw,value=nightly,enable={{is_default_branch}}
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: relay.Dockerfile
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-docker-release:
name: Build image on release
if: ${{ github.event_name == 'release' }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
-
name: Checkout repo
uses: actions/checkout@v4
-
name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GIT_MASTER_TOKEN }}
-
name: Extract Container metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }}
#
#tag on release, and a nightly build for 'dev'
tags: |
type=raw,value=nightly,enable={{is_default_branch}}
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: relay.Dockerfile
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@@ -1,16 +0,0 @@
name: Release drafter
on:
push:
branches: [ main ]
jobs:
update_release_draft:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: release-drafter/release-drafter@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,137 +0,0 @@
#Tabs not spaces, you moron :)
name: CI for netris:server
on:
pull_request:
paths:
- "server.Dockerfile"
- ".scripts/**"
- ".github/workflows/server.yml"
schedule:
- cron: 0 0 * * * # At the end of everyday
push:
branches: [main]
paths:
- "server.Dockerfile"
- ".scripts/**"
- ".github/workflows/server.yml"
tags:
- v*.*.*
release:
types: [created]
env:
REGISTRY: ghcr.io
IMAGE_NAME: nestriness/nestri
BASE_TAG_PREFIX: server
# concurrency:
# group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }}
# cancel-in-progress: true
jobs:
build-docker-pr:
name: Build image on pr
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }}
steps:
-
name: Checkout repo
uses: actions/checkout@v4
-
name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: server.Dockerfile
context: ./
push: false
load: true
tags: netris:server
build-docker-main:
name: Build image on merge to main
if: ${{github.ref == 'refs/heads/main'}}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
-
name: Checkout repo
uses: actions/checkout@v4
-
name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GIT_MASTER_TOKEN }}
-
name: Extract Container metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }}
#
#tag on release, and a nightly build for 'dev'
tags: |
type=raw,value=nightly,enable={{is_default_branch}}
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: server.Dockerfile
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-docker-release:
name: Build image on release
if: ${{ github.event_name == 'release' }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
-
name: Checkout repo
uses: actions/checkout@v4
-
name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GIT_MASTER_TOKEN }}
-
name: Extract Container metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }}
#
#tag on release, and a nightly build for 'dev'
tags: |
type=raw,value=nightly,enable={{is_default_branch}}
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: server.Dockerfile
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@@ -1,236 +0,0 @@
#Tabs not spaces, you moron :)
name: CI for netris:warp-input
on:
pull_request:
paths:
- "warp-input.Dockerfile"
- ".github/workflows/warp-input.yml"
- "bin/input/**"
schedule:
- cron: 0 0 * * * # At the end of everyday
workflow_dispatch:
push:
branches: [main]
paths:
- "warp-input.Dockerfile"
- ".github/workflows/warp-input.yml"
- "bin/input/**"
tags:
- v*.*.*
release:
types: [published, created]
env:
REGISTRY: ghcr.io
IMAGE_NAME: nestriness/nestri
BASE_TAG_PREFIX: warp-input
# concurrency:
# group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }}
# cancel-in-progress: true
jobs:
build-docker-pr:
name: Build image on pr
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }}
steps:
-
name: Checkout repo
uses: actions/checkout@v4
with:
submodules: recursive
-
name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: warp-input.Dockerfile
context: ./
push: false
load: true
tags: netris:warp-input
build-docker-main:
name: Build image on merge to main
if: ${{github.ref == 'refs/heads/main'}}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
-
name: Checkout repo
uses: actions/checkout@v4
with:
submodules: recursive
-
name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GIT_MASTER_TOKEN }}
-
name: Extract Container metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }}
#
#tag on release, and a nightly build for 'dev'
tags: |
type=raw,value=nightly,enable={{is_default_branch}}
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: warp-input.Dockerfile
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-docker-release:
name: Build image on release
if: ${{ github.event_name == 'release' }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
-
name: Checkout repo
uses: actions/checkout@v4
with:
submodules: recursive
-
name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GIT_MASTER_TOKEN }}
-
name: Extract Container metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }}
#
#tag on release, and a nightly build for 'dev'
tags: |
type=raw,value=nightly,enable={{is_default_branch}}
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: warp-input.Dockerfile
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-warp-input-release:
if: ${{ github.event_name == 'release' }}
defaults:
run:
working-directory: bin/input
strategy:
fail-fast: false
matrix:
settings:
- host: ubuntu-20.04
target: x86_64-unknown-linux-gnu
bundles: appimage
asset_name: warp-input-ubuntu-amd64
- host: windows-latest
target: x86_64-pc-windows-msvc
bundles: msi
asset_name: warp-input-windows-amd64
# - host: macos-latest
# target: x86_64-apple-darwin
# bundles: dmg
# asset_name: warp-input-macos-amd64
# - host: macos-latest
# target: aarch64-apple-darwin
# bundles: dmg
# asset_name: warp-input-macos-apple-silicon
# - host: ubuntu-20.04
# target: x86_64-unknown-linux-musl
# - host: ubuntu-20.04
# target: aarch64-unknown-linux-gnu
# - host: ubuntu-20.04
# target: aarch64-unknown-linux-musl
# - host: ubuntu-20.04
# target: armv7-unknown-linux-gnueabihf
name: Build warp-input on release
runs-on: ${{ matrix.settings.host }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Rust
id: toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.settings.target }}
toolchain: stable
components: clippy, rustfmt
- name: Cache Rust Dependencies
uses: Swatinem/rust-cache@v2
with:
save-if: false
prefix-key: 'v0-rust-deps'
shared-key: ${{ matrix.settings.target }}
- name: Cargo build
run: cargo build --target ${{ matrix.settings.target }} --release
- name: Copy and rename artifacts (Linux)
if: ${{ matrix.settings.host == 'ubuntu-20.04' }}
run: |
cp target/${{ matrix.settings.target }}/release/warp-input ./warp-input
- name: Copy and rename artifacts (Windows)
if: ${{ matrix.settings.host == 'windows-latest' }}
run: |
cp "target/${{ matrix.settings.target }}/release/warp-input.exe" ./warp-input.exe
- name: Copy and rename artifacts (macOS)
if: ${{ matrix.settings.host == 'macos-latest' }}
run: |
cp target/${{ matrix.settings.target }}/release/warp-input ./warp-input
- name: Publish release for (${{ matrix.settings.host }})
if: ${{ matrix.settings.host == 'windows-latest' }}
uses: svenstaro/upload-release-action@2.9.0
with:
repo_token: ${{ secrets.GIT_MASTER_TOKEN }}
file: ./bin/input/warp-input.exe
asset_name: ${{ matrix.settings.asset_name }}
tag: ${{ github.ref }}
- name: Publish release for (${{ matrix.settings.host }})
if: ${{ matrix.settings.host != 'windows-latest' }}
uses: svenstaro/upload-release-action@2.9.0
with:
repo_token: ${{ secrets.GIT_MASTER_TOKEN }}
file: ./bin/input/warp-input
asset_name: ${{ matrix.settings.asset_name }}
tag: ${{ github.ref }}

View File

@@ -1,234 +0,0 @@
#Tabs not spaces, you moron :)
name: CI for netris:warp
on:
pull_request:
paths:
- "warp.Dockerfile"
- ".github/workflows/warp.yml"
schedule:
- cron: 0 0 * * * # At the end of everyday
workflow_dispatch:
push:
branches: [main]
paths:
- "warp.Dockerfile"
- ".github/workflows/warp.yml"
tags:
- v*.*.*
release:
types: [published, created]
env:
REGISTRY: ghcr.io
IMAGE_NAME: nestriness/nestri
BASE_TAG_PREFIX: warp
# concurrency:
# group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }}
# cancel-in-progress: true
jobs:
build-docker-pr:
name: Build image on pr
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }}
steps:
-
name: Checkout repo
uses: actions/checkout@v4
with:
submodules: recursive
-
name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: warp.Dockerfile
context: ./
push: false
load: true
tags: netris:warp
build-docker-main:
name: Build image on merge to main
if: ${{github.ref == 'refs/heads/main'}}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
-
name: Checkout repo
uses: actions/checkout@v4
with:
submodules: recursive
-
name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GIT_MASTER_TOKEN }}
-
name: Extract Container metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }}
#
#tag on release, and a nightly build for 'dev'
tags: |
type=raw,value=nightly,enable={{is_default_branch}}
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: warp.Dockerfile
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-docker-release:
name: Build image on release
if: ${{ github.event_name == 'release' }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
-
name: Checkout repo
uses: actions/checkout@v4
with:
submodules: recursive
-
name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GIT_MASTER_TOKEN }}
-
name: Extract Container metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }}
#
#tag on release, and a nightly build for 'dev'
tags: |
type=raw,value=nightly,enable={{is_default_branch}}
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Build Docker image
uses: docker/build-push-action@v6
with:
file: warp.Dockerfile
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-warp-release:
if: ${{ github.event_name == 'release' }}
defaults:
run:
working-directory: moq-server
strategy:
fail-fast: false
matrix:
settings:
- host: ubuntu-20.04
target: x86_64-unknown-linux-gnu
bundles: appimage
asset_name: warp-ubuntu-amd64
- host: windows-latest
target: x86_64-pc-windows-msvc
bundles: msi
asset_name: warp-windows-amd64
- host: macos-latest
target: x86_64-apple-darwin
bundles: dmg
asset_name: warp-macos-amd64
- host: macos-latest
target: aarch64-apple-darwin
bundles: dmg
asset_name: warp-macos-apple-silicon
# - host: ubuntu-20.04
# target: x86_64-unknown-linux-musl
# - host: ubuntu-20.04
# target: aarch64-unknown-linux-gnu
# - host: ubuntu-20.04
# target: aarch64-unknown-linux-musl
# - host: ubuntu-20.04
# target: armv7-unknown-linux-gnueabihf
name: Build warp on release
runs-on: ${{ matrix.settings.host }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Rust
id: toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.settings.target }}
toolchain: stable
components: clippy, rustfmt
- name: Cache Rust Dependencies
uses: Swatinem/rust-cache@v2
with:
save-if: false
prefix-key: 'v0-rust-deps'
shared-key: ${{ matrix.settings.target }}
- name: Cargo build
run: cargo build --target ${{ matrix.settings.target }} --manifest-path ./moq-pub/Cargo.toml --release
- name: Copy and rename artifacts (Linux)
if: ${{ matrix.settings.host == 'ubuntu-20.04' }}
run: |
cp target/${{ matrix.settings.target }}/release/moq-pub ./warp
- name: Copy and rename artifacts (Windows)
if: ${{ matrix.settings.host == 'windows-latest' }}
run: |
cp "target/${{ matrix.settings.target }}/release/moq-pub.exe" ./warp.exe
- name: Copy and rename artifacts (macOS)
if: ${{ matrix.settings.host == 'macos-latest' }}
run: |
cp target/${{ matrix.settings.target }}/release/moq-pub ./warp
- name: Publish release for (${{ matrix.settings.host }})
if: ${{ matrix.settings.host == 'windows-latest' }}
uses: svenstaro/upload-release-action@2.9.0
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./moq-server/warp.exe
asset_name: ${{ matrix.settings.asset_name }}
tag: ${{ github.ref }}
- name: Publish release for (${{ matrix.settings.host }})
if: ${{ matrix.settings.host != 'windows-latest' }}
uses: svenstaro/upload-release-action@2.9.0
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./moq-server/warp
asset_name: ${{ matrix.settings.asset_name }}
tag: ${{ github.ref }}

38
.gitignore vendored Normal file
View File

@@ -0,0 +1,38 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Dependencies
node_modules
.pnp
.pnp.js
# Local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Testing
coverage
# Turbo
.turbo
# Vercel
.vercel
# Build Outputs
.next/
out/
build
dist
# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Misc
.DS_Store
*.pem

6
.gitmodules vendored
View File

@@ -1,6 +0,0 @@
[submodule "moq-server"]
path = moq-server
url = https://github.com/kixelated/moq-rs
[submodule "cli"]
path = cli
url = https://github.com/nestriness/cli.git

0
.npmrc Normal file
View File

View File

@@ -1,285 +0,0 @@
#!/bin/bash -e
trap "echo TRAPed signal" HUP INT QUIT TERM
# Include our gpu helper functions
source /etc/gpu_helpers.sh
# Create and modify permissions of XDG_RUNTIME_DIR
sudo -u nestri mkdir -pm700 /tmp/runtime-1000
sudo chown nestri:nestri /tmp/runtime-1000
sudo -u nestri chmod 700 /tmp/runtime-1000
# Make user directory owned by the user in case it is not
sudo chown nestri:nestri /home/nestri || sudo chown nestri:nestri /home/nestri/* || { echo "$(date +"[%Y-%m-%d %H:%M:%S]") Failed to change user directory permissions. There may be permission issues."; }
#Input devices ownable by our default user
export REQUIRED_DEVICES=${REQUIRED_DEVICES:-/dev/uinput /dev/input/event*}
declare -A group_map
for dev in $REQUIRED_DEVICES; do
if [ -e "$dev" ]; then
dev_group=$(stat -c "%G" "$dev")
dev_gid=$(stat -c "%g" "$dev")
if [ "$dev_group" = "UNKNOWN" ]; then
new_name="nestri-gid-$dev_gid"
# We only have a GID for this group; create a named group for it
# this isn't 100% necessary but it prevents some useless noise in the console
sudo groupadd -g "$dev_gid" "$new_name"
group_map[$new_name]=1
else
# the group already exists; just add it to the list
group_map[$dev_group]=1
fi
# is this device read/writable by the group? if not, make it so
if [ "$(stat -c "%a" "$dev" | cut -c2)" -lt 6 ]; then
sudo chmod g+rw "$dev"
fi
else
echo "$(date +"[%Y-%m-%d %H:%M:%S]") Path '$dev' is not present."
fi
done
join_by() { local IFS="$1"; shift; echo "$*"; }
groups=$(join_by "," "${!group_map[@]}")
if [ "$groups" != "" ]; then
echo "$(date +"[%Y-%m-%d %H:%M:%S]") Adding user '${USER}' to groups: $groups"
sudo usermod -a -G "$groups" "${USER}"
else
echo "$(date +"[%Y-%m-%d %H:%M:%S]") Not modifying user groups ($groups)"
fi
# Remove directories to make sure the desktop environment starts
sudo rm -rf /tmp/.X* ~/.cache
# Change time zone from environment variable
sudo ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime && echo "$TZ" | sudo tee /etc/timezone >/dev/null
# Add gamescope directories to path
export PATH="${PATH:+${PATH}:}/usr/local/games:/usr/games"
# This symbolic link enables running Xorg inside a container with `-sharevts`
sudo ln -snf /dev/ptmx /dev/tty7
# Start DBus without systemd
sudo /etc/init.d/dbus start
# Install Proton-GE for this user
nestri-proton -i
# Allow starting Xorg from a pseudoterminal instead of strictly on a tty console
if [ ! -f "/etc/X11/Xwrapper.config" ]; then
echo -e "allowed_users=anybody\nneeds_root_rights=yes" | sudo tee /etc/X11/Xwrapper.config > /dev/null
fi
if grep -Fxq "allowed_users=console" /etc/X11/Xwrapper.config; then
sudo sed -i "s/allowed_users=console/allowed_users=anybody/;$ a needs_root_rights=yes" /etc/X11/Xwrapper.config
fi
# Remove existing Xorg configuration
if [ -f "/etc/X11/xorg.conf" ]; then
sudo rm -f "/etc/X11/xorg.conf"
fi
# Setting `VIDEO_PORT` to none disables RANDR/XRANDR, do not set this if using datacenter GPUs
if [ "${VIDEO_PORT,,}" = "none" ]; then
export CONNECTED_MONITOR="--use-display-device=None"
# The X server is otherwise deliberately set to a specific video port despite not being plugged to enable RANDR/XRANDR, monitor will display the screen if plugged to the specific port
else
export CONNECTED_MONITOR="--connected-monitor=${VIDEO_PORT}"
fi
# A custom modeline should be generated because there is no monitor to fetch this information normally
custom_modeline="$(cvt -r "${SIZEW}" "${SIZEH}" "${REFRESH}" | sed -n 2p)"
custom_modeline_settings="$(echo "$custom_modeline" | sed 's/Modeline //')"
custom_modeline_identifier="$(echo "$custom_modeline_settings" | awk '{print $1}' | tr -d '"')"
# Pre-populate GPU information manually
if ! check_and_populate_gpus; then
exit 1
fi
# Select the GPU based on user input or first one available
selected_gpu="${GPU_SELECTION,,:-}"
if [[ -z "$selected_gpu" ]]; then
selected_gpu="${gpu_map[0]}" # Select first available GPU
echo "No GPU selected, using first one available: $selected_gpu"
elif ! selected_gpu=$(check_selected_gpu "$selected_gpu"); then
exit 1
fi
# Print selected GPU information
echo "Selected GPU: $(print_gpu_info "$selected_gpu")"
echo ""
# Get GPU vendor as separate variable
selected_gpu_vendor=$(get_gpu_vendor "$selected_gpu")
# Convert lshw gathered bus id into Xorg compatible one
xorg_bus_id=$(get_gpu_bus_xorg "$selected_gpu")
# Get GPU card path if available
selected_gpu_card=""
gpu_screen_recorder_device_arg=""
card_result=$(get_gpu_card "$selected_gpu")
if [[ $? -eq 0 && -n "$card_result" ]]; then
selected_gpu_card="$card_result"
gpu_screen_recorder_device_arg="-device $selected_gpu_card"
fi
# Check if the selected GPU is an NVIDIA GPU
if [[ "${selected_gpu_vendor,,}" =~ "nvidia" ]]; then
echo "Selected GPU is NVIDIA. Handling NVIDIA-specific configuration..."
# Install NVIDIA userspace driver components including X graphic libraries
if ! command -v nvidia-xconfig &> /dev/null; then
# Driver version is provided by the kernel through the container toolkit
export DRIVER_ARCH="$(dpkg --print-architecture | sed -e 's/arm64/aarch64/' -e 's/armhf/32bit-ARM/' -e 's/i.*86/x86/' -e 's/amd64/x86_64/' -e 's/unknown/x86_64/')"
export DRIVER_VERSION="$(head -n1 </proc/driver/nvidia/version | awk '{for(i=1;i<=NF;i++) if ($i ~ /^[0-9]+\.[0-9\.]+/) {print $i; exit}}')"
cd /tmp
# If version is different, new installer will overwrite the existing components
if [ ! -f "/tmp/NVIDIA-Linux-${DRIVER_ARCH}-${DRIVER_VERSION}.run" ]; then
# Check multiple sources in order to probe both consumer and datacenter driver versions
curl -fsSL -O "https://international.download.nvidia.com/XFree86/Linux-${DRIVER_ARCH}/${DRIVER_VERSION}/NVIDIA-Linux-${DRIVER_ARCH}-${DRIVER_VERSION}.run" || curl -fsSL -O "https://international.download.nvidia.com/tesla/${DRIVER_VERSION}/NVIDIA-Linux-${DRIVER_ARCH}-${DRIVER_VERSION}.run" || { echo "Failed NVIDIA GPU driver download. Exiting."; exit 1; }
fi
# Extract installer before installing
sudo sh "NVIDIA-Linux-${DRIVER_ARCH}-${DRIVER_VERSION}.run" -x
cd "NVIDIA-Linux-${DRIVER_ARCH}-${DRIVER_VERSION}"
# Run installation without the kernel modules and host components
sudo ./nvidia-installer --silent \
--no-kernel-module \
--install-compat32-libs \
--no-nouveau-check \
--no-nvidia-modprobe \
--no-rpms \
--no-backup \
--no-check-for-alternate-installs
sudo rm -rf /tmp/NVIDIA* && cd ~
fi
# Generate /etc/X11/xorg.conf with nvidia-xconfig
sudo nvidia-xconfig --virtual="${SIZEW}x${SIZEH}" --depth="$CDEPTH" --mode="$(echo "$custom_modeline" | awk '{print $2}' | tr -d '\"')" --allow-empty-initial-configuration --no-probe-all-gpus --busid="$xorg_bus_id" --include-implicit-metamodes --mode-debug --no-sli --no-base-mosaic --only-one-x-screen ${CONNECTED_MONITOR}
# Guarantee that the X server starts without a monitor by adding more options to the configuration
sudo sed -i '/Driver\s\+"nvidia"/a\ Option "ModeValidation" "NoMaxPClkCheck,NoEdidMaxPClkCheck,NoMaxSizeCheck,NoHorizSyncCheck,NoVertRefreshCheck,NoVirtualSizeCheck,NoExtendedGpuCapabilitiesCheck,NoTotalSizeCheck,NoDualLinkDVICheck,NoDisplayPortBandwidthCheck,AllowNon3DVisionModes,AllowNonHDMI3DModes,AllowNonEdidModes,NoEdidHDMI2Check,AllowDpInterlaced"' /etc/X11/xorg.conf
# Add custom generated modeline to the configuration
sudo sed -i '/Section\s\+"Monitor"/a\ '"$custom_modeline" /etc/X11/xorg.conf
# Prevent interference between GPUs, add this to the host or other containers running Xorg as well
echo -e "Section \"ServerFlags\"\n Option \"AutoAddGPU\" \"false\"\nEndSection" | sudo tee -a /etc/X11/xorg.conf > /dev/null
else
echo "Selected GPU is non-NVIDIA. Handling common configuration..."
# We need permissions for the GPU(s)
sudo chown -R root:root /dev/dri/*
sudo chmod -R 777 /dev/dri/*
# Create common config file
sudo touch /etc/X11/xorg.conf
config_common_xorg="
Section \"ServerLayout\"
Identifier \"Layout0\"
Screen 0 \"Screen0\"
InputDevice \"Keyboard0\" \"CoreKeyboard\"
InputDevice \"Mouse0\" \"CorePointer\"
EndSection
Section \"InputDevice\"
Identifier \"Mouse0\"
Driver \"mouse\"
Option \"Protocol\" \"auto\"
Option \"Device\" \"/dev/mouse\"
Option \"Emulate3Buttons\" \"no\"
Option \"ZAxisMapping\" \"4 5\"
EndSection
Section \"InputDevice\"
Identifier \"Keyboard0\"
Driver \"kbd\"
EndSection
Section \"Device\"
Identifier \"Device0\"
Driver \"modesetting\"
BusID \"$xorg_bus_id\"
EndSection
Section \"Screen\"
Identifier \"Screen0\"
Device \"Device0\"
Option \"ModeDebug\" \"True\"
EndSection
Section \"ServerFLags\"
Option \"AutoAddGPU\" \"off\"
EndSection
"
echo "$config_common_xorg" | sudo tee /etc/X11/xorg.conf > /dev/null
fi
# Default display is :0 across the container
export DISPLAY=":0"
# Run Xorg server with required extensions
/usr/bin/Xorg vt7 -noreset -novtswitch -sharevts -dpi "${DPI}" -fakescreenfps "${REFRESH}" +extension "COMPOSITE" +extension "DAMAGE" +extension "GLX" +extension "RANDR" +extension "RENDER" +extension "MIT-SHM" +extension "XFIXES" +extension "XTEST" "${DISPLAY}" &
# Wait for X11 to start
echo "Waiting for X socket"
until [ -S "/tmp/.X11-unix/X${DISPLAY/:/}" ]; do sleep 1; done
echo "X socket is ready"
# Wait for X11 to start
echo "$(date +"[%Y-%m-%d %H:%M:%S]") Waiting for X socket"
until [ -S "/tmp/.X11-unix/X${DISPLAY/:/}" ]; do sleep 1; done
echo "$(date +"[%Y-%m-%d %H:%M:%S]") X socket is ready"
# Additional non-NVIDIA configuration required
if [[ ! "${selected_gpu_vendor,,}" =~ "nvidia" ]]; then
# Get a list of all available outputs (connected or disconnected)
all_outputs=($(xrandr --query | awk '/ connected| disconnected/ {print $1}'))
for selected_output in "${all_outputs[@]}"; do
# Create a unique mode identifier by appending the output name
unique_mode_identifier="${selected_output}-${custom_modeline_identifier}"
# Create a unique modeline setting with the new identifier
unique_modeline_settings="$(echo "$custom_modeline_settings" | sed "s/$custom_modeline_identifier/$unique_mode_identifier/" | tr -d '"')"
# Check if the mode already exists for this output (avoid duplicates)
if xrandr --query | grep "$selected_output" | grep -q "$unique_mode_identifier"; then
echo "Mode '$unique_mode_identifier' already exists for output '$selected_output', skipping.."
continue
fi
# Add the new mode for the specific output (using the unique settings variable)
if xrandr --newmode $unique_modeline_settings; then
echo "Successfully added mode '$unique_mode_identifier' for output '$selected_output'"
# Configure the output to use the new mode
if xrandr --addmode "$selected_output" "$unique_mode_identifier" && \
xrandr --output "$selected_output" --primary --mode "$unique_mode_identifier"; then
echo "Successfully configured output '$selected_output' to use mode '$unique_mode_identifier'"
break
fi
fi
echo "Failed to configure output '$selected_output' to use mode '$unique_mode_identifier', trying the next output.."
done
if [[ "$selected_output" == "${all_outputs[-1]}" ]]; then
echo "Could not configure any output with the desired mode"
exit 1
fi
fi
# Make sure gpu-screen-recorder is owned by nestri
sudo chown nestri:nestri /usr/bin/gpu-screen-recorder
if [[ -z "${SESSION_ID}" ]]; then
echo "$(date +"[%Y-%m-%d %H:%M:%S]") No stream name was found, did you forget to set the env variable NAME?" && exit 1
else
/usr/bin/gpu-screen-recorder $gpu_screen_recorder_device_arg -v no -w screen -c flv -f "${REFRESH}" -a "$(pactl get-default-sink).monitor" | ffmpeg -hide_banner -v quiet -i pipe:0 -c copy -f mp4 -movflags empty_moov+frag_every_frame+separate_moof+omit_tfhd_offset - | /usr/bin/warp --name "${SESSION_ID}" https://fst.so:4443 &
fi
openbox-session &
# /usr/games/gamescope -- mangohud glxgears > /dev/null &
echo "$(date +"[%Y-%m-%d %H:%M:%S]") Session Running. Press [Return] to exit."
read

View File

@@ -1,610 +0,0 @@
#!/bin/bash
# Version 1.1
#Copied from https://github.com/noabody/unibuild/blob/master/data/wstart
# WINEARCH win32/win64 not as good as binary wine/wine64"
# WINEDEBUG="-all" not as good as dev/null
# complex command line builder to simplify command objects
# vars bin, pfx set to wine binary, prefix dirs initially
# menus will dynamically set bin pfx to specific targets
# menu shows gui based 32/64-bit pe header exe via pev
# var 'x' for unified cross-functionality
# ${@:2} skips 'wstart' and 1st arg
# manjaro 21.2.1
#sudo pacman -S bash binutils findutils gendesk grep icoutils pcre2 perl yay winetricks; yay -S pev
# wnbin needs wine dir with: bin lib lib64 share
# manjaro default path is /usr
# can symlink various wnbin="$HOME/.local/opt"
#mkdir "$HOME"/.local/opt
#ln -sf /usr "$HOME"/.local/opt/wine
#ln -sf /usr/share/steam/compatibilitytools.d/proton-ge-custom/files "$HOME"/.local/opt/proton
wnbin="/usr"
# top level wine dir, symlink into "$HOME/.local/opt" to flatten paths
wnpfx="$HOME"
# top level wine prefix dir
pntop="$HOME/.steam"
# top level linux steam dir
pnapp="$pntop/steam/steamapps"
# steamapps subdir
pnbin="$pnapp/common"
# proton subdir normally under top/app
pnpfx="$pnapp/compatdata"
# proton prefix subdir normally under top/app
pnpge="$pntop/root/compatibilitytools.d"
# proton ge
progs="drive_c/Program Files"
# Program Files standard subdir
stcmn="Steam/steamapps/common"
# windows steam client subdir under progs
desk="$HOME/Desktop"
# desktop entry folder
icon="applications-other"
# default icon
temp="$HOME/Downloads"
# temp folder
clprm=("${@:2}")
# store cmdline args minus first option
x="$(echo "$1" | grep -Pio '(?<=-)[wp]')"
# 1st letter of 1st cmd line arg determines wine/proton
if [[ -n "$x" ]]; then
xarg="$(echo "$1" | perl -pe 's/-[wp]/-x/gi')"
else
xarg="$(echo "$1" | perl -pe 's/-x+/-/gi')"
fi
# drop 1st letter x or change to it
xcmd=()
i_mnus=()
myprnt=()
i_syms=()
pmenu=("Command Prompt/wineconsole.exe" "Control Panel/control.exe" "Registry Editor/regedit.exe" "Task Manager/taskmgr.exe" "Windows Explorer/explorer.exe" "Wine Configuration/winecfg.exe")
# scalable built-in programs menu
unset WINEARCH WINEDLLPATH WINEPREFIX STEAM_COMPAT_CLIENT_INSTALL_PATH STEAM_COMPAT_DATA_PATH
# prevent shell inheritance of env vars we use
w_menu () {
PS3="Please enter your choice: "
select answer in "${i_mnus[@]}"; do
for item in "${i_mnus[@]}"; do
if [[ $item == "$answer" ]]; then
break 2
fi
done
done
# repeating menu requires valid selection from array
if [[ "$answer" = "quit" ]]; then
# pop quit from end of array for menu option
exit
else
xmrtn="$answer"
fi
unset i_mnus
clear
}
# Path ordering: wine64 x64/x32 or wine32 x32 then standard
# Order critical to proper operation
xn64 () {
xstrt="wine64"
xnldl="$xnbin/lib64:$xnbin/lib"
xndll="$xnbin/lib64/wine:$xnbin/lib/wine"
}
xn32 () {
xstrt="wine"
xnldl="$xnbin/lib"
xndll="$xnbin/lib/wine"
}
xnint () {
if [[ "$x" = "p" ]]; then
xnbin="$pnbin"
xnpfx="$pnpfx"
dpth=(4 3)
else
xnbin="$wnbin"
xnpfx="$wnpfx"
dpth=(3 2)
fi
}
xnexe () {
# menu installed wine/proton or exit
readarray -t i_mnus < <(find -L "$xnbin" -maxdepth "${dpth[0]}" -type f -iname 'wine' ! \( -ipath '*/sbin*' \) 2>/dev/null | perl -pe "s|\Q$xnbin\E/(.*)[/]*bin/wine|\1| ; s|/$||" | sort ; echo "quit")
if [[ ${#i_mnus[@]} -gt 2 ]]; then
clear
w_menu
xnbin="$(realpath "$xnbin/$xmrtn")"
unset xmrtn
elif [[ ${#i_mnus[@]} -eq 2 ]]; then
xnbin="$(realpath "$xnbin/${i_mnus[0]}")"
else
echo "No installed Wine/Proton found."
exit 1
fi
}
xndef () {
# create default prefix cross-function
if [[ "$x" = "p" ]]; then
if [[ ! -d "$xnpfx/0" ]]; then
# always create default 0 prefix
xnpfx="$xnpfx/0"
echo "Creating default prefix: $xnpfx"
mkdir -p "$xnpfx"
STEAM_COMPAT_DATA_PATH="$xnpfx" "${xnbin%/*}/proton" run > /dev/null 2>&1 &
xnpfx="$xnpfx/pfx"
fi
else
if [[ ! -d "$xnpfx/.wine" ]]; then
# always create default wine prefix
xnpfx="$xnpfx/.wine"
echo "Creating default prefix: $xnpfx"
WINEPREFIX="$xnpfx" "$xnbin"/bin/winecfg > /dev/null 2>&1 &
if [[ -d "$HOME/.wine" && "$HOME/.wine" != "$xnpfx" ]]; then
rm -rf "$HOME"/.wine
ln -sf "$xnpfx" "$HOME"/.wine
fi
fi
fi
}
xnpre () {
# menu wine/proton prefix
readarray -t i_mnus < <(find "$xnpfx" -maxdepth "${dpth[1]}" -type f -iname 'system.reg' 2>/dev/null | perl -pe "s|\Q$xnpfx\E/(.*)/system.reg|\1|" | sort ; echo "quit")
# use perl escaped Q/E to preserve special characters in path variable
if [[ ${#i_mnus[@]} -gt 2 ]]; then
# display menu with min two options plus quit
# guard proton cross-function
if [[ "$x" = "p" ]]; then
# shellcheck disable=SC2044
for value in $(find "$xnpfx" -maxdepth 1 -type d -ipath '*/[0-9]*' -printf "${xnpfx///compatdata}/appmanifest_%P.acf\n" 2>/dev/null); do
test -f "$value" && myprnt+=("$(grep -Pio '^\s+\"(appid|name)\"\s+\"(.*)\"' "$value" | perl -pe 's/.*appid.+?\"(.*)\"\v|.*name.+?\"(.*)\"/\1 \2/')")
done
if [[ ${#myprnt[@]} -gt 0 ]]; then
printf '%s\n' "${myprnt[@]}" | sort
fi
# correlate appmanifest to proton prefix and list before menu
unset myprnt
fi
w_menu
xnpfx="$xnpfx/$xmrtn"
unset xmrtn
elif [[ ${#i_mnus[@]} -eq 2 ]]; then
# don't menu if only one option plus quit
xnpfx="$xnpfx/${i_mnus[0]}"
fi
if [[ -d "$xnpfx/$progs (x86)" ]]; then
xn64
else
xn32
fi
}
xnenv () {
# core env vars allow proper targetting of wine/proton
xpath="$xnbin/bin:$PATH"
xcmd=(env PATH="$xpath" WINEDLLPATH="$xndll" LD_LIBRARY_PATH="$xnldl" WINEPREFIX="$xnpfx")
# guard proton cross-function which adds on to core env vars
if [[ "$x" = "p" ]]; then
xcmd+=(STEAM_COMPAT_DATA_PATH="${xnpfx///pfx}" STEAM_COMPAT_CLIENT_INSTALL_PATH="$pntop")
fi
}
xnldr () {
# loader default to proton as applicable, otherwise wine
if [[ "$x" = "p" ]]; then
read -r -p 'wine loader? [y/N] ' chse
if [[ "$chse" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
xcmd+=("$xstrt")
else
xcmd+=("${xnbin%/*}/proton" "run")
fi
else
xcmd+=("$xstrt")
fi
}
xnset () {
# set cross-fuction
xnint
# wine/proton menu
xnexe
# create default prefix as required
xndef
# wine/proton prefix
xnpre
# wine/proton env vars
xnenv
}
xlnch () {
# cross-function command line launcher
if [[ -z "$dbg" ]]; then
("${xcmd[@]}" > /dev/null 2>&1 &)
else
echo "${xcmd[@]}"
if [[ "$dbg" = "1" ]]; then
("${xcmd[@]}" &)
elif [[ "$dbg" = "2" ]]; then
(WINEDEBUG="warn+all" "${xcmd[@]}" &)
fi
fi
# prepend cmd with dbg=1 to see command and default debug output
# dbg=2 to see command and all debug output, dbg=? for command only
}
allexe () {
# unfiltered list of exe in specified path
if [[ -n "$(stat --file-system --format=%T "$(stat --format=%m "$pedir" 2>/dev/null)" 2>/dev/null | grep -Pio 'fuse')" ]]; then
readarray -t i_mnus < <(find "$pedir" -maxdepth 7 -type f -regextype posix-extended -iname '*.exe' 2>/dev/null | perl -pe "s|\Q$pedir\E/(.*)|\1|" | sort ; echo "quit")
# skip exe validity tests if file is on network drive
else
readarray -t i_mnus < <(env pedir="$pedir" find "$pedir" -maxdepth 7 -type f -regextype posix-extended -iname '*.exe' -exec sh -c '(readpe -h optional "$1" 2>/dev/null | grep -Piq '0x2.*gui') && (wrestool "$1" 2>/dev/null | grep -Piq 'type=icon') && echo "$1" 2>/dev/null | perl -pe "s|\Q$pedir\E/(.*)|\1|"' -- {} \; 2>/dev/null | sort ; echo "quit")
# perform exe validity tests if file is on local drive
fi
}
fewexe () {
# filtered list of exe in standard paths
readarray -t i_mnus < <(env pedir="$pedir" find "$pedir" -maxdepth 7 -type f -regextype posix-extended ! \( -ipath '*cache*' -o -ipath '*/microsoft*' -o -ipath '*/windows*' -o -ipath '*/temp*' \) ! \( -iregex '.*(capture|clokspl|helper|iexplore|install|internal|kernel|[^ ]launcher|legacypm|overlay|proxy|redist|renderer|(crash|error)reporter|serv(er|ice)|setup|streaming|tutorial|unins|update).*' \) -iname '*.exe' -exec sh -c '(readpe -h optional "$1" 2>/dev/null | grep -Piq '0x2.*gui') && (wrestool "$1" 2>/dev/null | grep -Piq 'type=icon') && echo "$1" 2>/dev/null | perl -pe "s|\Q$pedir\E/(.*)|\1|"' -- {} \; 2>/dev/null | sort ; echo "quit")
# valid exe will have gui and icon
}
alloth () {
# unfiltered list of variable type in specified path
readarray -t i_mnus < <(find "$pedir" -maxdepth 7 -type f -regextype posix-extended -iname "$xflt" 2>/dev/null | perl -pe "s|\Q$pedir\E/(.*)|\1|" | sort ; echo "quit")
}
fewoth () {
# filtered list of variable type in standard paths
readarray -t i_mnus < <(env pedir="$pedir" find "$pedir" -maxdepth 7 -type f -regextype posix-extended ! \( -ipath '*cache*' -o -ipath '*/microsoft*' -o -ipath '*/windows*' -o -ipath '*/temp*' \) ! \( -iregex '.*(capture|clokspl|helper|iexplore|install|internal|kernel|[^ ]launcher|legacypm|overlay|proxy|redist|renderer|(crash|error)reporter|serv(er|ice)|setup|streaming|tutorial|unins|update).*' \) -iname "$xflt" 2>/dev/null | perl -pe "s|\Q$pedir\E/(.*)|\1|" | sort ; echo "quit")
}
xbld () {
# cross-function custom prefix builder
pnpfx="$pnpfx/${clprm[0]}"
wnpfx="$wnpfx/${clprm[0]}"
xnint
if [[ -z "${clprm[0]}" ]]; then
echo "Wine/Proton prefix name required: (e.g. .wine, 0 )"
elif [[ -d "$xnpfx" ]]; then
echo "Wine/Proton Prefix exists: $xnpfx"
else
xnexe
echo "Creating Wine/Proton Prefix: ${clprm[0]}"
if [[ "$x" = "p" ]]; then
xnenv
mkdir -p "$xnpfx"
xcmd+=(STEAM_COMPAT_DATA_PATH="$xnpfx" "${xnbin%/*}/proton" "run")
else
read -r -p '32-bit only? [y/N] ' chse
if [[ "$chse" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
xn32
xnenv
xcmd+=(WINEARCH="win32" "$xstrt" "winecfg.exe")
else
xn64
xnenv
xcmd+=(WINEARCH="win64" "$xstrt" "winecfg.exe")
fi
fi
xlnch
fi
}
xpmn () {
# use specified exe, menu specified folder, or menu system
if [[ -f "${clprm[0]}" ]]; then
# parse 1st cmdline arg, queue if valid file
pedir="$(realpath "${clprm[0]}")"
xmrtn="$(basename "$pedir")"
pedir="$(dirname "$pedir")"
else
if [[ -d "${clprm[0]}" ]]; then
# parse 1st cmdline arg, use as path if valid
pedir="$(realpath "${clprm[0]}")"
test -z "$xflt" && allexe || alloth
else
# if no cmdline path, use prefix drive_c
pedir="$xnpfx/drive_c"
test -z "$xflt" && fewexe || fewoth
fi
# create menu, from path, of file
test ${#i_mnus[@]} -gt 1 && w_menu
fi
}
xlyt() {
# pe layout for launch
# 64-bit prefix, 32-bit pe header, reset env to 32
if [[ -n "$(readpe -h optional "$pedir/$xmrtn" 2>/dev/null | grep -Pi 'magic number.*0x10b')" && -d "$xnpfx/$progs (x86)" ]]; then
xn32
xnenv
fi
xnldr
# if 1st arg is file/folder, skip it and run selection + remaining args
if [[ -e "${clprm[0]}" ]];then
xcmd+=("$pedir/$xmrtn" "${clprm[@]:1}")
else
xcmd+=("$pedir/$xmrtn" "${clprm[@]}")
fi
}
xstm() {
if [[ "$x" = "p" ]]; then
sstrt="$(realpath "$(which steam)" 2>/dev/null)"
else
xnset
sstrt="$(find "$xnpfx/drive_c" -maxdepth 3 -iname 'steam.exe' 2>/dev/null)"
if [[ -f "$sstrt" ]]; then
pnapp="$(dirname "$sstrt")/steamapps"
xcmd+=("$xstrt")
fi
fi
# find wine/proton steam binary path, normally subdir of program files
if [[ -f "$sstrt" ]]; then
test -d "$pnapp" && readarray -t i_mnus < <(find "$pnapp" -maxdepth 1 -type f -iname 'appmanifest_*.acf' -exec grep -Pio '^\s+\"(appid|name)\"\s+\"(.*)\"' "{}" \; 2>/dev/null | perl -pe 's/.*appid.+?\"(.*)\"\v|.*name.+?\"(.*)\"/\1 \2/' | sort ; echo -e "steam\nquit")
# read appmanifests to create menu entries
test ${#i_mnus[@]} -gt 2 && w_menu && xmrtn="$(expr "$xmrtn" : '\([0-9]*\)')"
if [[ -n "$xmrtn" ]]; then
# lauch selection with steam
xcmd+=("$sstrt" "-no-browser" "-applaunch" "$xmrtn")
xlnch
else
# launch steam was selected
# minigamelist (short game list) for no-browser (disabled chrome) to save memory
xcmd+=("$sstrt" "-no-browser" "steam://open/minigameslist")
xlnch
fi
else
echo -e "Steam not found."
fi
}
xpge () {
test -d "$pnpge" || mkdir -p "$pnpge"
test -d "$pnbin" || mkdir -p "$pnbin"
if [[ ! -d "$(dirname "$pnpge")" ]]; then
echo -e "Could not create folder 'compatibilitytools.d/protonge' in:\n $(dirname "$pnpge")\n because that path does not exist.\nVerify script variable 'pnpge'"
elif [[ ! -d "$pnbin" ]]; then
echo -e "Could not create sym-link 'protonge' in:\n $pnbin\n because that path does not exist.\nVerify script variable 'pnbin'"
else
gedl="$(curl -sL https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest | jq -r ".tag_name")"
# gedl="$(gh release list -R GloriousEggroll/proton-ge-custom -L 1 | grep -Pio '^ge[^ ]+')"
gever="$(echo "$gedl" | grep -Pio '(?<=ge-proton).*')"
if [[ -f "$pnpge/protonge/version" ]]; then
if [[ -z "$(grep -Pio "$gever" "$pnpge/protonge/version")" ]]; then
echo -e "Available Proton GE $gever differs from installed, updating...\n"
chse=y
else
echo -e "Available Proton GE $gever matches installed, nothing to do.\n"
fi
else
echo -e "Proton GE not found, installing...\n"
chse=y
fi
fi
if [[ -n "$chse" ]]; then
wget --progress=dot:giga "$(curl -s https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest | grep browser_download_url | cut -d\" -f4 | grep .tar.gz)" -P "$temp"/
rm -rf "$pnpge/protonge"
tar -xf "$temp/$gedl".tar.gz -C "$pnpge/"
mv "$pnpge"/*roton* "$pnpge/protonge"
rm -f "$temp/$gedl".tar.gz
grep -Piq "$gever" "$pnpge/protonge/version" || perl -pi -e "s|(?<=ge-proton).*|$gever|gi" "$pnpge/protonge/version"
test -h "$pnbin/protonge" || ln -sf "$pnpge/protonge" "$pnbin"
fi
}
xwn () {
export WINE_BRANCH=staging
sudo mkdir -pm755 /etc/apt/keyrings && sudo curl -fsSL -o /etc/apt/keyrings/winehq-archive.key "https://dl.winehq.org/wine-builds/winehq.key" \
&& sudo curl -fsSL -o "/etc/apt/sources.list.d/winehq-$(grep UBUNTU_CODENAME= /etc/os-release | cut -d= -f2 | tr -d '\"').sources" "https://dl.winehq.org/wine-builds/ubuntu/dists/$(grep UBUNTU_CODENAME= /etc/os-release | cut -d= -f2 | tr -d '\"')/winehq-$(grep UBUNTU_CODENAME= /etc/os-release | cut -d= -f2 | tr -d '\"').sources" \
&& sudo apt-get update && sudo apt-get install --install-recommends -y winehq-${WINE_BRANCH} \
&& sudo curl -fsSL -o /usr/bin/winetricks "https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks" \
&& sudo chmod 755 /usr/bin/winetricks \
&& sudo curl -fsSL -o /usr/share/bash-completion/completions/winetricks "https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks.bash-completion"
}
usage() {
echo -e "\n$(basename $0): ERROR - $*" 1>&2
echo -e "\nusage: $(basename $0)\n [-?a,--?add] [-?b,--?bld] [-?c,--?cmd] [-?d,--?dsk]\n [-?i,--?inf] [-?k,--?kil] [-?o,--?ovr] [-?p,--?prg]\n [-?s,--?stm] [-?t,--?trk] [-?u,--?cut] [-?v,--?ver]\n\n[?] = (p)roton, (w)ine\n (add) exe path to reg, (bld) build prefix,\n (cmd) prog menu, (dsk) desktop, (inf) exe info,\n (kil) kill wine, (ovr) overrides, (prg) exe list,\n (stm) steam, (trk) winetricks, (cut) shortcut,\n (ver) wine version\n" 1>&2
}
if [[ $# -lt 1 ]]; then
usage "one option required!"
else
case $xarg in
-xa|--xadd)
# cross-fuction path add to registry based on exe
xnint
xnpre
xpmn
if [[ -n "$xmrtn" ]]; then
ptadd="$(dirname "$pedir/$xmrtn")"
ptadd="z:${ptadd////\\\\}"
read -r -p 'prepend to system path? [y/N] ' chse
clear
if [[ "$chse" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
xnreg='System\\CurrentControlSet\\Control\\Session Manager\\Environment'
xmrtn='system.reg'
else
xnreg='Environment'
xmrtn='user.reg'
fi
if [[ -z "$(pcre2grep -Mio "\[\Q${xnreg,,}\E\](?s).+?\"PATH\"=str\(2\):\".+?(?=\[.+?\])(?-s)" "$xnpfx/$xmrtn")" ]]; then
perl -0777 -pi -e "s|(\[\Q${xnreg,,}\E\](?s).+?#time=(?-s).*)(?s)(.+?)(?=\[.+?\])(?-s)|\\1\n\"PATH\"=str\(2\):\"${ptadd//\\/\\\\}\"\\2|gi" "$xnpfx/$xmrtn"
echo -e "$( (echo "$xnreg" | grep -Pioq '\\Environment') && echo 'HKLM\\' || echo 'HKCU\\')$xnreg:\n\n $ptadd\n\nPATH created successfully\n"
elif [[ -z "$(pcre2grep -Mio "\[\Q${xnreg,,}\E\](?s).+?\"PATH\"=str\(2\):\"(?-s).*\Q${ptadd,,}\E[\;\"](?s).+?(?=\[.+?\])(?-s)" "$xnpfx/$xmrtn")" ]]; then
perl -0777 -pi -e "s|(\[\Q${xnreg,,}\E\](?s).+?\"PATH\"=str\(2\):\")(?-s)(.*)(?s)(.+?)(?=\[.+?\])(?-s)|\\1${ptadd//\\/\\\\}\;\\2\\3|gi" "$xnpfx/$xmrtn"
echo -e "$( (echo "$xnreg" | grep -Pioq '\\Environment') && echo 'HKLM\\' || echo 'HKCU\\')$xnreg:\n\n $ptadd\n\nPATH added successfully\n"
else
echo -e "$( (echo "$xnreg" | grep -Pioq '\\Environment') && echo 'HKLM\\' || echo 'HKCU\\')$xnreg:\n\n $ptadd\n\nalready in PATH\n"
fi
# \Q \E adds \ to non alphanums but variable with \E ends \Q
# lowercase ${var,,} to avoid since path/reg not case sensitive
# linux path is case sensitive so user must not create duplicates
fi
;;
-xb|--xbld)
# cross-function prefix builder
xbld
;;
-xc|--xcmd)
# cross-function standard tools menu
xnset
readarray -t i_mnus < <(printf '%s\n' "${pmenu[@]}" | perl -pe 's|/.*||gi' ; echo "quit")
w_menu
xmrtn="$(printf '%s\n' "${pmenu[@]}" | grep -Pio "(?<=$xmrtn/).*")"
xnldr
if [[ -f "${clprm[0]}" ]];then
pedir="$(realpath "${clprm[0]}")"
cd "$(dirname "$pedir")" || exit
xcmd+=("$xmrtn" "$pedir" "${clprm[@]:1}")
else
xcmd+=("$xmrtn" "${clprm[@]}")
fi
xlnch
;;
-xd|--xdsk)
# cross-function wine desktop
xnset
xnldr
xcmd+=("explorer.exe" "/desktop=shell,1024x768" "explorer.exe")
xlnch
;;
-i|--install)
# proton ge
xpge
xwn
;;
-xi|--xinf)
# cross-fuction program info
if [[ ! -f "${clprm[0]}" && ! -d "${clprm[0]}" ]]; then
# don't menu prefix on supplied file or folder
xnint
xnpre
fi
if [[ ! -f "${clprm[0]}" ]]; then
# offer to menu dll if no file given
read -r -p 'query dll? [y/N] ' chse
if [[ "$chse" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
xflt="*.dll"
fi
fi
xpmn
IFS=$'\n'
if [[ -n "$xmrtn" ]]; then
if [[ $(readpe "$pedir/$xmrtn" 2>/dev/null) ]]; then
# if file is PE print 32/64-bit and dll references
myprnt+=("$(readpe -h optional "$pedir/$xmrtn" 2>/dev/null | grep -Piq 'PE32\+' && echo -e "FILE:\n$xmrtn\n \nPE HEADER:\n64-bit" || echo -e "FILE:\n$xmrtn\n \nPE HEADER:\n32-bit")")
myprnt+=("$(echo -e ' \nVersion:' ; peres -v "$pedir/$xmrtn" 2>/dev/null | grep -Pio '(?<=Product Version:).*' | tr -d ' ')")
# find dll references, filenames without spaces
myprnt+=("$(echo -e ' \nREFERENCES:' ; strings "$pedir/$xmrtn" | grep -Pio '[^<>:"/\\|?*\s]+\.dll' | perl -pe 's|([^/]*\Z)|lc($1)|e' | sort -u ; echo ' ')")
else
myprnt+=("$(echo -e ' \nNot a 32/64-bit program, no information to provide\n ')")
fi
else
myprnt+=("$(echo -e ' \nNo file found\n ')")
fi
printf '%s\n' "${myprnt[@]}"
unset IFS myprnt
;;
-xk|--xkil)
# cross-function wine kill, must select same as running
xnset
xcmd+=("wineserver" "-k")
xlnch
;;
-xo|--xovr)
# cross-function prefix override list
xnint
xnpre
read -r -p 'per application? [y/N] ' chse
clear
IFS=$'\n'
myprnt+=("$(echo -e "Prefix:\n$xnpfx\n ")")
if [[ "$chse" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
# show existing per-application overrides
myprnt+=("$(echo -e 'Per-application overrides:' ; pcre2grep -Mio '\[\Qsoftware\\wine\\appdefaults\\\E[^\\]+\Q\\dlloverrides\E\](?s).+?(?=\[.+?\])(?-s)' "$xnpfx/user.reg" | grep -Pio '(?<=appdefaults..).*(?=..dlloverrides)|\".*\"' && echo ' ' || echo -e 'None found\n ')")
else
# show existing prefix overrides
myprnt+=("$(echo -e 'Global overrides:' ; pcre2grep -Mio '\[\Qsoftware\\wine\\dlloverrides\E\](?s).+?(?=\[.+?\])(?-s)' "$xnpfx/user.reg" | grep -Pio '\".*\"' && echo ' ' || echo -e 'None found\n ')")
fi
printf '%s\n' "${myprnt[@]}"
unset IFS myprnt
# A command like:
# perl -pi -e 's/(\".*msvc.*\"=\")(.*),(.*)(")/\1\3,\2\4/g' user.reg
# Swaps msvc entries (native,builtin) to (builtin,native)
;;
-xr|--xrun)
# cross-fuction run program - 1st arg valid file to run, folder to menu,
# neither (sys menu), 2nd arg... passed to exe
xnset
xpmn
if [[ -n "$xmrtn" ]]; then
xlyt
# change to exe dir before run
cd "$(dirname "$pedir/$xmrtn")" || exit
xlnch
fi
;;
-xs|--xstm)
# cross-function steam launcher
xstm
;;
-xt|--xtrk)
# cross-function winetricks
xnset
# winetricks for selected wine/proton prefix
if [[ ${#clprm[@]} -gt 0 ]]; then
xcmd+=("winetricks" "${clprm[@]}")
dbg="1"
# use args if supplied, otherwise gui
else
xcmd+=("winetricks" "--gui")
# protontricks may work better
fi
xlnch
;;
-xu|--xcut)
# cross-function desktop shortcut
if [[ -d "$desk" ]]; then
xnset
xpmn
if [[ -n "$xmrtn" ]]; then
xlyt
# change to desktop dir before create icon
cd "$desk" || exit
read -r -e -p $'Shortcut Name?\x0a' -i "$(basename "${xmrtn/.*}")" chse
# create desktop entry
gendesk -f -n --name="$chse" --comment='created by wstart' --custom='Keywords=wine;proton;launcher;' --exec="bash -c 'cd \"$(dirname "$pedir/$xmrtn")\" ; $(printf '"%s" ' "${xcmd[@]}")'" --icon="$icon" --terminal=false --categories='Emulator;Game' --startupnotify=false --pkgname="$chse"
chmod 755 "$chse".desktop
fi
else
echo -e "Invalid desktop location: $desk\nPlease edit the script"
fi
;;
-xv|--xver)
# cross-function wine version
xnint
xnexe
xnenv
xcmd+=("wine" "--version")
("${xcmd[@]}" &)
;;
-h|--help)
echo -e "\n General usage: netris-proton -w? args\n -w? options for wine and -p? for proton.\n Type wstart by itself for command list.\n\n Edit script path variables as needed.\n bash, find, gendesk, grep, readpe,\n strings, winetricks, wrestool,\n pcre2grep, peres, perl needed by\n certain items.\n"
;;
-*|\*|*)
# do_usage
usage "invalid option $1"
exit 1
;;
esac
fi

View File

@@ -1,11 +0,0 @@
#!/bin/bash
#TODO: Fix the warp-input startup problem
if [ -z "$SESSION_ID" ]; then
echo "Error: SESSION_ID environment variable is not set."
exit 1
fi
#Open udp port to listed for QUIC events on
export PORT=${PORT:-"8080"}
sudo -E /usr/bin/supervisord -c /etc/supervisord.conf

View File

@@ -1,42 +0,0 @@
[supervisord]
user=nestri
nodaemon=true
loglevel=info
logfile=/tmp/supervisord.log
pidfile=/tmp/supervisord.pid
[program:entrypoint]
command=/etc/entrypoint.sh
user=nestri
logfile=/tmp/entrypoint.log
pidfile=/tmp/entrypoint.pid
stopsignal=INT
autostart=true
autorestart=true
redirect_stderr=true
priority=1
[program:pulseaudio]
user=nestri
command=bash -c "until [ -S \"/tmp/.X11-unix/X${DISPLAY/:/}\" ]; do sleep 1; done; sudo /usr/bin/pulseaudio -k >/dev/null 2>&1 || sudo /usr/bin/pulseaudio --system --verbose --log-target=stderr --realtime=true --disallow-exit -L 'module-native-protocol-tcp auth-ip-acl=127.0.0.0/8 port=4713 auth-anonymous=1'"
environment=DISPLAY=":0"
logfile=/tmp/pulseaudio.log
pidfile=/tmp/pulseaudio.pid
stopsignal=INT
autostart=true
autorestart=true
redirect_stderr=true
priority=10
[program:warp-input]
command=bash -c "until [ -S \"/tmp/.X11-unix/X${DISPLAY/:/}\" ]; do sleep 1; done; /usr/bin/warp-input --namespace $SESSION_ID --bind '[::]:8080' https://fst.so:4443"
logfile=/tmp/warp-input.log
pidfile=/tmp/warp-input.pid
stopsignal=INT
user=nestri
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY=":0",SESSION_ID="%(ENV_SESSION_ID)s"
autostart=true
autorestart=true
startretries=100
redirect_stderr=true
priority=20

View File

@@ -1,3 +0,0 @@
{
"files.eol": "\n"
}

View File

@@ -629,8 +629,8 @@ 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.
Netris: The open-source cloud gaming platform for friends
Copyright (C) 2024 Wanjohi Ryan
Nestri: Your games, Your rules
Copyright (C) 2023 WanjohiRyan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published

190
README.md
View File

@@ -1,39 +1,24 @@
<div align="center">
<div>
<a href="https://nestri.io" >
<img height="160" src="/assets/logo.png">
</a>
</div>
&nbsp;
&nbsp;
<div align="center" >
<div align="center">
<h1>
<a href="https://nestri.io" >
<picture>
<source media="(prefers-color-scheme: dark)" srcset="/assets/logo-name-white.png">
<source media="(prefers-color-scheme: light)" srcset="/assets/logo-name-black.png">
<img alt="nestri logo name" src="/assets/logo-name-black.png">
</picture>
<img src="/apps/www/public/images/next.png" alt="Nestri - What will you play next?">
</a>
</h1>
</div>
&nbsp;
&nbsp;
An open-source cloud gaming platform built for you to play together with your friends. <br/> A GeForce NOW alternative that can be self-hosted.<br/>
</div>
&nbsp;
&nbsp;
<div align="center" >
Nestri is an open-source, self-hosted cloud gaming platform that allows you to play games in your web browser with friends. <strong>Built and shaped by our gaming community.</strong>
<br/>
<br/>
</div>
<div align="center">
[![][github-release-shield]][github-release-link]
[![][discord-shield]][discord-link]
@@ -49,158 +34,21 @@ An open-source cloud gaming platform built for you to play together with your fr
&nbsp;
&nbsp;
<div align="center" >
[![][image-overview]][website-link]
</div>
## Features 🌟
As Nestri navigates through its _experimental_ phase, we're in the process of crafting and testing a variety of features for you:
1. **🎮 Proton-GE Compatibility:** We utilize Proton-GE for running games, offering extensive game compatibility and ensuring you can play a wide variety of titles.
2. **⚡ QUIC Protocol:** For input and video/audio transmission, we use QUIC via [MoQ][moq-github-url] to significantly reduce latency on variable networks, ensuring crisp visuals without sacrificing data or increasing latency.
3. **🔗 Session IDs:** With a `SESSION_ID`, we seamlessly tie your game progress, achievements, and devices without the need for logging into [nestri.io][website-link]. Our public CDNs, currently located in Europe (eu-north-1) and the US (us-east-1), further reduce latency for friends playing together from afar. More locations are on the way!
4. **🔄 Automatic Game Progress Sync:** Like Stadia's state share, we automatically sync your game progress based on the *.exe file you're running, allowing you to share your progress with friends via a link.
5. **👫 Co-op Gameplay:** Play co-op with up to 8 people, or play single player by sharing mouse and keyboard inputs seamlessly (similar to [neko][neko-url]).
6. **🌐 Cross-Platform Play:** Our platform is accessible on any device that can run a Chrome-based browser, including Edge, Chrome, Brave, and Arc. And you do not have to set up anything, it's all done and maintained for you.
7. **📊 Bandwidth Optimization:** Experience hardware-accelerated `VMAF` for optimized bandwidth, ensuring the best possible video quality. [Learn More][vmaf-cuda-link]
8. **🌟 ...and more:** Stay tuned as we continue to add features _sometimes inspired by platforms like Stadia_, to give you the best and most customizable gaming experience.
This platform is in an _experimental_ phase, and we're actively working on adding new features. Your feedback and support is very much appreciated.
> \[!IMPORTANT]
>
> If you're excited about what we're doing and want to support our journey, consider giving us a star ⭐ on our repository. Your support fuels our progress!. \~ ✨
> More info coming soon!
[![][image-star]][github-stars-link]
## Getting Started 🎮
Whether you're looking to self-host Nestri or simply want to try it out without the need for your own GPU, we've got you covered. Choose the path that best suits your needs:
<!-- _You can always change your option later without losing game progress_ -->
| If you don't have a Nvidia GPU or prefer not to self-host, you can visit our website. No installation or set up required ! <br/> This is the perfect option for gamers looking to dive straight into the action without any setup. | [👉🏽 Get Access][website-link] |
| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------- |
| If you're interested in self-hosting Nestri, continue reading for detailed instructions on how to get started. <br/> This option is ideal if you have your own Nvidia GPU and are comfortable with setting up and managing your own server. | [🛠️ Self Host Nestri](#self-hosting) |
> \[!TIP]
>
> Remember, flexibility is key with Nestri. You're free to switch between self-hosting and using `nestri.io` whenever you like, without losing your game progress. \~ 💡
<a name="self-hosting"></a>
### Self-Hosting Nestri 🔨
For those interested in self-hosting, here are is what you need to get your own Nestri server up and running:
- **Nvidia GPU**: Unfortunately, this setup is exclusive to Nvidia GPUs. If you don't own one, consider renting from cloud services like AWS, GCP, or Vast.ai. We highly recommend this approach.
- **CUDA**: For GPU acceleration, CUDA version `12.0` or newer is required. Verify your CUDA installation by running `nvcc --version`.
- **Docker**: Ensure you have `docker` and `nvidia-docker` are up to date to avoid compatibility issues with CUDA. You can check your Docker version by running `docker --version` in your terminal.
- **GPU Driver**: Ensure your GPU drivers are up to date to avoid compatibility issues with CUDA. Nvidia driver version `520.56.06` or newer is required.
- **Xorg Display**: Your Nvidia GPU should not be attached to a running X display server. You can confirm this by running `nvidia-smi`.
- **Nvidia-DRM**: Make sure that the `nvidia-drm` module has been loaded and that the module is loaded with the flag `modeset=1`. Confirm this by running `sudo cat /sys/module/nvidia_drm/parameters/modeset`
> \[!TIP]
>
> Typically, if your setup meets the necessary CUDA requirements, the `nvidia-drm` module will already be loaded, particularly in AWS G4dn instances. \~ 💡
### Step-by-Step Guide
Follow these steps to get Nestri up and running on your system.
> \[!IMPORTANT]
>
> This is our pilot, there is a lot we haven't figured out yet. Please file an issue if anything comes up. \~ 🫂
> \[!TIP]
>
> The setup process will become much simpler with the launch of our CLI tool, so stay tuned for that! In the meantime, you'll need to follow these manual steps.
#### Step 1: Navigate to Your Game Directory
First, change your directory to the location of your `.exe` file. For Steam games, this typically means:
```bash
cd $HOME/.steam/steam/steamapps
ls -la .
```
#### Step 2: Generate a Session ID
Create a unique session ID using the following command:
```bash
head /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | head -c 16
```
This command generates a random 16-character string. Be sure to note this string carefully, as you'll need it for the next step.
#### Step 3: Launch the Nestri Server
With your SESSION_ID ready, insert it into the command below, replacing `<copy here>` with your actual session ID. Then, run the command to start the Nestri server:
```
docker run --gpus all --device=/dev/dri --name nestri -it --entrypoint /bin/bash -e SESSION_ID=<copy here> -v "$(pwd)":/game -p 8080:8080/udp --cap-add=SYS_NICE --cap-add=SYS_ADMIN ghcr.io/nestriness/nestri/server:nightly
```
> \[!TIP]
>
> Ensure UDP port 8080 is accessible from the internet. Use `ufw allow 8080/udp` or adjust your cloud provider's security group settings accordingly.
#### Step 4: Configure the Game within the Container
After executing the previous command, you'll be in a new shell within the container (example: `nestri@3f199ee68c01:~$`). Perform the following checks:
1. Verify the game is mounted by executing `ls -la /game`. If not, exit and ensure you've correctly mounted the game directory as a volume.
2. Then, start the Netris server by running `/etc/startup.sh > /dev/null &`.
#### Step 5: Running Your Game
Wait for the `.X11-unix` directory to appear in `/tmp` (check with `ls -la /tmp`). Once it appears, you're ready to launch your game.
- With Proton-GE: `nestri-proton -pr <game>.exe`
- With Wine: `nestri-proton -wr <game>.exe`
#### Step 6: Begin Playing
Finally, construct the play URL with your session ID:
```
echo "https://nestri.io/play/$SESSION_ID"
```
Navigate to this URL in your browser, click on the page to capture your mouse pointer, and start playing!
[github-release-link]: https://github.com/wanjohiryan/netris/releases
[github-release-shield]: https://img.shields.io/github/v/release/wanjohiryan/netris?color=369eff&labelColor=black&logo=github&style=flat-square
[github-release-link]: https://github.com/nestriness/nestri/releases
[github-release-shield]: https://img.shields.io/github/v/release/nestriness/nestri?color=369eff&labelColor=black&logo=github&style=flat-square
[discord-shield]: https://img.shields.io/discord/1080111004698021909?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square
[discord-link]: https://discord.com/invite/Y6etn3qKZ3
[github-license-shield]: https://img.shields.io/github/license/wanjohiryan/netris?color=white&labelColor=black&style=flat-square
[github-license-link]: https://github.com/wanjohiryan/netris/blob/main/LICENSE
[github-stars-shield]: https://img.shields.io/github/stars/wanjohiryan/netris?color=ffcb47&labelColor=black&style=flat-square
[github-stars-link]: https://github.com/wanjohiryan/netris/network/stargazers
[github-license-shield]: https://img.shields.io/github/license/nestriness/nestri?color=white&labelColor=black&style=flat-square
[github-license-link]: https://github.com/nestriness/nestri/blob/main/LICENSE
[github-stars-shield]: https://img.shields.io/github/stars/nestriness/nestri?color=ffcb47&labelColor=black&style=flat-square
[github-stars-link]: https://github.com/nestriness/nestri/network/stargazers
[share-x-shield]: https://img.shields.io/badge/-share%20on%20x-black?labelColor=black&logo=x&logoColor=white&style=flat-square
[share-x-link]: https://twitter.com/intent/tweet?text=Hey%2C%20check%20out%20this%20Github%20repository.%20It%20is%20an%20open-source%20self-hosted%20Geforce%20Now%20alternative.&url=https%3A%2F%2Fgithub.com%2Fwanjohiryan%2Fnetris
[share-x-link]: https://twitter.com/intent/tweet?text=Hey%2C%20check%20out%20this%20Github%20repository.%20It%20is%20an%20open-source%20self-hosted%20Geforce%20Now%20alternative.&url=https%3A%2F%2Fgithub.com%2Fnestriness%2Fnestri
[share-reddit-shield]: https://img.shields.io/badge/-share%20on%20reddit-black?labelColor=black&logo=reddit&logoColor=white&style=flat-square
[share-reddit-link]: https://www.reddit.com/submit?title=Hey%2C%20check%20out%20this%20Github%20repository.%20It%20is%20an%20open-source%20self-hosted%20Geforce%20Now%20alternative.&url=https%3A%2F%2Fgithub.com%2Fwanjohiryan%2Fnetris
[share-reddit-link]: https://www.reddit.com/submit?title=Hey%2C%20check%20out%20this%20Github%20repository.%20It%20is%20an%20open-source%20self-hosted%20Geforce%20Now%20alternative.&url=https%3A%2F%2Fgithub.com%2Fnestriness%2Fnestri
[image-overview]: assets/banner.png
[website-link]: https://nestri.io
[neko-url]: https://github.com/m1k1o/neko

9
apps/docs/.eslintrc.js Normal file
View File

@@ -0,0 +1,9 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
root: true,
extends: ["@nestri/eslint-config/next.js"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,
},
};

36
apps/docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# env files (can opt-in for commiting if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

36
apps/docs/README.md Normal file
View File

@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

BIN
apps/docs/app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Binary file not shown.

39
apps/docs/app/globals.css Normal file
View File

@@ -0,0 +1,39 @@
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
}
body {
color: var(--foreground);
background: var(--background);
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
}

31
apps/docs/app/layout.tsx Normal file
View File

@@ -0,0 +1,31 @@
import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css";
const geistSans = localFont({
src: "./fonts/GeistVF.woff",
variable: "--font-geist-sans",
});
const geistMono = localFont({
src: "./fonts/GeistMonoVF.woff",
variable: "--font-geist-mono",
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable}`}>
{children}
</body>
</html>
);
}

View File

@@ -0,0 +1,188 @@
.page {
--gray-rgb: 0, 0, 0;
--gray-alpha-200: rgba(var(--gray-rgb), 0.08);
--gray-alpha-100: rgba(var(--gray-rgb), 0.05);
--button-primary-hover: #383838;
--button-secondary-hover: #f2f2f2;
display: grid;
grid-template-rows: 20px 1fr 20px;
align-items: center;
justify-items: center;
min-height: 100svh;
padding: 80px;
gap: 64px;
font-synthesis: none;
}
@media (prefers-color-scheme: dark) {
.page {
--gray-rgb: 255, 255, 255;
--gray-alpha-200: rgba(var(--gray-rgb), 0.145);
--gray-alpha-100: rgba(var(--gray-rgb), 0.06);
--button-primary-hover: #ccc;
--button-secondary-hover: #1a1a1a;
}
}
.main {
display: flex;
flex-direction: column;
gap: 32px;
grid-row-start: 2;
}
.main ol {
font-family: var(--font-geist-mono);
padding-left: 0;
margin: 0;
font-size: 14px;
line-height: 24px;
letter-spacing: -0.01em;
list-style-position: inside;
}
.main li:not(:last-of-type) {
margin-bottom: 8px;
}
.main code {
font-family: inherit;
background: var(--gray-alpha-100);
padding: 2px 4px;
border-radius: 4px;
font-weight: 600;
}
.ctas {
display: flex;
gap: 16px;
}
.ctas a {
appearance: none;
border-radius: 128px;
height: 48px;
padding: 0 20px;
border: none;
font-family: var(--font-geist-sans);
border: 1px solid transparent;
transition: background 0.2s, color 0.2s, border-color 0.2s;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
line-height: 20px;
font-weight: 500;
}
a.primary {
background: var(--foreground);
color: var(--background);
gap: 8px;
}
a.secondary {
border-color: var(--gray-alpha-200);
min-width: 180px;
}
button.secondary {
appearance: none;
border-radius: 128px;
height: 48px;
padding: 0 20px;
border: none;
font-family: var(--font-geist-sans);
border: 1px solid transparent;
transition: background 0.2s, color 0.2s, border-color 0.2s;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
line-height: 20px;
font-weight: 500;
background: transparent;
border-color: var(--gray-alpha-200);
min-width: 180px;
}
.footer {
font-family: var(--font-geist-sans);
grid-row-start: 3;
display: flex;
gap: 24px;
}
.footer a {
display: flex;
align-items: center;
gap: 8px;
}
.footer img {
flex-shrink: 0;
}
/* Enable hover only on non-touch devices */
@media (hover: hover) and (pointer: fine) {
a.primary:hover {
background: var(--button-primary-hover);
border-color: transparent;
}
a.secondary:hover {
background: var(--button-secondary-hover);
border-color: transparent;
}
.footer a:hover {
text-decoration: underline;
text-underline-offset: 4px;
}
}
@media (max-width: 600px) {
.page {
padding: 32px;
padding-bottom: 80px;
}
.main {
align-items: center;
}
.main ol {
text-align: center;
}
.ctas {
flex-direction: column;
}
.ctas a {
font-size: 14px;
height: 40px;
padding: 0 16px;
}
a.secondary {
min-width: auto;
}
.footer {
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
}
@media (prefers-color-scheme: dark) {
.logo {
filter: invert();
}
}

99
apps/docs/app/page.tsx Normal file
View File

@@ -0,0 +1,99 @@
import Image from "next/image";
import { Button } from "@nestri/ui/button";
import styles from "./page.module.css";
export default function Home() {
return (
<div className={styles.page}>
<main className={styles.main}>
<Image
className={styles.logo}
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol>
<li>
Get started by editing <code>app/page.tsx</code>
</li>
<li>Save and see your changes instantly.</li>
</ol>
<div className={styles.ctas}>
<a
className={styles.primary}
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className={styles.logo}
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
className={styles.secondary}
>
Read our docs
</a>
</div>
<Button appName="docs" className={styles.secondary}>
Open alert
</Button>
</main>
<footer className={styles.footer}>
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file-text.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</div>
);
}

View File

@@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;

27
apps/docs/package.json Normal file
View File

@@ -0,0 +1,27 @@
{
"name": "@nestri/docs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbo --port 3001",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@nestri/ui": "*",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522",
"next": "15.0.0-rc.0"
},
"devDependencies": {
"@nestri/eslint-config": "*",
"@nestri/typescript-config": "*",
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "15.0.0-rc.0"
}
}

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.5 13.5V6.5V5.41421C14.5 5.149 14.3946 4.89464 14.2071 4.70711L9.79289 0.292893C9.60536 0.105357 9.351 0 9.08579 0H8H3H1.5V1.5V13.5C1.5 14.8807 2.61929 16 4 16H12C13.3807 16 14.5 14.8807 14.5 13.5ZM13 13.5V6.5H9.5H8V5V1.5H3V13.5C3 14.0523 3.44772 14.5 4 14.5H12C12.5523 14.5 13 14.0523 13 13.5ZM9.5 5V2.12132L12.3787 5H9.5ZM5.13 5.00062H4.505V6.25062H5.13H6H6.625V5.00062H6H5.13ZM4.505 8H5.13H11H11.625V9.25H11H5.13H4.505V8ZM5.13 11H4.505V12.25H5.13H11H11.625V11H11H5.13Z" fill="#666666"/>
</svg>

After

Width:  |  Height:  |  Size: 645 B

View File

@@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_868_525)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.268 14.0934C11.9051 13.4838 13.2303 12.2333 13.9384 10.6469C13.1192 10.7941 12.2138 10.9111 11.2469 10.9925C11.0336 12.2005 10.695 13.2621 10.268 14.0934ZM8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM8.48347 14.4823C8.32384 14.494 8.16262 14.5 8 14.5C7.83738 14.5 7.67616 14.494 7.51654 14.4823C7.5132 14.4791 7.50984 14.4759 7.50647 14.4726C7.2415 14.2165 6.94578 13.7854 6.67032 13.1558C6.41594 12.5744 6.19979 11.8714 6.04101 11.0778C6.67605 11.1088 7.33104 11.125 8 11.125C8.66896 11.125 9.32395 11.1088 9.95899 11.0778C9.80021 11.8714 9.58406 12.5744 9.32968 13.1558C9.05422 13.7854 8.7585 14.2165 8.49353 14.4726C8.49016 14.4759 8.4868 14.4791 8.48347 14.4823ZM11.4187 9.72246C12.5137 9.62096 13.5116 9.47245 14.3724 9.28806C14.4561 8.87172 14.5 8.44099 14.5 8C14.5 7.55901 14.4561 7.12828 14.3724 6.71194C13.5116 6.52755 12.5137 6.37904 11.4187 6.27753C11.4719 6.83232 11.5 7.40867 11.5 8C11.5 8.59133 11.4719 9.16768 11.4187 9.72246ZM10.1525 6.18401C10.2157 6.75982 10.25 7.36805 10.25 8C10.25 8.63195 10.2157 9.24018 10.1525 9.81598C9.46123 9.85455 8.7409 9.875 8 9.875C7.25909 9.875 6.53877 9.85455 5.84749 9.81598C5.7843 9.24018 5.75 8.63195 5.75 8C5.75 7.36805 5.7843 6.75982 5.84749 6.18401C6.53877 6.14545 7.25909 6.125 8 6.125C8.74091 6.125 9.46123 6.14545 10.1525 6.18401ZM11.2469 5.00748C12.2138 5.08891 13.1191 5.20593 13.9384 5.35306C13.2303 3.7667 11.9051 2.51622 10.268 1.90662C10.695 2.73788 11.0336 3.79953 11.2469 5.00748ZM8.48347 1.51771C8.4868 1.52089 8.49016 1.52411 8.49353 1.52737C8.7585 1.78353 9.05422 2.21456 9.32968 2.84417C9.58406 3.42562 9.80021 4.12856 9.95899 4.92219C9.32395 4.89118 8.66896 4.875 8 4.875C7.33104 4.875 6.67605 4.89118 6.04101 4.92219C6.19978 4.12856 6.41594 3.42562 6.67032 2.84417C6.94578 2.21456 7.2415 1.78353 7.50647 1.52737C7.50984 1.52411 7.51319 1.52089 7.51653 1.51771C7.67615 1.50597 7.83738 1.5 8 1.5C8.16262 1.5 8.32384 1.50597 8.48347 1.51771ZM5.73202 1.90663C4.0949 2.51622 2.76975 3.7667 2.06159 5.35306C2.88085 5.20593 3.78617 5.08891 4.75309 5.00748C4.96639 3.79953 5.30497 2.73788 5.73202 1.90663ZM4.58133 6.27753C3.48633 6.37904 2.48837 6.52755 1.62761 6.71194C1.54392 7.12828 1.5 7.55901 1.5 8C1.5 8.44099 1.54392 8.87172 1.62761 9.28806C2.48837 9.47245 3.48633 9.62096 4.58133 9.72246C4.52807 9.16768 4.5 8.59133 4.5 8C4.5 7.40867 4.52807 6.83232 4.58133 6.27753ZM4.75309 10.9925C3.78617 10.9111 2.88085 10.7941 2.06159 10.6469C2.76975 12.2333 4.0949 13.4838 5.73202 14.0934C5.30497 13.2621 4.96639 12.2005 4.75309 10.9925Z" fill="#666666"/>
</g>
<defs>
<clipPath id="clip0_868_525">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,10 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_977_547)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 3L18.5 17H2.5L10.5 3Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_977_547">
<rect width="16" height="16" fill="white" transform="translate(2.5 2)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 367 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5H14.5V12.5C14.5 13.0523 14.0523 13.5 13.5 13.5H2.5C1.94772 13.5 1.5 13.0523 1.5 12.5V2.5ZM0 1H1.5H14.5H16V2.5V12.5C16 13.8807 14.8807 15 13.5 15H2.5C1.11929 15 0 13.8807 0 12.5V2.5V1ZM3.75 5.5C4.16421 5.5 4.5 5.16421 4.5 4.75C4.5 4.33579 4.16421 4 3.75 4C3.33579 4 3 4.33579 3 4.75C3 5.16421 3.33579 5.5 3.75 5.5ZM7 4.75C7 5.16421 6.66421 5.5 6.25 5.5C5.83579 5.5 5.5 5.16421 5.5 4.75C5.5 4.33579 5.83579 4 6.25 4C6.66421 4 7 4.33579 7 4.75ZM8.75 5.5C9.16421 5.5 9.5 5.16421 9.5 4.75C9.5 4.33579 9.16421 4 8.75 4C8.33579 4 8 4.33579 8 4.75C8 5.16421 8.33579 5.5 8.75 5.5Z" fill="#666666"/>
</svg>

After

Width:  |  Height:  |  Size: 750 B

18
apps/docs/tsconfig.json Normal file
View File

@@ -0,0 +1,18 @@
{
"extends": "@nestri/typescript-config/nextjs.json",
"compilerOptions": {
"plugins": [
{
"name": "next"
}
]
},
"include": [
"next-env.d.ts",
"next.config.mjs",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": ["node_modules"]
}

View File

@@ -1,46 +0,0 @@
{
"name": "qwik-project-name",
"scripts": {
"build": "qwik build",
"build.client": "vite build",
"build.preview": "vite build --ssr src/entry.preview.tsx",
"build.types": "tsc --incremental --noEmit",
"dev": "vite --mode ssr",
"dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
"fmt": "prettier --write .",
"fmt.check": "prettier --check .",
"lint": "eslint \"src/**/*.ts*\"",
"preview": "qwik build preview && vite preview --open",
"start": "vite --open --mode ssr",
"deploy": "echo 'Run \"npm run qwik add\" to install a server adapter'",
"qwik": "qwik"
},
"devDependencies": {
"@builder.io/qwik": "^1.5.1",
"@builder.io/qwik-city": "^1.5.1",
"@types/eslint": "^8.56.5",
"@types/node": "^20.11.24",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"eslint": "^8.57.0",
"eslint-plugin-qwik": "^1.5.1",
"prettier": "^3.2.5",
"typescript": "5.3.3",
"undici": "*",
"vite": "^5.1.4",
"vite-tsconfig-paths": "^4.2.1"
},
"trustedDependencies": [
"sharp"
],
"trustedDependencies-annotation": "Needed for bun to allow running install scripts",
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"npm": ">=10.0.0",
"pnpm": ">=8.0.0",
"yarn": ">=3.0.0"
},
"engines-annotation": "Mostly required by sharp which needs a Node-API v9 compatible runtime",
"private": true,
"type": "module"
}

View File

@@ -35,7 +35,7 @@ module.exports = {
"prefer-spread": "off",
"no-case-declarations": "off",
"no-console": "off",
"@typescript-eslint/no-unused-vars": ["warn"],
"@typescript-eslint/no-unused-vars": ["error"],
"@typescript-eslint/consistent-type-imports": "warn",
"@typescript-eslint/no-unnecessary-condition": "warn",
},

3
apps/www/.gitignore vendored
View File

@@ -39,6 +39,3 @@ lerna-debug.log*
# Yarn
.yarn/*
!.yarn/releases
# Vercel
.vercel

View File

@@ -1,3 +0,0 @@
export default {
plugins: ['prettier-plugin-tailwindcss'],
}

View File

@@ -1,8 +1,8 @@
# Qwik City App ⚡️
- [Qwik Docs](https://qwik.builder.io/)
- [Discord](https://qwik.builder.io/chat)
- [Qwik GitHub](https://github.com/BuilderIO/qwik)
- [Qwik Docs](https://qwik.dev/)
- [Discord](https://qwik.dev/chat)
- [Qwik GitHub](https://github.com/QwikDev/qwik)
- [@QwikDev](https://twitter.com/QwikDev)
- [Vite](https://vitejs.dev/)
@@ -10,7 +10,7 @@
## Project Structure
This project is using Qwik with [QwikCity](https://qwik.builder.io/qwikcity/overview/). QwikCity is just an extra set of tools on top of Qwik to make it easier to build a full site, including directory-based routing, layouts, and more.
This project is using Qwik with [QwikCity](https://qwik.dev/qwikcity/overview/). QwikCity is just an extra set of tools on top of Qwik to make it easier to build a full site, including directory-based routing, layouts, and more.
Inside your project, you'll see the following directory structure:
@@ -24,7 +24,7 @@ Inside your project, you'll see the following directory structure:
└── ...
```
- `src/routes`: Provides the directory-based routing, which can include a hierarchy of `layout.tsx` layout files, and an `index.tsx` file as the page. Additionally, `index.ts` files are endpoints. Please see the [routing docs](https://qwik.builder.io/qwikcity/routing/overview/) for more info.
- `src/routes`: Provides the directory-based routing, which can include a hierarchy of `layout.tsx` layout files, and an `index.tsx` file as the page. Additionally, `index.ts` files are endpoints. Please see the [routing docs](https://qwik.dev/qwikcity/routing/overview/) for more info.
- `src/components`: Recommended directory for components.
@@ -32,10 +32,10 @@ Inside your project, you'll see the following directory structure:
## Add Integrations and deployment
Use the `pnpm qwik add` command to add additional integrations. Some examples of integrations includes: Cloudflare, Netlify or Express Server, and the [Static Site Generator (SSG)](https://qwik.builder.io/qwikcity/guides/static-site-generation/).
Use the `bun qwik add` command to add additional integrations. Some examples of integrations includes: Cloudflare, Netlify or Express Server, and the [Static Site Generator (SSG)](https://qwik.dev/qwikcity/guides/static-site-generation/).
```shell
pnpm qwik add # or `pnpm qwik add`
bun qwik add # or `bun qwik add`
```
## Development
@@ -43,7 +43,7 @@ pnpm qwik add # or `pnpm qwik add`
Development mode uses [Vite's development server](https://vitejs.dev/). The `dev` command will server-side render (SSR) the output during development.
```shell
npm start # or `pnpm start`
npm start # or `bun start`
```
> Note: during dev mode, Vite may request a significant number of `.js` files. This does not represent a Qwik production build.
@@ -53,7 +53,7 @@ npm start # or `pnpm start`
The preview command will create a production build of the client modules, a production build of `src/entry.preview.tsx`, and run a local server. The preview server is only for convenience to preview a production build locally and should not be used as a production server.
```shell
pnpm preview # or `pnpm preview`
bun preview # or `bun preview`
```
## Production
@@ -61,49 +61,5 @@ pnpm preview # or `pnpm preview`
The production build will generate client and server modules by running both client and server build commands. The build command will use Typescript to run a type check on the source code.
```shell
pnpm build # or `pnpm build`
bun build # or `bun build`
```
## Vercel Edge
This starter site is configured to deploy to [Vercel Edge Functions](https://vercel.com/docs/concepts/functions/edge-functions), which means it will be rendered at an edge location near to your users.
## Installation
The adaptor will add a new `vite.config.ts` within the `adapters/` directory, and a new entry file will be created, such as:
```
└── adapters/
└── vercel-edge/
└── vite.config.ts
└── src/
└── entry.vercel-edge.tsx
```
Additionally, within the `package.json`, the `build.server` script will be updated with the Vercel Edge build.
## Production build
To build the application for production, use the `build` command, this command will automatically run `pnpm build.server` and `pnpm build.client`:
```shell
pnpm build
```
[Read the full guide here](https://github.com/BuilderIO/qwik/blob/main/starters/adapters/vercel-edge/README.md)
## Dev deploy
To deploy the application for development:
```shell
pnpm deploy
```
Notice that you might need a [Vercel account](https://docs.Vercel.com/get-started/) in order to complete this step!
## Production deploy
The project is ready to be deployed to Vercel. However, you will need to create a git repository and push the code to it.
You can [deploy your site to Vercel](https://vercel.com/docs/concepts/deployments/overview) either via a Git provider integration or through the Vercel CLI.

View File

@@ -1,16 +0,0 @@
import { vercelEdgeAdapter } from "@builder.io/qwik-city/adapters/vercel-edge/vite";
import { extendConfig } from "@builder.io/qwik-city/vite";
import baseConfig from "../../vite.config";
export default extendConfig(baseConfig, () => {
return {
build: {
ssr: true,
rollupOptions: {
input: ["src/entry.vercel-edge.tsx", "@qwik-city-plan"],
},
outDir: ".vercel/output/functions/_qwik-city.func",
},
plugins: [vercelEdgeAdapter()],
};
});

View File

@@ -1,6 +1,6 @@
{
"name": "my-qwik-empty-starter",
"description": "App with Routing built-in ready to create your app",
"name": "@nestri/www",
"description": "Website for Nestri",
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
@@ -15,9 +15,8 @@
"build": "qwik build",
"build.client": "vite build",
"build.preview": "vite build --ssr src/entry.preview.tsx",
"build.server": "vite build -c adapters/vercel-edge/vite.config.ts",
"build.types": "tsc --incremental --noEmit",
"deploy": "vercel deploy",
"deploy": "echo 'Run \"npm run qwik add\" to install a server adapter'",
"dev": "vite --mode ssr",
"dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
"fmt": "prettier --write .",
@@ -28,31 +27,18 @@
"qwik": "qwik"
},
"devDependencies": {
"@builder.io/qwik": "^1.5.1",
"@builder.io/qwik-city": "^1.5.1",
"@builder.io/qwik-react": "0.5.0",
"@types/eslint": "^8.56.5",
"@types/node": "^20.11.24",
"@types/react": "^18.2.28",
"@types/react-dom": "^18.2.13",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"autoprefixer": "^10.4.14",
"eslint": "^8.57.0",
"eslint-plugin-qwik": "^1.5.1",
"postcss": "^8.4.31",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.3",
"typescript": "5.3.3",
"@builder.io/qwik": "^1.8.0",
"@builder.io/qwik-city": "^1.8.0",
"@types/eslint": "8.56.10",
"@types/node": "20.14.11",
"@typescript-eslint/eslint-plugin": "7.16.1",
"@typescript-eslint/parser": "7.16.1",
"eslint": "8.57.0",
"eslint-plugin-qwik": "^1.8.0",
"prettier": "3.3.3",
"typescript": "5.4.5",
"undici": "*",
"vercel": "^29.1.1",
"vite": "^5.1.4",
"vite": "5.3.5",
"vite-tsconfig-paths": "^4.2.1"
},
"dependencies": {
"@fontsource/geist-sans": "^5.0.2"
}
}

9347
apps/www/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 500 500"><g clip-path="url(#a)"><circle cx="250" cy="250" r="250" fill="#fff"/><path fill="#18B6F6" d="m367.87 418.45-61.17-61.18-.94.13v-.67L175.7 227.53l32.05-31.13L188.9 87.73 99.56 199.09c-15.22 15.42-18.03 40.51-7.08 59.03l55.83 93.11a46.82 46.82 0 0 0 40.73 22.81l27.65-.27 151.18 44.68Z"/><path fill="#AC7EF4" d="m401.25 196.94-12.29-22.81-6.41-11.67-2.54-4.56-.26.26-33.66-58.63a47.07 47.07 0 0 0-41.27-23.75l-29.51.8-88.01.28a47.07 47.07 0 0 0-40.33 23.34L93.4 207l95.76-119.54L314.7 226.19l-22.3 22.67 13.35 108.54.13-.26v.26h-.26l.26.27 10.42 10.2 50.62 49.78c2.13 2 5.6-.4 4.13-2.96l-31.25-61.85 54.5-101.3 1.73-2c.67-.81 1.33-1.62 1.87-2.42a46.8 46.8 0 0 0 3.34-50.18Z"/><path fill="#fff" d="M315.1 225.65 189.18 87.6l17.9 108.14L175 227l130.5 130.27-11.75-108.14 21.37-23.48Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h500v500H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

4
apps/www/qwik.env.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
// This file can be used to add references for global types like `vite/client`.
// Add global `vite/client` types. For more info, see: https://vitejs.dev/guide/features#client-types
/// <reference types="vite/client" />

View File

@@ -14,7 +14,7 @@ export const RouterHead = component$(() => {
<link rel="canonical" href={loc.url.href} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="/favicon.png" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
{head.meta.map((m) => (
<meta key={m.key} {...m} />
@@ -25,7 +25,23 @@ export const RouterHead = component$(() => {
))}
{head.styles.map((s) => (
<style key={s.key} {...s.props} dangerouslySetInnerHTML={s.style} />
<style
key={s.key}
{...s.props}
{...(s.props?.dangerouslySetInnerHTML
? {}
: { dangerouslySetInnerHTML: s.style })}
/>
))}
{head.scripts.map((s) => (
<script
key={s.key}
{...s.props}
{...(s.props?.dangerouslySetInnerHTML
? {}
: { dangerouslySetInnerHTML: s.script })}
/>
))}
</>
);

View File

@@ -12,6 +12,7 @@
*/
import { createQwikCity } from "@builder.io/qwik-city/middleware/node";
import qwikCityPlan from "@qwik-city-plan";
// make sure qwikCityPlan is imported before entry
import render from "./entry.ssr";
/**

View File

@@ -26,5 +26,8 @@ export default function (opts: RenderToStreamOptions) {
lang: "en-us",
...opts.containerAttributes,
},
serverData: {
...opts.serverData,
},
});
}

View File

@@ -1,22 +0,0 @@
/*
* WHAT IS THIS FILE?
*
* It's the entry point for Vercel Edge when building for production.
*
* Learn more about the Vercel Edge integration here:
* - https://qwik.builder.io/docs/deployments/vercel-edge/
*
*/
import {
createQwikCity,
type PlatformVercel,
} from "@builder.io/qwik-city/middleware/vercel-edge";
import qwikCityPlan from "@qwik-city-plan";
import { manifest } from "@qwik-client-manifest";
import render from "./entry.ssr";
declare global {
interface QwikCityPlatform extends PlatformVercel {}
}
export default createQwikCity({ render, qwikCityPlan, manifest });

View File

@@ -1,15 +0,0 @@
/**
* Tailwind CSS imports
* View the full documentation at https://tailwindcss.com
*/
@tailwind base;
@tailwind components;
@tailwind utilities;
html *,
html *::after,
html *::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}

View File

@@ -5,9 +5,9 @@ import {
ServiceWorkerRegister,
} from "@builder.io/qwik-city";
import { RouterHead } from "./components/router-head/router-head";
import { isDev } from "@builder.io/qwik/build";
import "./global.css";
import "@fontsource/geist-sans/400.css";
export default component$(() => {
/**
@@ -20,13 +20,18 @@ export default component$(() => {
return (
<QwikCityProvider>
<head>
<meta charSet="utf-8" />
<link rel="manifest" href="/manifest.json" />
<meta charset="utf-8" />
{!isDev && (
<link
rel="manifest"
href={`${import.meta.env.BASE_URL}manifest.json`}
/>
)}
<RouterHead />
</head>
<body lang="en" class="min-h-screen font-sans antialiased">
<body lang="en">
<RouterOutlet />
<ServiceWorkerRegister />
{!isDev && <ServiceWorkerRegister />}
</body>
</QwikCityProvider>
);

View File

@@ -1,14 +1,25 @@
import { component$ } from "@builder.io/qwik";
import type { DocumentHead } from "@builder.io/qwik-city";
export default component$(() => {
return (
<div class='justify-center items-center w-screen h-screen flex flex-col gap-3' >
<h1 class='text-3xl' >Hi 👋</h1>
<p class='text-xl' >
<>
<h1>Hi 👋</h1>
<div>
Can't wait to see what you build with qwik!
<br />
Happy coding.
</p>
</div>
</>
);
});
export const head: DocumentHead = {
title: "Welcome to Qwik",
meta: [
{
name: "description",
content: "Qwik site description",
},
],
};

View File

@@ -1,10 +1,9 @@
import { component$, Slot } from "@builder.io/qwik";
import type { RequestHandler } from "@builder.io/qwik-city";
import type { DocumentHead } from "@builder.io/qwik-city";
export const onGet: RequestHandler = async ({ cacheControl }) => {
// Control caching for this request for best performance and to reduce hosting costs:
// https://qwik.builder.io/docs/caching/
// https://qwik.dev/docs/caching/
cacheControl({
// Always serve a cached response by default, up to a week stale
staleWhileRevalidate: 60 * 60 * 24 * 7,
@@ -16,14 +15,3 @@ export const onGet: RequestHandler = async ({ cacheControl }) => {
export default component$(() => {
return <Slot />;
});
export const head: DocumentHead = {
title: "netris.me/dev | Build the future of gaming",
meta: [
{
name: "description",
content: "Play with your friends right from your browser",
},
],
};

View File

@@ -2,7 +2,7 @@
* WHAT IS THIS FILE?
*
* The service-worker.ts file is used to have state of the art prefetching.
* https://qwik.builder.io/qwikcity/prefetching/overview/
* https://qwik.dev/qwikcity/prefetching/overview/
*
* Qwik uses a service worker to speed up your site and reduce latency, ie, not used in the traditional way of offline.
* You can also use this file to add more functionality that runs in the service worker.

View File

@@ -1,26 +0,0 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
theme: {
extend: {
fontFamily: {
sans: [
"Geist Sans",
"ui-sans-serif",
"system-ui",
"-apple-system",
"BlinkMacSystemFont",
"Inter",
"Segoe UI",
"Roboto",
"sans-serif",
"Apple Color Emoji",
"Segoe UI Emoji",
"Segoe UI Symbol",
"Noto Color Emoji",
],
},
},
},
plugins: [],
};

View File

@@ -9,14 +9,13 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"moduleResolution": "Bundler",
"esModuleInterop": true,
"skipLibCheck": true,
"incremental": true,
"isolatedModules": true,
"outDir": "tmp",
"noEmit": true,
"types": ["node", "vite/client"],
"paths": {
"~/*": ["./src/*"]
}

View File

@@ -1,13 +0,0 @@
{
"headers": [
{
"source": "/build/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, s-maxage=31536000, immutable"
}
]
}
]
}

View File

@@ -7,39 +7,45 @@ import { qwikVite } from "@builder.io/qwik/optimizer";
import { qwikCity } from "@builder.io/qwik-city/vite";
import tsconfigPaths from "vite-tsconfig-paths";
import pkg from "./package.json";
import { qwikReact } from "@builder.io/qwik-react/vite";
type PkgDep = Record<string, string>;
const { dependencies = {}, devDependencies = {} } = pkg as any as {
dependencies: Record<string, string>;
devDependencies: Record<string, string>;
dependencies: PkgDep;
devDependencies: PkgDep;
[key: string]: unknown;
};
errorOnDuplicatesPkgDeps(devDependencies, dependencies);
/**
* Note that Vite normally starts from `index.html` but the qwikCity plugin makes start at `src/entry.ssr.tsx` instead.
*/
export default defineConfig(({ command, mode }): UserConfig => {
return {
plugins: [qwikCity(), qwikVite(), tsconfigPaths(), qwikReact()],
plugins: [qwikCity(), qwikVite(), tsconfigPaths()],
// This tells Vite which dependencies to pre-build in dev mode.
optimizeDeps: {
// Put problematic deps that break bundling here, mostly those with binaries.
// For example ['better-sqlite3'] if you use that in server functions.
exclude: [],
},
// This tells Vite how to bundle the server code.
ssr:
command === "build" && mode === "production"
? {
// All dev dependencies should be bundled in the server build
noExternal: Object.keys(devDependencies),
// Anything marked as a dependency will not be bundled
// These should only be production binary deps (including deps of deps), CLI deps, and their module graph
// If a dep-of-dep needs to be external, add it here
// For example, if something uses `bcrypt` but you don't have it as a dep, you can write
// external: [...Object.keys(dependencies), 'bcrypt']
external: Object.keys(dependencies),
}
: undefined,
/**
* This is an advanced setting. It improves the bundling of your server code. To use it, make sure you understand when your consumed packages are dependencies or dev dependencies. (otherwise things will break in production)
*/
// ssr:
// command === "build" && mode === "production"
// ? {
// // All dev dependencies should be bundled in the server build
// noExternal: Object.keys(devDependencies),
// // Anything marked as a dependency will not be bundled
// // These should only be production binary deps (including deps of deps), CLI deps, and their module graph
// // If a dep-of-dep needs to be external, add it here
// // For example, if something uses `bcrypt` but you don't have it as a dep, you can write
// // external: [...Object.keys(dependencies), 'bcrypt']
// external: Object.keys(dependencies),
// }
// : undefined,
server: {
headers: {
// Don't cache the server response in dev mode
@@ -54,3 +60,47 @@ export default defineConfig(({ command, mode }): UserConfig => {
},
};
});
// *** utils ***
/**
* Function to identify duplicate dependencies and throw an error
* @param {Object} devDependencies - List of development dependencies
* @param {Object} dependencies - List of production dependencies
*/
function errorOnDuplicatesPkgDeps(
devDependencies: PkgDep,
dependencies: PkgDep,
) {
let msg = "";
// Create an array 'duplicateDeps' by filtering devDependencies.
// If a dependency also exists in dependencies, it is considered a duplicate.
const duplicateDeps = Object.keys(devDependencies).filter(
(dep) => dependencies[dep],
);
// include any known qwik packages
const qwikPkg = Object.keys(dependencies).filter((value) =>
/qwik/i.test(value),
);
// any errors for missing "qwik-city-plan"
// [PLUGIN_ERROR]: Invalid module "@qwik-city-plan" is not a valid package
msg = `Move qwik packages ${qwikPkg.join(", ")} to devDependencies`;
if (qwikPkg.length > 0) {
throw new Error(msg);
}
// Format the error message with the duplicates list.
// The `join` function is used to represent the elements of the 'duplicateDeps' array as a comma-separated string.
msg = `
Warning: The dependency "${duplicateDeps.join(", ")}" is listed in both "devDependencies" and "dependencies".
Please move the duplicated dependencies to "devDependencies" only and remove it from "dependencies"
`;
// Throw an error with the constructed message.
if (duplicateDeps.length > 0) {
throw new Error(msg);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -1,296 +0,0 @@
# This builds and updates the screen recorder we use on Netris
# From https://git.dec05eba.com/gpu-screen-recorder
FROM ubuntu:23.10
ENV DEBIAN_FRONTEND=noninteractive \
TIMEZONE=Africa/Nairobi \
XDG_RUNTIME_DIR=/tmp/runtime-ubuntu \
DISPLAY=:0 \
PULSE_SERVER=unix:/run/pulse/native
# WAYLAND_DISPLAY=wayland-0
# Install fundamental packages
RUN apt-get clean \
&& apt-get update \
&& apt-get upgrade -y \
&& apt-get install --no-install-recommends -y \
apt-transport-https \
apt-utils \
build-essential \
ca-certificates \
curl \
gnupg \
locales \
make \
software-properties-common \
wget \
&& rm -rf /var/lib/apt/lists/* \
&& locale-gen en_US.UTF-8
# Set locales
ENV LANG=en_US.UTF-8 \
LANGUAGE=en_US:en \
LC_ALL=en_US.UTF-8
# Install operating system libraries or packages
RUN dpkg --add-architecture i386 \
&& apt-get update \
&& apt-get install --no-install-recommends -y \
alsa-base \
alsa-utils \
cups-browsed \
cups-bsd \
cups-common \
cups-filters \
printer-driver-cups-pdf \
file \
bzip2 \
gzip \
xz-utils \
unar \
rar \
unrar \
zip \
unzip \
zstd \
gcc \
git \
jq \
python3 \
python3-cups \
python3-numpy \
ssl-cert \
nano \
vim \
htop \
fakeroot \
fonts-dejavu \
fonts-freefont-ttf \
fonts-hack \
fonts-liberation \
fonts-noto \
fonts-noto-cjk \
fonts-noto-cjk-extra \
fonts-noto-color-emoji \
fonts-noto-extra \
fonts-noto-ui-extra \
fonts-noto-hinted \
fonts-noto-mono \
fonts-noto-unhinted \
fonts-opensymbol \
fonts-symbola \
fonts-ubuntu \
lame \
less \
libavcodec-extra \
libpulse0 \
pulseaudio \
supervisor \
net-tools \
packagekit-tools \
pkg-config \
mesa-utils \
va-driver-all \
va-driver-all:i386 \
i965-va-driver-shaders \
i965-va-driver-shaders:i386 \
intel-media-va-driver-non-free \
intel-media-va-driver-non-free:i386 \
libva2 \
libva2:i386 \
vainfo \
vdpau-driver-all \
vdpau-driver-all:i386 \
vdpauinfo \
mesa-vulkan-drivers \
mesa-vulkan-drivers:i386 \
libvulkan-dev \
libvulkan-dev:i386 \
vulkan-tools \
ocl-icd-libopencl1 \
clinfo \
dbus-user-session \
dbus-x11 \
libdbus-c++-1-0v5 \
xkb-data \
xauth \
xbitmaps \
xdg-user-dirs \
xdg-utils \
xfonts-base \
xfonts-scalable \
xinit \
xsettingsd \
libxrandr-dev \
x11-xkb-utils \
x11-xserver-utils \
x11-utils \
x11-apps \
xserver-xorg-input-all \
xserver-xorg-input-wacom \
xserver-xorg-video-all \
xserver-xorg-video-intel \
xserver-xorg-video-qxl \
# Install OpenGL libraries
libxau6 \
libxau6:i386 \
libxdmcp6 \
libxdmcp6:i386 \
libxcb1 \
libxcb1:i386 \
libxext6 \
libxext6:i386 \
libx11-6 \
libx11-6:i386 \
libxv1 \
libxv1:i386 \
libxtst6 \
libxtst6:i386 \
libglvnd0 \
libglvnd0:i386 \
libgl1 \
libgl1:i386 \
libglx0 \
libglx0:i386 \
libegl1 \
libegl1:i386 \
libgles2 \
libgles2:i386 \
libglu1 \
libglu1:i386 \
libsm6 \
libsm6:i386 \
mesa-utils \
mesa-utils-extra \
xcvt \
&& rm -rf /var/lib/apt/lists/* \
&& echo "/usr/local/nvidia/lib" >> /etc/ld.so.conf.d/nvidia.conf \
&& echo "/usr/local/nvidia/lib64" >> /etc/ld.so.conf.d/nvidia.conf \
# Configure OpenCL manually
&& mkdir -pm755 /etc/OpenCL/vendors \
&& echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd \
# Configure Vulkan manually
&& VULKAN_API_VERSION=$(dpkg -s libvulkan1 | grep -oP 'Version: [0-9|\.]+' | grep -oP '[0-9]+(\.[0-9]+)(\.[0-9]+)') \
&& mkdir -pm755 /etc/vulkan/icd.d/ \
&& echo "{\n\
\"file_format_version\" : \"1.0.0\",\n\
\"ICD\": {\n\
\"library_path\": \"libGLX_nvidia.so.0\",\n\
\"api_version\" : \"${VULKAN_API_VERSION}\"\n\
}\n\
}" > /etc/vulkan/icd.d/nvidia_icd.json \
# Configure EGL manually
&& mkdir -pm755 /usr/share/glvnd/egl_vendor.d/ \
&& echo "{\n\
\"file_format_version\" : \"1.0.0\",\n\
\"ICD\": {\n\
\"library_path\": \"libEGL_nvidia.so.0\"\n\
}\n\
}" > /usr/share/glvnd/egl_vendor.d/10_nvidia.json
# Expose NVIDIA libraries and paths
ENV PATH=/usr/local/nvidia/bin${PATH:+:${PATH}} \
LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}/usr/local/nvidia/lib:/usr/local/nvidia/lib64 \
XDG_SESSION_TYPE=x11 \
# Enable AppImage execution in containers
APPIMAGE_EXTRACT_AND_RUN=1
ENV \
# Make all NVIDIA GPUs visible by default
NVIDIA_VISIBLE_DEVICES=all \
# All NVIDIA driver capabilities should preferably be used, check `NVIDIA_DRIVER_CAPABILITIES` inside the container if things do not work
NVIDIA_DRIVER_CAPABILITIES=all \
# Disable VSYNC for NVIDIA GPUs
__GL_SYNC_TO_VBLANK=0
COPY .patches /tmp/
#Build and install gpu-screen-recorder
RUN apt-get update -y \
&& apt-get install -y \
curl \
unzip \
git \
build-essential \
ninja-build \
gcc \
meson \
cmake \
ccache \
bison \
software-properties-common \
ca-certificates \
equivs \
ca-certificates\
libcap2-bin \
libllvm15 \
libavcodec-dev \
libavformat-dev \
libavutil-dev \
libavfilter-dev \
libavdevice-dev \
libswresample-dev \
libswscale-dev \
libx11-dev \
libxcomposite-dev \
libkpipewire-dev \
libxrandr-dev \
libxi-dev \
libxdamage-dev \
libxfixes-dev \
libxi-dev \
libxdamage-dev \
libpulse-dev \
libswresample-dev \
libva-dev \
libcap-dev \
libdrm-dev \
libgl-dev \
libegl-dev \
libwayland-dev \
libnvidia-egl-wayland-dev \
libwayland-egl-backend-dev \
wayland-protocols \
&& rm -rf /var/lib/apt/lists/* \
#Install Nvrtc
&& NVRTC_VERSION="11.0.221" \
&& cd /tmp && curl -fsSL -o nvidia_cuda_nvrtc_linux_x86_64.whl "https://developer.download.nvidia.com/compute/redist/nvidia-cuda-nvrtc/nvidia_cuda_nvrtc-${NVRTC_VERSION}-cp36-cp36m-linux_x86_64.whl" \
&& unzip -joq -d ./nvrtc nvidia_cuda_nvrtc_linux_x86_64.whl && cd nvrtc && chmod 755 libnvrtc* \
&& find . -maxdepth 1 -type f -name "*libnvrtc.so.*" -exec sh -c 'ln -snf $(basename {}) libnvrtc.so' \; \
&& mkdir -p /usr/local/nvidia/lib && mv -f libnvrtc* /usr/local/nvidia/lib \
&& git clone https://repo.dec05eba.com/gpu-screen-recorder && cd gpu-screen-recorder \
&& git apply /tmp/connectcheckskip.patch && git apply /tmp/devicearg.patch \
&& meson setup build \
&& meson configure --prefix=/usr --buildtype=release -Dsystemd=true -Dstrip=true build \
&& ninja -C build install
# #Try building shadow-cast
# RUN git clone https://github.com/gmbeard/shadow-cast && cd shadow-cast \
# && mkdir ./build && cd ./build \
# && cmake -DCMAKE_CXX_FLAGS="-Wno-error=unused-result" -DCMAKE_C_FLAGS="-Wno-error=unused-result" .. \
# && cmake --build . -- -j$(nproc) \
# && chmod +x ./install-helper.sh \
# && ./install-helper.sh
RUN apt-get update -y; \
apt-get upgrade -y; \
add-apt-repository ppa:savoury1/ffmpeg4 \
add-apt-repository ppa:savoury1/ffmpeg6 \
apt-get update -y; \
apt-get upgrade -y && apt-get dist-upgrade -y; \
apt-get install ffmpeg -y; \
#
# Log the ffmpeg version
ffmpeg -version
# Install Xorg and NVIDIA driver installer dependencies
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
kmod \
libc6-dev \
libc6:i386 \
libpci3 \
libelf-dev \
pkg-config \
xorg \
&& rm -rf /var/lib/apt/lists/*

View File

@@ -1 +0,0 @@
/target

1701
bin/input/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
[package]
name = "warp-input"
version = "0.0.1"
edition = "2021"
[dependencies]
anyhow = "1.0.82"
chrono = "0.4.38"
clap = "4.5.4"
enigo = "0.2.1"
env_logger = "0.11.3"
log = "0.4.21"
moq-native = { git = "https://github.com/kixelated/moq-rs", version = "0.1.0" }
moq-transport = { git = "https://github.com/kixelated/moq-rs", version = "0.5.0" }
rand = "0.8.5"
serde = { version="1.0.202" , features = ["derive"]}
serde_json = "1.0.117"
tokio = "1.37.0"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
url = "2.5.0"

View File

@@ -1,43 +0,0 @@
use clap::Parser;
use std::{net, path};
use url::Url;
#[derive(Parser, Clone, Debug)]
pub struct Config {
/// Listen for UDP packets on the given address.
#[arg(long, default_value = "[::]:8080")]
pub bind: net::SocketAddr,
/// Connect to the given URL starting with https://
#[arg(value_parser = moq_url)]
pub url: Url,
/// Use the TLS root CA at this path, encoded as PEM.
///
/// This value can be provided multiple times for multiple roots.
/// If this is empty, system roots will be used instead
#[arg(long)]
pub tls_root: Vec<path::PathBuf>,
/// Danger: Disable TLS certificate verification.
///
/// Fine for local development, but should be used in caution in production.
#[arg(long)]
pub tls_disable_verify: bool,
/// Publish the current time to the relay, otherwise only subscribe.
// #[arg(long)]
// pub publish: bool,
/// The name of the input track.
#[arg(long, default_value = "netris")]
pub namespace: String,
/// The name of the input track.
#[arg(long, default_value = ".catalog")]
pub track: String,
}
fn moq_url(s: &str) -> Result<Url, String> {
Url::try_from(s).map_err(|e| e.to_string())
}

View File

@@ -1,277 +0,0 @@
use anyhow::Context;
use enigo::{
Axis::Horizontal,
Coordinate::Rel,
Direction::{Press, Release},
Enigo, Keyboard, Mouse, Settings,
};
use moq_transport::
serve::{
DatagramsReader, GroupsReader, ObjectsReader,
StreamReader, TrackReader, TrackReaderMode,
};
use serde::{Deserialize, Serialize};
pub struct Subscriber {
track: TrackReader,
}
#[derive(Debug, Serialize, Deserialize)]
struct MessageObject {
input_type: String,
delta_y: Option<i32>,
delta_x: Option<i32>,
button: Option<i32>,
key_code: Option<i32>,
}
impl Subscriber {
pub fn new(track: TrackReader) -> Self {
Self { track }
}
// pub async fn run(self) -> anyhow::Result<()> {
// loop {
// match self.track.mode().await {
// Ok(mode) => match mode {
// TrackReaderMode::Stream(stream) => loop {
// if let Err(err) = Self::recv_stream(stream.clone()).await {
// tracing::warn!("Error receiving streams: {}, retrying...", err);
// tokio::time::sleep(std::time::Duration::from_millis(100)).await;
// } else {
// break;
// }
// },
// TrackReaderMode::Groups(groups) => loop {
// if let Err(err) = Self::recv_groups(groups.clone()).await {
// tracing::warn!("Error receiving groups: {}, retrying...", err);
// tokio::time::sleep(std::time::Duration::from_millis(100)).await;
// } else {
// break;
// }
// },
// TrackReaderMode::Objects(objects) => loop {
// if let Err(err) = Self::recv_objects(objects.clone()).await {
// tracing::warn!("Error receiving objects: {}, retrying...", err);
// tokio::time::sleep(std::time::Duration::from_millis(100)).await;
// } else {
// break;
// }
// },
// TrackReaderMode::Datagrams(datagrams) => loop {
// if let Err(err) = Self::recv_datagrams(datagrams.clone()).await {
// tracing::warn!("Error receiving datagrams: {}, retrying...", err);
// tokio::time::sleep(std::time::Duration::from_millis(100)).await;
// } else {
// break;
// }
// },
// },
// Err(_) => {
// tracing::warn!("Failed to get mode, retrying...");
// tokio::time::sleep(std::time::Duration::from_millis(100)).await;
// return Ok(());
// }
// }
// }
// }
pub async fn run(self) -> anyhow::Result<()> {
match self.track.mode().await.context("failed to connect")? {
TrackReaderMode::Stream(stream) => Self::recv_stream(stream).await,
TrackReaderMode::Groups(groups) => Self::recv_groups(groups).await,
TrackReaderMode::Objects(objects) => Self::recv_objects(objects).await,
TrackReaderMode::Datagrams(datagrams) => Self::recv_datagrams(datagrams).await,
}
}
async fn recv_stream(mut track: StreamReader) -> anyhow::Result<()> {
while let Some(mut group) = track.next().await? {
while let Some(object) = group.read_next().await? {
let str = String::from_utf8_lossy(&object);
println!("{}", str);
}
}
Ok(())
}
async fn recv_groups(mut groups: GroupsReader) -> anyhow::Result<()> {
while let Some(mut group) = groups.next().await? {
let base = group
.read_next()
.await
.context("failed to get first object")?
.context("empty group")?;
//TODO: Use this to allow for authorisation (admin, player or guest) etc etc
let _base = String::from_utf8_lossy(&base);
// let json = serde_json::from_str(&str)?;
//TODO: Handle clipboard
let mut enigo = Enigo::new(&Settings::default()).unwrap();
while let Some(object) = group.read_next().await? {
let str = String::from_utf8_lossy(&object);
let parsed: MessageObject = serde_json::from_str(&str)?;
match parsed.input_type.as_str() {
"mouse_move" => {
if let (Some(x), Some(y)) = (parsed.delta_x, parsed.delta_y) {
// println!("Handling mouse_move with delta_x: {}, delta_y: {}", x, y);
enigo.move_mouse(x, y, Rel).unwrap();
}
}
"mouse_key_down" => {
if let Some(button) = parsed.button {
// println!("Handling mouse_key_down with key: {}", button);
if let Some(key) = mouse_key_to_enigo(button) {
enigo.button(key, Press).unwrap();
}
}
}
"mouse_key_up" => {
if let Some(button) = parsed.button {
// println!("Handling mouse_key_up with key: {}", button);
if let Some(key) = mouse_key_to_enigo(button) {
enigo.button(key, Release).unwrap();
}
}
}
"mouse_wheel_up" => {
//TODO: handle vertical scrolling
// println!("Handling mouse_wheel_up with key");
enigo.scroll(-2, Horizontal).unwrap();
}
"mouse_wheel_down" => {
//TODO: handle vertical scrolling
// println!("Handling mouse_wheel_down with key");
enigo.scroll(2, Horizontal).unwrap();
}
"key_up" => {
if let Some(key_code) = parsed.key_code {
// println!("Handling key_up with key: {}", key_code);
if let Some(key) = key_to_enigo(key_code as u8) {
enigo.key(key, Release).unwrap();
}
}
}
"key_down" => {
if let Some(key_code) = parsed.key_code {
// println!("Handling key_down with key: {}", key_code);
if let Some(key) = key_to_enigo(key_code as u8) {
enigo.key(key, Press).unwrap();
}
}
}
_ => {
println!("Unknown input_type: {}", parsed.input_type);
}
}
}
}
Ok(())
}
async fn recv_objects(mut objects: ObjectsReader) -> anyhow::Result<()> {
while let Some(mut object) = objects.next().await? {
let payload = object.read_all().await?;
let str = String::from_utf8_lossy(&payload);
println!("{}", str);
}
Ok(())
}
async fn recv_datagrams(mut datagrams: DatagramsReader) -> anyhow::Result<()> {
while let Some(datagram) = datagrams.read().await? {
let str = String::from_utf8_lossy(&datagram.payload);
println!("{}", str);
}
Ok(())
}
}
pub fn mouse_key_to_enigo(key: i32) -> Option<enigo::Button> {
match key {
0 => Some(enigo::Button::Left),
1 => Some(enigo::Button::Middle),
2 => Some(enigo::Button::Right),
_ => None,
}
}
pub fn key_to_enigo(key: u8) -> Option<enigo::Key> {
match key {
27 => Some(enigo::Key::Escape),
112 => Some(enigo::Key::F1),
113 => Some(enigo::Key::F2),
114 => Some(enigo::Key::F3),
115 => Some(enigo::Key::F4),
116 => Some(enigo::Key::F5),
117 => Some(enigo::Key::F6),
118 => Some(enigo::Key::F7),
119 => Some(enigo::Key::F8),
120 => Some(enigo::Key::F9),
121 => Some(enigo::Key::F10),
122 => Some(enigo::Key::F11),
123 => Some(enigo::Key::F12),
// 19 => Some(enigo::Key::Pause), // Pause
// 97 => Some(enigo::Key::Print), // Print
46 => Some(enigo::Key::Delete),
35 => Some(enigo::Key::End),
192 => Some(enigo::Key::Unicode('`')),
48 => Some(enigo::Key::Unicode('0')),
49 => Some(enigo::Key::Unicode('1')),
50 => Some(enigo::Key::Unicode('2')),
51 => Some(enigo::Key::Unicode('3')),
52 => Some(enigo::Key::Unicode('4')),
53 => Some(enigo::Key::Unicode('5')),
54 => Some(enigo::Key::Unicode('6')),
55 => Some(enigo::Key::Unicode('7')),
56 => Some(enigo::Key::Unicode('8')),
57 => Some(enigo::Key::Unicode('9')),
189 => Some(enigo::Key::Unicode('-')),
187 => Some(enigo::Key::Unicode('=')),
8 => Some(enigo::Key::Backspace),
9 => Some(enigo::Key::Tab),
219 => Some(enigo::Key::Unicode('[')),
221 => Some(enigo::Key::Unicode(']')),
220 => Some(enigo::Key::Unicode('\\')),
20 => Some(enigo::Key::CapsLock),
186 => Some(enigo::Key::Unicode(';')),
222 => Some(enigo::Key::Unicode('\'')),
13 => Some(enigo::Key::Return),
16 => Some(enigo::Key::Shift), // ShiftL
188 => Some(enigo::Key::Unicode(',')),
190 => Some(enigo::Key::Unicode('.')),
191 => Some(enigo::Key::Unicode('/')),
161 => Some(enigo::Key::Shift), // ShiftR
38 => Some(enigo::Key::UpArrow),
17 => Some(enigo::Key::Control), // ControlL
18 => Some(enigo::Key::Alt), // AltL
32 => Some(enigo::Key::Space),
165 => Some(enigo::Key::Alt), // AltR
// 103 => Some(enigo::Key::Menu),
163 => Some(enigo::Key::Control), // ControlR
37 => Some(enigo::Key::LeftArrow),
40 => Some(enigo::Key::DownArrow),
39 => Some(enigo::Key::RightArrow),
// 99 => Some(enigo::Key::Raw(45)), // Insert
34 => Some(enigo::Key::PageDown),
36 => Some(enigo::Key::Home),
33 => Some(enigo::Key::PageUp),
a if a >= 65 && a <= 90 => Some(enigo::Key::Unicode((a - 65 + ('a' as u8)) as char)),
_ => None,
}
}
//NAME="${NAME:-$(head /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | head -c 16)}"
// let _name = env::var("NAMESPACE").unwrap_or_else(|_| {
// let rng = rand::thread_rng();
// let random_string: String = rng
// .sample_iter(&rand::distributions::Alphanumeric)
// .take(16)
// .map(char::from)
// .collect();
// random_string
// });

View File

@@ -1,121 +0,0 @@
use anyhow::Context;
use chrono::prelude::*;
use clap::Parser;
use moq_native::quic;
use moq_transport::{serve, session::Subscriber};
use std::net;
use url::Url;
mod input;
#[derive(Parser, Clone)]
pub struct Cli {
/// Listen for UDP packets on the given address.
#[arg(long, default_value = "[::]:0")]
pub bind: net::SocketAddr,
/// Connect to the given URL starting with https://
#[arg()]
pub url: Url,
/// The TLS configuration.
#[command(flatten)]
pub tls: moq_native::tls::Cli,
// /// Publish the current time to the relay, otherwise only subscribe.
// #[arg(long)]
// pub publish: bool,
/// The name of the input track.
#[arg(long, default_value = "netris")]
pub namespace: String,
/// The name of the input track.
#[arg(long, default_value = ".catalog")]
pub track: String,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
env_logger::init();
// Disable tracing so we don't get a bunch of Quinn spam.
let tracer = tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::WARN)
.finish();
tracing::subscriber::set_global_default(tracer).unwrap();
let config = Cli::parse();
let tls = config.tls.load()?;
let quic = quic::Endpoint::new(quic::Config {
bind: config.bind,
tls,
})?;
let start = Utc::now();
let mut now = start;
loop {
log::info!("connecting to server: url={}", config.url);
let session = quic.client.connect(&config.url).await?;
let (session, mut subscriber) = Subscriber::connect(session)
.await
.context("failed to create MoQ Transport session")?;
let namespace = format!("{}input", config.namespace);
let (prod, sub) = serve::Track::new(namespace, config.track.clone()).produce();
let input = input::Subscriber::new(sub);
// let (session, mut publisher) = Publisher::connect(session)
// .await
// .context("failed to create MoQ Transport session")?;
// let (mut writer, _, reader) = serve::Tracks {
// namespace: config.namespace.clone(),
// }
// .produce();
// let track = writer.create(&config.track).unwrap();
// let input_publisher = input::Publisher::new(track.groups()?);
tokio::select! {
res = session.run() => {
if let Err(e) = res {
log::error!("session error: {}", e);
continue;
}
},
res = input.run() => {
if let Err(e) = res {
log::error!("input subscriber error: {}", e);
continue;
}
},
// res = publisher.announce(reader) => res.context("failed to serve tracks")?,
res = subscriber.subscribe(prod) => {
if let Err(e) = res {
log::error!("failed to subscribe to track: {}", e);
continue;
}
},
}
let next = now + chrono::Duration::try_minutes(1).unwrap();
let next = next.with_second(0).unwrap().with_nanosecond(0).unwrap();
let delay = (next - now).to_std().unwrap();
tokio::time::sleep(delay).await;
// if next.minute() - now.minute() == 10 {
// return Ok(());
// }
now = next; // just assume we didn't undersleep
}
// Ok(())
}

BIN
bun.lockb Executable file

Binary file not shown.

1
cli

Submodule cli deleted from d95b4a84cb

View File

@@ -1,3 +0,0 @@
#!/bin/bash -e
docker build -t server -f server.Dockerfile .

View File

@@ -1,3 +0,0 @@
#!/bin/bash -e
cargo run -- --namespace $SESSION_ID --bind "[::]:8080" https://fst.so:4443

View File

@@ -1,5 +0,0 @@
#!/bin/bash -e
NAME="${NAME:-$(head /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | head -c 16)}"
# docker run --gpus all --device=/dev/dri --rm -it --entrypoint /bin/bash -e SESSION_ID=G4r06Kc8vDmwIXPG -v "$(pwd)":/game -p 8080:8080/udp --cap-add=SYS_NICE --cap-add=SYS_ADMIN server
docker run --gpus all --device=/dev/dri --rm -it --entrypoint /bin/bash -e SESSION_ID=$NAME -v "$(pwd)":/game -p 8080:8080/udp --cap-add=SYS_NICE --cap-add=SYS_ADMIN server

View File

@@ -1,20 +0,0 @@
#This fixes the error where x11-apps in docker cannot seem to access the hosts DISPLAY server
# Error: No protocol specified
# Error: Can't open display: :10.0
#https://askubuntu.com/a/1470341 //Run it on host OS
xhost +Local:*
xhost
#Fun fact Weston won't run if you do not have Wayland running on the host (i have yet to try running with Xwayland inside the container)
#Run weston using your host's X11 display server
weston --backend=x11-backend.so
#Run inside the terminal of the weston you just created... cool right?
weston --backend=wayland-backend.so
#Run
docker run --gpus all --entrypoint /bin/bash --rm -it -v $(pwd):/games -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY --cap-add=SYS_NICE --cap-add=SYS_ADMIN recorder
docker run --gpus all --entrypoint /bin/bash --device=/dev/dri --rm -it -v $(pwd):/game --cap-add=SYS_NICE --cap-add=SYS_ADMIN ghcr.io/wanjohiryan/netris/server:nightly
ffmpeg -hide_banner -v quiet -stream_loop -1 -re -i /game/test.mp4 -an -f mp4 -movflags empty_moov+frag_every_frame+separate_moof+omit_tfhd_offset - | RUST_LOG=moq_pub=info warp --name "netris" https://fst.so:4443

Submodule moq-server deleted from aedb284ac8

23
package.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "nestri",
"private": true,
"scripts": {
"build": "turbo build",
"dev": "turbo dev",
"lint": "turbo lint",
"format": "prettier --write \"**/*.{ts,tsx,md}\""
},
"devDependencies": {
"prettier": "^3.2.5",
"turbo": "^2.0.14",
"typescript": "^5.4.5"
},
"engines": {
"node": ">=18"
},
"packageManager": "bun@1.1.18",
"workspaces": [
"apps/*",
"packages/*"
]
}

View File

@@ -0,0 +1,3 @@
# `@turbo/eslint-config`
Collection of internal eslint configurations.

View File

@@ -0,0 +1,34 @@
const { resolve } = require("node:path");
const project = resolve(process.cwd(), "tsconfig.json");
/** @type {import("eslint").Linter.Config} */
module.exports = {
extends: ["eslint:recommended", "prettier", "turbo"],
plugins: ["only-warn"],
globals: {
React: true,
JSX: true,
},
env: {
node: true,
},
settings: {
"import/resolver": {
typescript: {
project,
},
},
},
ignorePatterns: [
// Ignore dotfiles
".*.js",
"node_modules/",
"dist/",
],
overrides: [
{
files: ["*.js?(x)", "*.ts?(x)"],
},
],
};

View File

@@ -0,0 +1,35 @@
const { resolve } = require("node:path");
const project = resolve(process.cwd(), "tsconfig.json");
/** @type {import("eslint").Linter.Config} */
module.exports = {
extends: [
"eslint:recommended",
"prettier",
require.resolve("@vercel/style-guide/eslint/next"),
"turbo",
],
globals: {
React: true,
JSX: true,
},
env: {
node: true,
browser: true,
},
plugins: ["only-warn"],
settings: {
"import/resolver": {
typescript: {
project,
},
},
},
ignorePatterns: [
// Ignore dotfiles
".*.js",
"node_modules/",
],
overrides: [{ files: ["*.js?(x)", "*.ts?(x)"] }],
};

View File

@@ -0,0 +1,19 @@
{
"name": "@nestri/eslint-config",
"version": "0.0.0",
"private": true,
"files": [
"library.js",
"next.js",
"react-internal.js"
],
"devDependencies": {
"@vercel/style-guide": "^5.2.0",
"eslint-config-turbo": "^2.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-only-warn": "^1.1.0",
"@typescript-eslint/parser": "^7.1.0",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"typescript": "^5.3.3"
}
}

View File

@@ -0,0 +1,39 @@
const { resolve } = require("node:path");
const project = resolve(process.cwd(), "tsconfig.json");
/*
* This is a custom ESLint configuration for use with
* internal (bundled by their consumer) libraries
* that utilize React.
*/
/** @type {import("eslint").Linter.Config} */
module.exports = {
extends: ["eslint:recommended", "prettier", "turbo"],
plugins: ["only-warn"],
globals: {
React: true,
JSX: true,
},
env: {
browser: true,
},
settings: {
"import/resolver": {
typescript: {
project,
},
},
},
ignorePatterns: [
// Ignore dotfiles
".*.js",
"node_modules/",
"dist/",
],
overrides: [
// Force ESLint to detect .tsx files
{ files: ["*.js?(x)", "*.ts?(x)"] },
],
};

View File

@@ -0,0 +1,20 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Default",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"incremental": false,
"isolatedModules": true,
"lib": ["es2022", "DOM", "DOM.Iterable"],
"module": "NodeNext",
"moduleDetection": "force",
"moduleResolution": "NodeNext",
"noUncheckedIndexedAccess": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "ES2022"
}
}

View File

@@ -0,0 +1,13 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Next.js",
"extends": "./base.json",
"compilerOptions": {
"plugins": [{ "name": "next" }],
"module": "ESNext",
"moduleResolution": "Bundler",
"allowJs": true,
"jsx": "preserve",
"noEmit": true
}
}

View File

@@ -0,0 +1,9 @@
{
"name": "@nestri/typescript-config",
"version": "0.0.0",
"private": true,
"license": "MIT",
"publishConfig": {
"access": "public"
}
}

View File

@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "React Library",
"extends": "./base.json",
"compilerOptions": {
"jsx": "react-jsx"
}
}

10
packages/ui/.eslintrc.js Normal file
View File

@@ -0,0 +1,10 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
root: true,
extends: ["@nestri/eslint-config/react-internal.js"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "./tsconfig.lint.json",
tsconfigRootDir: __dirname,
},
};

28
packages/ui/package.json Normal file
View File

@@ -0,0 +1,28 @@
{
"name": "@nestri/ui",
"version": "0.0.0",
"private": true,
"exports": {
"./button": "./src/button.tsx",
"./card": "./src/card.tsx",
"./code": "./src/code.tsx"
},
"scripts": {
"lint": "eslint . --max-warnings 0",
"generate:component": "turbo gen react-component"
},
"devDependencies": {
"@nestri/eslint-config": "*",
"@nestri/typescript-config": "*",
"@turbo/gen": "^1.12.4",
"@types/node": "^20.11.24",
"@types/eslint": "^8.56.5",
"@types/react": "^18.2.61",
"@types/react-dom": "^18.2.19",
"eslint": "^8.57.0",
"typescript": "^5.3.3"
},
"dependencies": {
"react": "^18.2.0"
}
}

View File

@@ -0,0 +1,20 @@
"use client";
import { ReactNode } from "react";
interface ButtonProps {
children: ReactNode;
className?: string;
appName: string;
}
export const Button = ({ children, className, appName }: ButtonProps) => {
return (
<button
className={className}
onClick={() => alert(`Hello from your ${appName} app!`)}
>
{children}
</button>
);
};

Some files were not shown because too many files have changed in this diff Show More