Compare commits

...

51 Commits
test ... v2.0.5

Author SHA1 Message Date
Janis
232bca2bbc remove doubled pathname issue 2023-11-12 18:52:24 +01:00
Janis
0b74390d2e update version 2023-11-12 18:50:13 +01:00
Janis
d16e80a48d fix pathname in generator 2023-11-12 18:49:49 +01:00
Janis
c5009271c4 update version 2023-11-12 18:43:57 +01:00
Janis
e9f3fa28f9 remove auto reroute 2023-11-12 18:43:24 +01:00
Janis
8b23a8cca7 update version 2023-11-12 18:41:36 +01:00
Janis
87dd069dec fix url generation for gh pages 2023-11-12 18:41:12 +01:00
Janis
1a5f0b8e6e update version 2023-11-12 18:35:34 +01:00
Janis
8db32153d2 Use Hashrouter because of ghpages 2023-11-12 18:34:40 +01:00
Janis
b1da52ad1d Merge pull request #88 from DerTyp7/dev
Implement generator to simplify customization
2023-11-12 18:24:08 +01:00
Janis
dbc2bd422d Merge pull request #87 from DerTyp7/feature_implement-url-generator
Feature implement URL generator
2023-11-12 18:21:44 +01:00
Janis
86d2eb4c50 remove vite-plugin-singlefile 2023-11-12 18:19:14 +01:00
Janis
63bef6e11d update readme 2023-11-12 18:16:18 +01:00
Janis
e65eb5b3c4 Update README.md 2023-11-12 18:14:30 +01:00
Janis
e847dde41d update readme 2023-11-12 18:14:08 +01:00
Janis
5f0ca2c209 clean up 2023-11-12 18:03:20 +01:00
Janis
a4f64c3781 add instructions 2023-11-12 17:24:34 +01:00
Janis
9870fdcdb4 add responsive design 2023-11-12 17:16:58 +01:00
Janis
ce7aee8481 fix viewer style 2023-11-12 16:41:09 +01:00
Janis
5ef2facfec better generator 2023-11-12 16:28:42 +01:00
bbd10af0dc add basic generator structure 2023-11-10 11:20:58 +01:00
Janis
e2a8472cf9 add generator url 2023-11-05 17:40:16 +01:00
Janis
ed78b23bb1 add router 2023-11-05 17:32:27 +01:00
Janis
58fa81eb71 change version to 2.0.0 2023-11-05 17:20:01 +01:00
Janis
3a75c91bbd use moduleResolution node 2023-11-05 17:19:14 +01:00
Janis
fa64de9666 update vite 2023-11-05 17:16:22 +01:00
Janis
847cb76bca Merge pull request #83 from DerTyp7/dependabot/npm_and_yarn/dev/eslint-8.53.0
Bump eslint from 8.45.0 to 8.53.0
2023-11-05 17:10:25 +01:00
Janis
6a7dd4455e Merge pull request #84 from DerTyp7/dependabot/npm_and_yarn/dev/typescript-eslint/parser-6.9.1
Bump @typescript-eslint/parser from 6.2.1 to 6.9.1
2023-11-05 17:10:19 +01:00
dependabot[bot]
61678b3ed4 Bump eslint from 8.45.0 to 8.53.0
Bumps [eslint](https://github.com/eslint/eslint) from 8.45.0 to 8.53.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.45.0...v8.53.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-05 16:10:14 +00:00
Janis
fea7f03d20 Merge pull request #85 from DerTyp7/dependabot/npm_and_yarn/dev/types/react-dom-18.2.14
Bump @types/react-dom from 18.2.7 to 18.2.14
2023-11-05 17:10:12 +01:00
dependabot[bot]
7551dd421c Bump @typescript-eslint/parser from 6.2.1 to 6.9.1
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.2.1 to 6.9.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.9.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-05 16:10:00 +00:00
dependabot[bot]
18b4c0c67d Bump @types/react-dom from 18.2.7 to 18.2.14
Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 18.2.7 to 18.2.14.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

---
updated-dependencies:
- dependency-name: "@types/react-dom"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-05 16:09:55 +00:00
Janis
e75116d6c9 Merge pull request #86 from DerTyp7/dependabot/npm_and_yarn/dev/types/react-18.2.35
Bump @types/react from 18.2.23 to 18.2.35
2023-11-05 17:09:20 +01:00
dependabot[bot]
176cdc79ed Bump @types/react from 18.2.23 to 18.2.35
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.23 to 18.2.35.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-05 16:08:39 +00:00
Janis
6ca724bd77 Merge pull request #82 from DerTyp7/dependabot/npm_and_yarn/react-router-dom-6.18.0
Bump react-router-dom from 6.14.2 to 6.18.0
2023-11-05 17:07:53 +01:00
Janis
3bd6c60987 dependabot should target dev branch 2023-11-05 17:06:49 +01:00
dependabot[bot]
eb73c51ee1 Bump react-router-dom from 6.14.2 to 6.18.0
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.14.2 to 6.18.0.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.18.0/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-01 01:11:44 +00:00
Janis
945c3ec582 Update bug_report.md 2023-10-23 15:12:11 +02:00
Janis
2c24379bbb Update README.md 2023-10-21 16:28:24 +02:00
Janis
d67a243c5d Update README 2023-10-07 23:04:42 +02:00
Janis
3174145067 update package.json 2023-10-07 22:59:51 +02:00
Janis
c40b33de2b update prettier config 2023-10-07 22:53:39 +02:00
Janis
7974a08b24 remove old eslint comment 2023-10-07 22:50:45 +02:00
Janis
ab38a1b1fe add . to meta 2023-10-07 22:48:23 +02:00
Janis
bb7d2d61c6 add meta tags 2023-10-07 22:46:04 +02:00
Janis
81eed1e0ab Delete .github/workflows/eslint.yml 2023-10-07 22:39:04 +02:00
Janis
d75e11f3cd Create eslint.yml 2023-10-07 22:36:17 +02:00
Janis
c5a8f484b7 Update README.md 2023-10-07 20:32:00 +02:00
Janis
c5b801d3d5 Update README.md 2023-10-07 17:29:33 +02:00
Janis
c86618f78f add better readability 2023-10-07 17:26:20 +02:00
Janis
2f1e91c199 add new common issue 2023-10-07 17:24:26 +02:00
19 changed files with 796 additions and 336 deletions

View File

@@ -1,14 +1,11 @@
module.exports = {
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
plugins: ['react-refresh'],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended"],
parser: "@typescript-eslint/parser",
parserOptions: { ecmaVersion: "latest", sourceType: "module" },
plugins: ["react-refresh"],
rules: {
'react-refresh/only-export-components': 'warn',
"react-refresh/only-export-components": "warn",
"react-hooks/exhaustive-deps": "ignore",
},
}
};

View File

@@ -17,6 +17,10 @@ Example or explanation to reproduce the issue.
## Expected behavior
A clear and concise description of what you expected to happen.
**Do you use the local or online version?**
- [x] Online version with URL: `https://dertyp7.github.io/ts5-obs-overlay`
- [ ] The version named below locally downloaded on my device
**Your environment:**
- OS: [e.g. Windows 11]
- OBS version: [e.g. 29.1.3]

View File

@@ -8,3 +8,4 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "monthly"
target-branch: "dev"

View File

@@ -1,5 +1,7 @@
{
"tabWidth": 2,
"useTabs": false,
"printWidth": 120
"printWidth": 180,
"singleQuote": false,
"semi": true
}

124
README.md
View File

@@ -1,104 +1,61 @@
# Teamspeak5-OBS-Overlay
# TeamSpeak5-OBS-Overlay
This is an overlay for OBS to show the current talking clients in your Teamspeak 5 Channel.
This App uses the new "Remote Apps" feature of Teamspeak 5.
This is an overlay for OBS to show the current talking clients in your TeamSpeak5 Channel.
This App uses the new "Remote Apps" feature of TeamSpeak5.
This overlay uses the [Teamspeak 5 Remote App API](https://github.com/DerTyp7/react-ts5-remote-app-api).
This overlay uses the [TeamSpeak5 Remote App API](https://github.com/DerTyp7/react-ts5-remote-app-api).
![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/d0ab06f2-1a36-479d-826f-bd4bd3d405b7)
- [Teamspeak5-OBS-Overlay](#teamspeak5-obs-overlay)
- [TeamSpeak5-OBS-Overlay](#teamspeak5-obs-overlay)
- [Usage](#usage)
- [Quick instructions (online usage only)](#quick-instructions-online-usage-only)
- [Instructions](#instructions)
- [Settings (Parameters)](#settings-parameters)
- [Are you using the **online** version (recommended version)?](#are-you-using-the-online-version-recommended-version)
- [Are you using the **offline** version?](#are-you-using-the-offline-version)
- [Adding Parameters](#adding-parameters)
- [Setup (Developer)](#setup-developer)
- [Quick instructions](#quick-instructions)
- [Detailed instructions](#detailed-instructions)
- [Common Issues](#common-issues)
- [The overlay is empty, but i'm connected to a Teamspeak 5 server](#the-overlay-is-empty-but-im-connected-to-a-teamspeak-5-server)
- [The overlay is empty, but i'm connected to a TeamSpeak5 server](#the-overlay-is-empty-but-im-connected-to-a-teamspeak5-server)
- [OBS doesn't show the latest version of the overlay](#obs-doesnt-show-the-latest-version-of-the-overlay)
- [Setup (Developer)](#setup-developer)
## Usage
### Quick instructions (online usage only)
### Quick instructions
1. Go into the Teamspeak 5 Settings and enable "Remote Apps"
2. Add a new Browser Source to your OBS Scene and enter `https://dertyp7.github.io/ts5-obs-overlay/` as URL
3. Set the width and height to your desired size (e.g. 1920x1080 OR 1280x720)
4. You should now receive a notification in Teamspeak 5 that the app is allowed to connect to your Teamspeak 5 client. Allow it.
1. Open this link in your Browser: [https://dertyp7.github.io/ts5-obs-overlay/generate](https://dertyp7.github.io/ts5-obs-overlay/generate)
2. Follow the instructions on the website
3. Accept overlay inside TeamSpeak5
![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/40faa435-e128-415f-98eb-a9e8809e8f65)
### Instructions
### Detailed instructions
1. Go into the Teamspeak 5 Settings and enable "Remote Apps"
Try this instruction if you have problems with the quick instructions above.
1. Open this link in your Browser: [https://dertyp7.github.io/ts5-obs-overlay/generate](https://dertyp7.github.io/ts5-obs-overlay/generate)
2. Follow the instructions on the website
3. Go into the TeamSpeak5 Settings and enable "Remote Apps"
![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/b31bc553-fde2-46ab-b07c-d3c81339cc7d)
2. Add a new Browser Source to your OBS Scene
4. Add a new Browser Source to your OBS Scene
![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/0198b468-bb96-4b65-bdd4-3d6bb3ef7d25)
![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/58ad399f-5344-456f-b243-6e267b489fd5)
3. Configure **Browser Source** (Use **ONE** of the following methods)
5. Enter the in step 1 generated URL into the URL field of the Browser Source
![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/50b755f9-d4b4-469f-9136-e2b18f226547)
1. **Online Usage (recommended):** Enter **`https://dertyp7.github.io/ts5-obs-overlay/`** as URL
2. **Offline Usage (ignore this if you use the online usage above):**
1. Download the `ts5-overlay-{version}.html` of the latest release from [here](https://github.com/DerTyp7/ts5-obs-overlay/releases/latest)
![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/04dc3a66-c493-429b-b4ae-44bade473ad6)
1.1. (optional) You can rename the file. Just remember using the new file name in the future instead of `ts5-overlay-{version}.html`
2. Tick the checkbox "Local File" and select the downloaded `ts5-overlay-{version}.html`
6. Set the width and height to your desired size. Recommended is a ratio of 1:1 or 1:2 (e.g. 1500x3000 OR 1000x2000)
4. Set the width and height to your desired size (e.g. 1920x1080 OR 1280x720)
5. You should now receive a notification in Teamspeak 5 that the app is allowed to connect to your Teamspeak 5 client. Allow it. (If you don't get a notification, restart Teamspeak 5 and OBS -> try again)
7. You should now receive a notification in TeamSpeak5 that the app is allowed to connect to your TeamSpeak5 client. Allow it. (If you don't get a notification, restart TeamSpeak5 and OBS -> try again)
![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/40faa435-e128-415f-98eb-a9e8809e8f65)
## Settings (Parameters)
You can customize the overlay by **adding parameters** to the URL of the **Browser Source**.
### Are you using the **online** version (recommended version)?
1. Open your Browser Source settings
2. Start adding parameters like discribed in [Adding Parameters](#adding-parameters)
### Are you using the **offline** version?
1. Open your Browser Source settings
2. **Untick** the checkbox "Local File"
3. Add `file://` to the beginning of the URL
![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/87985b4c-4397-4681-9635-239d1e382c24)
4. Start adding parameters like discribed in [Adding Parameters](#adding-parameters)
### Adding Parameters
Start by adding a `?` to the end of the URL and then add the parameters.
To add multiple parameters, you have to seperate them with a `&`.
Like this:
**Online:** _`https://dertyp7.github.io/ts5-obs-overlay/?parameter1=value1&parameter2=value2`_
**Offline:** _`file://C:/Users/.../ts5-overlay-{version}.html?parameter1=value1&parameter2=value2`_
Real example:
**Online:** _`https://dertyp7.github.io/ts5-obs-overlay/?remoteAppPort=5899&hideNonTalking=true&clientLimit=5&showChannelName=true`_
**Offline:** _`file://C:/Users/.../ts5-overlay-{version}.html?remoteAppPort=5899&hideNonTalking=true&clientLimit=5&showChannelName=true`_
This is a **list** of **all available parameters** (all parameters are optional):
| Parameter | Description | Type | Default |
| ----------------- | ---------------------------------------- | ------- | --------------- |
| `remoteAppPort` | The port of the Teamspeak 5 remote app | number | `5899` |
| `hideNonTalking` | Hide all non-talking clients | boolean | `false` |
| `clientLimit` | Count of how many client should be shown | number | `0` (unlimited) |
| `showChannelName` | Display the channel name | boolean | `false` |
## Setup (Developer)
1. Clone this repository
2. Run `npm install`
3. To start the development server run `npm run dev`
## Common Issues
### The overlay is empty, but i'm connected to a Teamspeak 5 server
### The overlay is empty, but i'm connected to a TeamSpeak5 server
**Fix 1**
Make sure you accepted the notification in your TeamSpeak Client.
**Fix 2**
Sadly TeamSpeak5 does not give us any information about the current active server tab.
So we try currently use a workaround, where the active server tab is determined by looking on which server the your hardware input was unmuted the latest, since the non-active server tabs in TS5 usually mute the clients microphone.
@@ -108,3 +65,18 @@ Possible fixes:
- Unmute and mute yourself in the active server tab (Just a normal unmute and mute, not the hardware mute)
- Reconnect to the TS5 server while the overlay is open
### OBS doesn't show the latest version of the overlay
This can happen if the OBS Browser Source is caching the overlay.
To fix this, open the Browser Source settings and click on "Refresh cache of current page".
## Setup (Developer)
1. Clone this repository
2. Run `npm install`
3. To start the development server run `npm run dev`
> **Note:** Pull requests are welcome, but please be consistent with the code style.
> This project uses [Prettier](https://prettier.io/) to format the code.
> Pull requests always in the `dev` branch.

View File

@@ -1,12 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TS5-OBS-Overlay</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="An OBS overlay for TeamSpeak5, so your audience can see who is talking. https://github.com/DerTyp7/ts5-obs-overlay" />
<meta name="author" content="DerTyp7" />
<link rel="icon" href="/logo.svg" type="image/x-icon" />
<title>TS5-OBS-Overlay</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

270
package-lock.json generated
View File

@@ -1,33 +1,32 @@
{
"name": "ts5-obs-overlay",
"version": "1.2.4",
"version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ts5-obs-overlay",
"version": "1.2.4",
"version": "2.0.0",
"dependencies": {
"@types/node": "^20.8.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.14.2",
"react-router-dom": "^6.18.0",
"react-ts5-remote-app-api": "^1.1.1",
"sass": "^1.68.0"
},
"devDependencies": {
"@types/jest": "^29.5.2",
"@types/react": "^18.2.23",
"@types/react-dom": "^18.2.7",
"@types/react": "^18.2.35",
"@types/react-dom": "^18.2.14",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.2.1",
"@typescript-eslint/parser": "^6.9.1",
"@vitejs/plugin-react-swc": "^3.0.0",
"eslint": "^8.45.0",
"eslint": "^8.53.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"typescript": "^5.1.6",
"vite": "^4.4.7",
"vite-plugin-singlefile": "^0.13.5"
"typescript": "^5.2.2",
"vite": "^4.5.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -533,18 +532,18 @@
}
},
"node_modules/@eslint-community/regexpp": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz",
"integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==",
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
"integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
"dev": true,
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/eslintrc": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz",
"integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==",
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
"integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
"dev": true,
"dependencies": {
"ajv": "^6.12.4",
@@ -565,21 +564,21 @@
}
},
"node_modules/@eslint/js": {
"version": "8.44.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz",
"integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
"integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
"integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
"version": "0.11.13",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
"integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
"dev": true,
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
"@humanwhocodes/object-schema": "^2.0.1",
"debug": "^4.1.1",
"minimatch": "^3.0.5"
},
@@ -601,9 +600,9 @@
}
},
"node_modules/@humanwhocodes/object-schema": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
"integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
"dev": true
},
"node_modules/@jest/expect-utils": {
@@ -696,11 +695,11 @@
}
},
"node_modules/@remix-run/router": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.2.tgz",
"integrity": "sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.11.0.tgz",
"integrity": "sha512-BHdhcWgeiudl91HvVa2wxqZjSHbheSgIiDvxrF1VjFzBzpTtuDPkOdOi3Iqvc08kXtFkLjhbS+ML9aM8mJS+wQ==",
"engines": {
"node": ">=14"
"node": ">=14.0.0"
}
},
"node_modules/@rollup/plugin-typescript": {
@@ -1026,9 +1025,9 @@
"devOptional": true
},
"node_modules/@types/react": {
"version": "18.2.23",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.23.tgz",
"integrity": "sha512-qHLW6n1q2+7KyBEYnrZpcsAmU/iiCh9WGCKgXvMxx89+TYdJWRjZohVIo9XTcoLhfX3+/hP0Pbulu3bCZQ9PSA==",
"version": "18.2.37",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz",
"integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==",
"devOptional": true,
"dependencies": {
"@types/prop-types": "*",
@@ -1037,9 +1036,9 @@
}
},
"node_modules/@types/react-dom": {
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz",
"integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==",
"version": "18.2.15",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.15.tgz",
"integrity": "sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg==",
"dev": true,
"dependencies": {
"@types/react": "*"
@@ -1114,15 +1113,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.2.1.tgz",
"integrity": "sha512-Ld+uL1kYFU8e6btqBFpsHkwQ35rw30IWpdQxgOqOh4NfxSDH6uCkah1ks8R/RgQqI5hHPXMaLy9fbFseIe+dIg==",
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz",
"integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "6.2.1",
"@typescript-eslint/types": "6.2.1",
"@typescript-eslint/typescript-estree": "6.2.1",
"@typescript-eslint/visitor-keys": "6.2.1",
"@typescript-eslint/scope-manager": "6.10.0",
"@typescript-eslint/types": "6.10.0",
"@typescript-eslint/typescript-estree": "6.10.0",
"@typescript-eslint/visitor-keys": "6.10.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1142,13 +1141,13 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.2.1.tgz",
"integrity": "sha512-UCqBF9WFqv64xNsIEPfBtenbfodPXsJ3nPAr55mGPkQIkiQvgoWNo+astj9ZUfJfVKiYgAZDMnM6dIpsxUMp3Q==",
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz",
"integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.2.1",
"@typescript-eslint/visitor-keys": "6.2.1"
"@typescript-eslint/types": "6.10.0",
"@typescript-eslint/visitor-keys": "6.10.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -1159,9 +1158,9 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.2.1.tgz",
"integrity": "sha512-528bGcoelrpw+sETlyM91k51Arl2ajbNT9L4JwoXE2dvRe1yd8Q64E4OL7vHYw31mlnVsf+BeeLyAZUEQtqahQ==",
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz",
"integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -1172,13 +1171,13 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.2.1.tgz",
"integrity": "sha512-G+UJeQx9AKBHRQBpmvr8T/3K5bJa485eu+4tQBxFq0KoT22+jJyzo1B50JDT9QdC1DEmWQfdKsa8ybiNWYsi0Q==",
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz",
"integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.2.1",
"@typescript-eslint/visitor-keys": "6.2.1",
"@typescript-eslint/types": "6.10.0",
"@typescript-eslint/visitor-keys": "6.10.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -1199,12 +1198,12 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.2.1.tgz",
"integrity": "sha512-iTN6w3k2JEZ7cyVdZJTVJx2Lv7t6zFA8DCrJEHD2mwfc16AEvvBWVhbFh34XyG2NORCd0viIgQY1+u7kPI0WpA==",
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz",
"integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.2.1",
"@typescript-eslint/types": "6.10.0",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
@@ -1341,6 +1340,12 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"dev": true
},
"node_modules/@vitejs/plugin-react-swc": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.2.tgz",
@@ -1728,27 +1733,28 @@
}
},
"node_modules/eslint": {
"version": "8.45.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz",
"integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
"integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.4.0",
"@eslint/eslintrc": "^2.1.0",
"@eslint/js": "8.44.0",
"@humanwhocodes/config-array": "^0.11.10",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.3",
"@eslint/js": "8.53.0",
"@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"ajv": "^6.10.0",
"@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^7.2.0",
"eslint-visitor-keys": "^3.4.1",
"espree": "^9.6.0",
"eslint-scope": "^7.2.2",
"eslint-visitor-keys": "^3.4.3",
"espree": "^9.6.1",
"esquery": "^1.4.2",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@@ -1802,22 +1808,10 @@
"eslint": ">=7"
}
},
"node_modules/eslint-visitor-keys": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz",
"integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/eslint-scope": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
"integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
"node_modules/eslint-scope": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
@@ -1830,19 +1824,22 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true,
"engines": {
"node": ">=4.0"
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/espree": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz",
"integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==",
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"dev": true,
"dependencies": {
"acorn": "^8.9.0",
@@ -1868,15 +1865,6 @@
"node": ">=0.10"
}
},
"node_modules/esquery/node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/esrecurse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
@@ -1889,7 +1877,7 @@
"node": ">=4.0"
}
},
"node_modules/esrecurse/node_modules/estraverse": {
"node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
@@ -2099,9 +2087,9 @@
}
},
"node_modules/globals": {
"version": "13.20.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
"integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
"version": "13.23.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
"integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
"dev": true,
"dependencies": {
"type-fest": "^0.20.2"
@@ -2713,9 +2701,9 @@
}
},
"node_modules/punycode": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"engines": {
"node": ">=6"
@@ -2771,29 +2759,29 @@
"dev": true
},
"node_modules/react-router": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz",
"integrity": "sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==",
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.18.0.tgz",
"integrity": "sha512-vk2y7Dsy8wI02eRRaRmOs9g2o+aE72YCx5q9VasT1N9v+lrdB79tIqrjMfByHiY5+6aYkH2rUa5X839nwWGPDg==",
"dependencies": {
"@remix-run/router": "1.7.2"
"@remix-run/router": "1.11.0"
},
"engines": {
"node": ">=14"
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.2.tgz",
"integrity": "sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==",
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.18.0.tgz",
"integrity": "sha512-Ubrue4+Ercc/BoDkFQfc6og5zRQ4A8YxSO3Knsne+eRbZ+IepAsK249XBH/XaFuOYOYr3L3r13CXTLvYt5JDjw==",
"dependencies": {
"@remix-run/router": "1.7.2",
"react-router": "6.14.2"
"@remix-run/router": "1.11.0",
"react-router": "6.18.0"
},
"engines": {
"node": ">=14"
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
@@ -2890,9 +2878,9 @@
}
},
"node_modules/rollup": {
"version": "3.26.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.2.tgz",
"integrity": "sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==",
"version": "3.29.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
"integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
"devOptional": true,
"bin": {
"rollup": "dist/bin/rollup"
@@ -3184,9 +3172,9 @@
}
},
"node_modules/typescript": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
"integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -3210,14 +3198,14 @@
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
},
"node_modules/vite": {
"version": "4.4.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.7.tgz",
"integrity": "sha512-6pYf9QJ1mHylfVh39HpuSfMPojPSKVxZvnclX1K1FyZ1PXDOcLBibdq5t1qxJSnL63ca8Wf4zts6mD8u8oc9Fw==",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",
"integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==",
"dev": true,
"dependencies": {
"esbuild": "^0.18.10",
"postcss": "^8.4.26",
"rollup": "^3.25.2"
"postcss": "^8.4.27",
"rollup": "^3.27.1"
},
"bin": {
"vite": "bin/vite.js"
@@ -3264,22 +3252,6 @@
}
}
},
"node_modules/vite-plugin-singlefile": {
"version": "0.13.5",
"resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-0.13.5.tgz",
"integrity": "sha512-y/aRGh8qHmw2f1IhaI/C6PJAaov47ESYDvUv1am1YHMhpY+19B5k5Odp8P+tgs+zhfvak6QB1ykrALQErEAo7g==",
"dev": true,
"dependencies": {
"micromatch": "^4.0.5"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"peerDependencies": {
"rollup": ">=2.79.0",
"vite": ">=3.2.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

@@ -1,9 +1,27 @@
{
"name": "ts5-obs-overlay",
"private": true,
"version": "1.3.1",
"description": "Overlay for OBS to show the current talking clients in your Teamspeak 5 Channel",
"homepage": "https://dertyp7.github.io/ts5-obs-overlay",
"private": false,
"version": "2.0.5",
"description": "Overlay for OBS to show the current talking clients in your TeamSpeak 5 Channel",
"author": "DerTyp7",
"homepage": "https://dertyp7.github.io/ts5-obs-overlay/#",
"repository": {
"type": "git",
"url": "https://github.com/DerTyp7/ts5-obs-overlay"
},
"keywords": [
"ts5",
"teamspeak",
"overlay",
"remote app",
"obs",
"typescript",
"react",
"vite"
],
"bugs": {
"url": "https://github.com/DerTyp7/ts5-obs-overlay/issues"
},
"type": "module",
"scripts": {
"dev": "vite",
@@ -15,22 +33,21 @@
"@types/node": "^20.8.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.14.2",
"react-router-dom": "^6.18.0",
"react-ts5-remote-app-api": "^1.1.1",
"sass": "^1.68.0"
},
"devDependencies": {
"@types/jest": "^29.5.2",
"@types/react": "^18.2.23",
"@types/react-dom": "^18.2.7",
"@types/react": "^18.2.35",
"@types/react-dom": "^18.2.14",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.2.1",
"@typescript-eslint/parser": "^6.9.1",
"@vitejs/plugin-react-swc": "^3.0.0",
"eslint": "^8.45.0",
"eslint": "^8.53.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"typescript": "^5.1.6",
"vite": "^4.4.7",
"vite-plugin-singlefile": "^0.13.5"
"typescript": "^5.2.2",
"vite": "^4.5.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

23
public/logo.svg Normal file
View File

@@ -0,0 +1,23 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="307.000000pt" height="339.000000pt" viewBox="0 0 307.000000 339.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,339.000000) scale(0.100000,-0.100000)"
fill="#008080" stroke="none">
<path d="M1297 3382 c-3 -4 -7 -61 -11 -127 -20 -341 -120 -616 -370 -1010
-158 -251 -306 -436 -331 -415 -7 6 -18 52 -24 103 -22 170 -77 377 -101 377
-5 0 -21 -38 -35 -86 -27 -89 -79 -213 -212 -504 -201 -438 -244 -657 -180
-901 52 -198 102 -290 232 -427 137 -145 345 -310 474 -376 l34 -18 -5 24 c-3
12 -18 63 -32 113 -64 213 -19 418 154 701 106 173 164 314 194 474 16 82 30
120 45 120 13 0 55 -59 94 -131 45 -83 81 -198 102 -329 9 -57 18 -106 20
-108 7 -7 122 231 144 298 34 104 49 202 63 403 6 103 15 186 20 185 4 -2 79
-72 167 -155 257 -247 392 -414 501 -625 97 -187 102 -205 107 -413 6 -223 -1
-265 -73 -418 -31 -65 -53 -121 -50 -124 7 -7 188 122 295 212 210 176 368
383 455 598 149 370 117 760 -98 1193 -110 220 -245 417 -434 633 l-39 45 -7
-183 c-5 -106 -14 -203 -22 -230 -14 -46 -80 -144 -99 -145 -14 -1 -59 92
-101 209 -121 342 -331 651 -579 854 -130 107 -287 203 -298 183z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,38 +1,26 @@
/* eslint-disable react-hooks/exhaustive-deps */
import "@styles/App.scss";
import { useSearchParams } from "react-router-dom";
import useTSRemoteApp, { IClient } from "react-ts5-remote-app-api";
import { Navigate, Route, Routes, useSearchParams } from "react-router-dom";
import Viewer from "./Viewer";
import Generator from "./Generator";
export default function App() {
const [searchParams] = useSearchParams();
const { clients, activeConnectionId, currentChannel } = useTSRemoteApp({
remoteAppPort: parseInt(searchParams.get("remoteAppPort") ?? "5899"),
auth: {
identifier: "de.tealfire.obs",
version: "1.3.1",
name: "TS5 OBS Overlay",
description: "A OBS overlay for TS5 by DerTyp7",
},
logging: true,
});
return (
<div className="App">
<Viewer
showChannelName={searchParams.get("showChannelName") === "true"}
hideNonTalking={searchParams.get("hideNonTalking") === "true"}
clientLimit={searchParams.get("clientLimit") ? parseInt(searchParams.get("clientLimit") ?? "0") : 0}
clients={
clients.map((client) => {
if (client.channel?.id === currentChannel?.id && client.channel.connection.id === activeConnectionId) {
return client;
}
}) as IClient[]
<Routes>
<Route
path="/"
element={
<Viewer
remoteAppPort={parseInt(searchParams.get("remoteAppPort") ?? "5899")}
showChannelName={searchParams.get("showChannelName") === "true"}
hideNonTalking={searchParams.get("hideNonTalking") === "true"}
clientLimit={searchParams.get("clientLimit") ? parseInt(searchParams.get("clientLimit") ?? "0") : 0}
/>
}
channel={currentChannel}
/>
</div>
<Route path="/generate" element={<Generator />} />
</Routes>
);
}

124
src/Generator.tsx Normal file
View File

@@ -0,0 +1,124 @@
import React, { ChangeEvent, useRef, useState, useEffect } from "react";
import "@styles/Generator.scss";
import Viewer from "./Viewer";
export default function Generator() {
// State variables
const [outputUrl, setOutputUrl] = useState(() => new URL(window.location.href).toString());
const copiedTooltipRef = useRef<HTMLDivElement>(null);
const [remoteAppPort, setRemoteAppPort] = useState(5899);
const [showChannelName, setShowChannelName] = useState(true);
const [hideNonTalking, setHideNonTalking] = useState(false);
const [clientLimit, setClientLimit] = useState(0);
// Effect to generate URL when dependencies change
useEffect(() => {
generateUrl();
}, [remoteAppPort, showChannelName, hideNonTalking, clientLimit]);
// Function to generate the output URL
function generateUrl() {
const url = new URL(window.location.href);
url.hash = "";
url.searchParams.set("remoteAppPort", remoteAppPort.toString());
url.searchParams.set("showChannelName", showChannelName.toString());
url.searchParams.set("hideNonTalking", hideNonTalking.toString());
url.searchParams.set("clientLimit", clientLimit.toString());
setOutputUrl(url.toString());
}
// Function to copy URL to clipboard
function copy() {
navigator.clipboard.writeText(outputUrl);
if (copiedTooltipRef.current) {
copiedTooltipRef.current.style.animation = "tooltipAnimation 200ms";
copiedTooltipRef.current.style.opacity = "1";
setTimeout(() => {
if (copiedTooltipRef.current) {
copiedTooltipRef.current.style.opacity = "0";
copiedTooltipRef.current.style.animation = "";
}
}, 1000);
}
}
return (
<div className="generator">
{/* Header */}
<div className="headline">
<h1>TS5-OBS-Overlay Generator</h1>
<h4>by DerTyp7</h4>
</div>
{/* Instructions */}
<div className="instructions">
<p>1. Customize your settings</p>
<p>2. Copy the generated URL</p>
<p>3. Paste the URL into the BrowserSource URL field in OBS</p>
<a href="#">Click here for detailed instructions</a>
</div>
{/* Output Section */}
<div className="output">
<p className="url">
<code>{outputUrl}</code>
</p>
<button onClick={copy} className="copy">
Copy
</button>
<div ref={copiedTooltipRef} className="copiedTooltip">
Copied!
</div>
</div>
{/* Generator Content */}
<div className="generatorContent">
{/* Configurations */}
<div className="configurations">
<h2>Configurations</h2>
<div className="options">
{/* Option Sections */}
<section>
{/* Show Channel Name Option */}
<div className="option" onClick={() => setShowChannelName(!showChannelName)}>
<input type="checkbox" checked={showChannelName} />
<label>Show channel name</label>
</div>
{/* Hide Non-Talking Clients Option */}
<div className="option" onClick={() => setHideNonTalking(!hideNonTalking)}>
<input type="checkbox" checked={hideNonTalking} />
<label>Hide non talking clients</label>
</div>
</section>
<section>
{/* Client Limit Option */}
<div className="option">
<input type="number" value={clientLimit} min={0} onChange={(e: ChangeEvent<HTMLInputElement>) => setClientLimit(parseInt(e.target.value))} />
<label>Client Limit</label>
</div>
{/* RemoteApp-Port Option */}
<div className="option">
<input type="number" value={remoteAppPort} min={0} onChange={(e: ChangeEvent<HTMLInputElement>) => setRemoteAppPort(parseInt(e.target.value))} />
<label>RemoteApp-Port</label>
</div>
</section>
</div>
</div>
{/* Preview */}
<div className="preview">
<Viewer remoteAppPort={remoteAppPort} showChannelName={showChannelName} hideNonTalking={hideNonTalking} clientLimit={clientLimit} />
</div>
</div>
</div>
);
}

View File

@@ -1,27 +1,42 @@
import "@styles/Viewer.scss";
import { IChannel, IClient } from "react-ts5-remote-app-api";
import useTSRemoteApp, { IClient } from "react-ts5-remote-app-api";
export default function Viewer({
clients,
channel,
remoteAppPort = 5899,
showChannelName = false,
hideNonTalking = false,
clientLimit = 0,
}: {
clients: IClient[] | undefined;
channel: IChannel | undefined;
remoteAppPort?: number;
showChannelName?: boolean;
hideNonTalking?: boolean;
clientLimit?: number;
}) {
const { clients, activeConnectionId, currentChannel } = useTSRemoteApp({
remoteAppPort: remoteAppPort,
auth: {
identifier: "de.tealfire.obs",
version: "2.0.5",
name: "TS5 OBS Overlay",
description: "A OBS overlay for TS5 by DerTyp7",
},
logging: true,
});
const currentClients = clients.map((client) => {
if (client.channel?.id === currentChannel?.id && client.channel.connection.id === activeConnectionId) {
return client;
}
}) as IClient[];
return (
<div className="viewer">
{showChannelName ? (
<div className="channelNameContainer">
<h3>{channel?.properties.name}</h3>
<h1>{currentChannel?.properties.name}</h1>
</div>
) : null}
{clients?.map((client, i) => {
{currentClients?.map((client, i) => {
//* Client limit
if (clientLimit != 0 && i >= clientLimit) {
return null;
@@ -29,10 +44,7 @@ export default function Viewer({
if (client) {
//* Non-talking client
if (
hideNonTalking &&
(client.properties.inputMuted || client.properties.outputMuted || client.talkStatus == 0)
) {
if (hideNonTalking && (client.properties.inputMuted || client.properties.outputMuted || client.talkStatus == 0)) {
return null;
}
@@ -73,16 +85,7 @@ export default function Viewer({
d="M88.62,54.15V64A24.69,24.69,0,0,1,64,88.62a25.26,25.26,0,0,1-8.38-1.46l-7.39,7.39A34,34,0,0,0,64,98.46,34.5,34.5,0,0,0,98.46,64V54.15a4.92,4.92,0,1,1,9.85,0V64a44.31,44.31,0,0,1-39.38,44v10.15H88.62a4.92,4.92,0,0,1,0,9.85H39.38a4.92,4.92,0,1,1,0-9.85H59.08V108A43.3,43.3,0,0,1,41,101.77L21.46,121.31a2.46,2.46,0,0,1-3.54,0L11.62,115a2.46,2.46,0,0,1,0-3.54l94.92-94.92a2.46,2.46,0,0,1,3.54,0l6.31,6.31a2.46,2.46,0,0,1,0,3.54ZM22.92,80.46A43.3,43.3,0,0,1,19.69,64V54.15a4.92,4.92,0,1,1,9.85,0V64a35.94,35.94,0,0,0,1.15,8.69ZM39.38,64V24.62a24.62,24.62,0,0,1,47.77-8.38Z"
fill="#d8d8d8"
/>
<rect
x="-5.93"
y="61.89"
width="139.87"
height="14.02"
rx="2.87"
ry="2.87"
transform="translate(-29.97 65.43) rotate(-45)"
fill="#c9070a"
/>
<rect x="-5.93" y="61.89" width="139.87" height="14.02" rx="2.87" ry="2.87" transform="translate(-29.97 65.43) rotate(-45)" fill="#c9070a" />
</g>
</svg>
) : client.properties.outputMuted ? (

View File

@@ -1,11 +1,11 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App.tsx";
import "@styles/index.scss";
import { BrowserRouter, HashRouter } from "react-router-dom";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<BrowserRouter>
ReactDOM.createRoot(document.getElementById("root")!).render(
<HashRouter>
<App />
</BrowserRouter>
</HashRouter>
);

259
src/styles/Generator.scss Normal file
View File

@@ -0,0 +1,259 @@
// Breakpoints
$breakpoint-1: 1200px;
$breakpoint-2: 790px;
$breakpoint-3: 600px;
// Tooltip animation keyframes
@keyframes tooltipAnimation {
0% {
opacity: 0;
transform: translateY(-10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.generator {
background-color: #232528;
color: #fff;
width: 100%;
display: flex;
height: 100%;
flex-direction: column;
align-items: center;
gap: 50px;
padding: 50px 0;
.headline {
text-align: center;
letter-spacing: 1.8px;
}
.instructions {
border-bottom: 2px solid #75797773;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 10px 50px;
flex-wrap: wrap;
padding-bottom: 10px;
p,
a {
font-size: 0.8rem;
}
p {
color: #b4b4b4;
}
}
.output {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
column-gap: 20px;
position: relative;
height: 50px;
.url {
white-space: nowrap;
overflow: auto;
font-weight: bold;
width: 500px;
padding: 5px 10px;
background-color: #313136;
border-radius: 5px;
text-align: center;
}
.copy {
padding: 5px 10px;
border: 2px solid #31f39973;
transition: all 100ms ease-in-out;
&:hover {
border-color: #42d486;
background-color: transparent;
color: #fff;
}
}
.copiedTooltip {
opacity: 0;
position: absolute;
right: -90px;
background-color: #31f399;
color: #202024;
padding: 5px 10px;
border-radius: 5px;
font-size: 0.8rem;
font-weight: bold;
}
}
.generatorContent {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 0 50px;
gap: 30px;
width: 100%;
.configurations {
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
flex: 1;
gap: 50px;
.options {
display: flex;
flex-direction: row;
align-items: center;
gap: 100px;
user-select: none;
section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: left;
gap: 10px;
}
.option {
display: flex;
flex-direction: row;
align-items: center;
justify-content: left;
column-gap: 10px;
width: 100%;
input {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border: 2px solid #31f399;
border-radius: 5px;
background-color: #202024;
outline: none;
transition: all 200ms ease-in-out;
position: relative;
color: #fff;
text-align: center;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
// Cross when checked styles
&:checked {
&:after {
content: "";
position: absolute;
width: 10px;
height: 2px;
background-color: #31f399;
transform: rotate(45deg);
}
&:before {
content: "";
position: absolute;
width: 10px;
height: 2px;
background-color: #31f399;
transform: rotate(-45deg);
}
&:after,
&:before {
top: 7px;
left: 3px;
}
}
cursor: pointer;
}
input[type="number"] {
width: 50px;
height: 25px;
cursor: text;
-moz-appearance: textfield;
appearance: textfield;
}
label {
cursor: pointer;
}
}
}
}
// Preview styles
.preview {
flex: 1;
border: 2px solid #31f39973;
// Viewer styles (see src/styles/Viewer.scss)
.viewer {
background-image: url("/images/viewer_example_background.png");
background-repeat: no-repeat;
background-size: cover;
min-height: 500px;
}
}
}
// Responsive styles
@media screen and (max-width: $breakpoint-1) {
.generatorContent {
flex-direction: column;
.preview {
width: 80%;
}
}
}
@media screen and (max-width: $breakpoint-2) {
.output {
.url {
width: 300px;
}
}
.generatorContent {
.preview {
width: 100%;
}
}
}
@media screen and (max-width: $breakpoint-3) {
.output {
.url {
width: 200px;
}
.generatorContent {
.configurations {
.options {
flex-direction: column;
gap: 30px;
}
}
}
}
}
}

View File

@@ -1,22 +1,28 @@
//* Viewer styles
// this file contains styles for the viewer component
// styles for the viewer component should not be modified somewhere else
.viewer {
display: flex;
flex-direction: column;
gap: 0 0;
padding: 1rem;
padding: 0.5rem;
h1,
p {
background-color: #2f313680;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 20ch;
user-select: none;
}
.channelNameContainer {
display: flex;
align-items: center;
margin-bottom: 20px;
h3 {
font-size: 1.7rem;
font-weight: 500;
margin: 0;
background-color: rgba(47, 49, 54, 0.5);
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
color: white;
}
}
.client {
@@ -26,23 +32,16 @@
align-items: center;
margin: 0.5rem 0;
// icon styles
svg {
width: 2.8rem;
height: 2.8rem;
width: 2.1rem;
aspect-ratio: 1/1;
margin-right: 0.5rem;
}
p {
margin: 0;
color: white;
background-color: rgba(47, 49, 54, 0.5);
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
// ellipsis after 22 characters
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 20ch;
// client name styles
p {
font-size: 1.4rem;
}
}
}

View File

@@ -1,5 +1,101 @@
// Reset styles for all elements
* {
font-family: Arial, Helvetica, sans-serif;
font-weight: bold;
font-size: 3rem;
font-size: 1rem;
margin: 0;
padding: 0;
box-sizing: border-box;
}
// Set up basic styles for the entire page
body,
html {
min-height: 100vh;
min-width: 100%;
display: flex;
overflow-x: hidden;
color: #fff;
}
// Ensure the root element takes up the full viewport
#root {
height: 100%;
min-width: 100%;
}
// Headline styles
h1 {
font-size: 1.8rem;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.2rem;
}
h4 {
font-size: 1.1rem;
}
// Common styles for heading elements
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: bold;
}
// Text styles
a {
color: #3abe78;
font-weight: bold;
text-decoration: none;
transition: all 100ms ease-in-out;
&:hover {
color: #31f399;
}
}
// Button styles
button {
background-color: #202024;
color: #fff;
font-weight: bold;
border: 2px solid #31f399;
border-radius: 5px;
padding: 10px 20px;
cursor: pointer;
transition: all 300ms ease-in-out;
&:hover {
background-color: #42d486;
color: #202024;
}
}
// Custom dark-themed scrollbar
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
::-webkit-scrollbar-track {
background: #363638;
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: #31f39973;
border-radius: 10px;
&:hover {
background: #48ee95;
}
}

View File

@@ -18,7 +18,8 @@
},
/* Bundler mode */
"moduleResolution": "bundler",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true,
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,

View File

@@ -1,6 +1,5 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import { viteSingleFile } from "vite-plugin-singlefile"
import path from 'path';
// https://vitejs.dev/config/
@@ -21,5 +20,5 @@ export default defineConfig({
outDir: 'dist',
emptyOutDir: true,
},
plugins: [react(), viteSingleFile({ useRecommendedBuildConfig: false })],
plugins: [react()],
})