refactor: 💥 v2.0.0 Beta.
BREAKING CHANGE: ⚠ Please avoid using v1 and v2 simultaneously. This version is stable enough to be released but is still under heavy development, **You may encounter bugs, if you encounter any, you can uninstall this version and go back to v1.** For users: - Users can now share presets and vote for them. - New installer/uninstaller with customizable installation path - #66 Add multi-language: English, Français, 简体中文, Deutsch, Türkçe. - Presets triggers are now in preset tabs with more options - Added splash screen - Better night mode - Release tab now open browser - Notifications are now handled within the app/system accordingly to the app's minimized state For developers: - Now using React and TypeScript - Switched from npm to yarn: https://github.com/electron-userland/electron-builder/issues/1147#issuecomment-276284477 - Updated Continuous Delivery Contributors: @rikoopa @Jamiexhz @Silvaburn#3669
3
.env
Normal file
@ -0,0 +1,3 @@
|
||||
BROWSER=none
|
||||
REACT_APP_VERSION=${npm_package_version}-dev
|
||||
REACT_APP_SERVER_ENDPOINT=https://api.ryzencontroller.localhost
|
2
.env.production
Normal file
@ -0,0 +1,2 @@
|
||||
REACT_APP_VERSION=$npm_package_version
|
||||
REACT_APP_SERVER_ENDPOINT=https://api.ryzencontroller.com
|
27
.gitignore
vendored
@ -1,3 +1,24 @@
|
||||
node_modules
|
||||
release-builds
|
||||
installer-builds
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
/dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
219
.gitlab-ci.yml
@ -1,11 +1,12 @@
|
||||
include:
|
||||
- template: Dependency-Scanning.gitlab-ci.yml
|
||||
- template: SAST.gitlab-ci.yml
|
||||
- template: Code-Quality.gitlab-ci.yml
|
||||
|
||||
stages:
|
||||
- test
|
||||
- install
|
||||
- package
|
||||
- build
|
||||
- installer
|
||||
- release-note
|
||||
- release
|
||||
@ -16,31 +17,10 @@ stages:
|
||||
|
||||
code_quality:
|
||||
stage: test
|
||||
image: docker:stable
|
||||
allow_failure: true
|
||||
services:
|
||||
- docker:stable-dind
|
||||
variables:
|
||||
DOCKER_DRIVER: overlay2
|
||||
script:
|
||||
- |
|
||||
if ! docker info &>/dev/null; then
|
||||
if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
|
||||
export DOCKER_HOST='tcp://localhost:2375'
|
||||
fi
|
||||
fi
|
||||
- docker run
|
||||
--env CODECLIMATE_CODE="$PWD"
|
||||
--volume "$PWD":/code
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock
|
||||
--volume /tmp/cc:/tmp/cc
|
||||
--entrypoint "/bin/sh" codeclimate/codeclimate -c "/usr/src/app/bin/codeclimate engines:install && /usr/src/app/bin/codeclimate analyze"
|
||||
only:
|
||||
- branches
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
except:
|
||||
variables:
|
||||
- $CODE_QUALITY_DISABLED
|
||||
except: []
|
||||
|
||||
dependency_scanning:
|
||||
stage: test
|
||||
@ -56,21 +36,6 @@ sast:
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
except: []
|
||||
|
||||
js_syntax:
|
||||
stage: test
|
||||
only:
|
||||
- branches
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
image: node:carbon
|
||||
tags: [ docker ]
|
||||
cache: {}
|
||||
dependencies: []
|
||||
before_script:
|
||||
- npm install -g acorn
|
||||
- rm -rf node_modules
|
||||
script:
|
||||
- find . -iname "*.js" -print0 | xargs -0 -I % sh -c 'echo "%"; acorn --ecma6 --silent "%"'
|
||||
|
||||
release-check:
|
||||
tags: [ docker ]
|
||||
image: registry.gitlab.com/juhani/go-semrel-gitlab:v0.20.4
|
||||
@ -78,9 +43,9 @@ release-check:
|
||||
- branches
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
stage: test
|
||||
dependencies: []
|
||||
script:
|
||||
- release next-version > .next_version
|
||||
- cat .next_version
|
||||
artifacts:
|
||||
paths:
|
||||
- .next_version
|
||||
@ -95,15 +60,14 @@ node:
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
stage: install
|
||||
tags: [ docker ]
|
||||
image: electronuserland/builder:wine-mono
|
||||
dependencies:
|
||||
image: storm1er/electron-builder-wine-dubnium:1.0.2
|
||||
needs:
|
||||
- release-check
|
||||
script:
|
||||
- VERSION=`cat .next_version`
|
||||
- npm version --no-git-tag-version ${VERSION}
|
||||
- npm install
|
||||
- cp vendor/7z/7z.exe node_modules/electron-winstaller/vendor/7z.exe
|
||||
- cp vendor/7z/7z.dll node_modules/electron-winstaller/vendor/7z.dll
|
||||
- yarn config set version-git-tag false
|
||||
- yarn version --new-version "${VERSION}"
|
||||
- yarn install --frozen-lockfile
|
||||
cache:
|
||||
paths:
|
||||
- node_modules/
|
||||
@ -111,128 +75,66 @@ node:
|
||||
paths:
|
||||
- node_modules/
|
||||
- package.json
|
||||
- package-lock.json
|
||||
|
||||
###############################################################################
|
||||
## PACKAGE
|
||||
## BUILD
|
||||
###############################################################################
|
||||
|
||||
win32:
|
||||
build:
|
||||
only:
|
||||
- branches
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
stage: package
|
||||
stage: build
|
||||
tags: [ docker ]
|
||||
image: electronuserland/builder:wine-mono
|
||||
image: storm1er/electron-builder-wine-dubnium:1.0.2
|
||||
script:
|
||||
- rm -rf vendor/*
|
||||
- npm run-script package-win32
|
||||
- yarn build
|
||||
artifacts:
|
||||
paths:
|
||||
- release-builds/
|
||||
dependencies:
|
||||
- node
|
||||
|
||||
linux:
|
||||
only:
|
||||
- branches
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
stage: package
|
||||
tags: [ docker ]
|
||||
image: electronuserland/builder:wine-mono
|
||||
script:
|
||||
- rm -rf bin/*
|
||||
- npm run-script package-linux
|
||||
artifacts:
|
||||
paths:
|
||||
- release-builds/
|
||||
dependencies:
|
||||
- build/
|
||||
needs:
|
||||
- node
|
||||
|
||||
###############################################################################
|
||||
## INSTALLER
|
||||
###############################################################################
|
||||
|
||||
exe-installer:
|
||||
installers:
|
||||
only:
|
||||
- branches
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
stage: installer
|
||||
tags: [ docker ]
|
||||
image: docker:stable
|
||||
services:
|
||||
- docker:stable-dind
|
||||
variables:
|
||||
DOCKER_DRIVER: overlay2
|
||||
image: storm1er/electron-builder-wine-dubnium:1.0.2
|
||||
script:
|
||||
- |
|
||||
if ! docker info &>/dev/null; then
|
||||
if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
|
||||
export DOCKER_HOST='tcp://localhost:2375'
|
||||
fi
|
||||
fi
|
||||
- mkdir installer-builds
|
||||
- chmod 777 installer-builds
|
||||
- docker run
|
||||
--env ELECTRON_CACHE="/root/.cache/electron"
|
||||
--env ELECTRON_BUILDER_CACHE="/root/.cache/electron-builder"
|
||||
-v ${PWD}:/project
|
||||
-v ~/.cache/electron:/root/.cache/electron
|
||||
-v ~/.cache/electron-builder:/root/.cache/electron-builder
|
||||
electronuserland/builder:wine-mono /bin/bash -c "npm run-script build-exe"
|
||||
- ls installer-builds/RyzenControllerInstaller.exe
|
||||
- echo "https://gitlab.com/ryzen-controller-team/ryzen-controller/-/jobs/${CI_JOB_ID}/artifacts/browse/installer-builds/" > .exe_link
|
||||
- export ELECTRON_CACHE=`pwd`/.cache/electron
|
||||
- export ELECTRON_BUILDER_CACHE=`pwd`/.cache/builder
|
||||
- mkdir -p `pwd`/.cache/electron
|
||||
- mkdir -p `pwd`/.cache/builder
|
||||
- yarn dist-pack:all
|
||||
- mkdir dist/win
|
||||
- mkdir dist/rpm
|
||||
- mkdir dist/deb
|
||||
- mv dist/*.exe dist/win/
|
||||
- mv dist/*.rpm dist/rpm/
|
||||
- mv dist/*.deb dist/deb/
|
||||
- echo "https://gitlab.com/ryzen-controller-team/ryzen-controller/-/jobs/${CI_JOB_ID}/artifacts/browse/dist/win/" > .exe_link
|
||||
- echo "https://gitlab.com/ryzen-controller-team/ryzen-controller/-/jobs/${CI_JOB_ID}/artifacts/browse/dist/rpm/" > .rpm_link
|
||||
- echo "https://gitlab.com/ryzen-controller-team/ryzen-controller/-/jobs/${CI_JOB_ID}/artifacts/browse/dist/deb/" > .deb_link
|
||||
cache:
|
||||
paths:
|
||||
- .cache
|
||||
artifacts:
|
||||
paths:
|
||||
- installer-builds/
|
||||
- dist/win
|
||||
- dist/rpm
|
||||
- dist/deb
|
||||
- .exe_link
|
||||
dependencies:
|
||||
- node
|
||||
- win32
|
||||
|
||||
deb-installer:
|
||||
only:
|
||||
- branches
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
stage: installer
|
||||
tags: [ docker ]
|
||||
image: debian:stretch
|
||||
before_script:
|
||||
- apt update -y
|
||||
- apt install fakeroot curl -y
|
||||
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash
|
||||
- export NVM_DIR="$HOME/.nvm"
|
||||
- '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"'
|
||||
- nvm install 10.15.1
|
||||
- nvm use 10.15.1
|
||||
- echo "https://gitlab.com/ryzen-controller-team/ryzen-controller/-/jobs/${CI_JOB_ID}/artifacts/browse/installer-builds/installers/" > .deb_link
|
||||
script:
|
||||
- npm run-script build-deb
|
||||
artifacts:
|
||||
paths:
|
||||
- installer-builds/
|
||||
- .deb_link
|
||||
dependencies:
|
||||
- node
|
||||
- linux
|
||||
|
||||
rpm-installer:
|
||||
only:
|
||||
- branches
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
stage: installer
|
||||
tags: [ docker ]
|
||||
image: electronuserland/builder:wine-mono
|
||||
script:
|
||||
- npm run-script build-rpm
|
||||
- echo "https://gitlab.com/ryzen-controller-team/ryzen-controller/-/jobs/${CI_JOB_ID}/artifacts/browse/installer-builds/installers/" > .rpm_link
|
||||
artifacts:
|
||||
paths:
|
||||
- installer-builds/
|
||||
- .rpm_link
|
||||
dependencies:
|
||||
- .deb_link
|
||||
needs:
|
||||
- node
|
||||
- linux
|
||||
- build
|
||||
|
||||
###############################################################################
|
||||
## RELEASE-NOTE
|
||||
@ -242,8 +144,8 @@ create-rpm-link:
|
||||
tags: [ docker ]
|
||||
image: alpine
|
||||
stage: release-note
|
||||
dependencies:
|
||||
- rpm-installer
|
||||
needs:
|
||||
- installers
|
||||
- release-check
|
||||
only:
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
@ -256,10 +158,12 @@ create-rpm-link:
|
||||
- curl -H "Content-Type:application/json"
|
||||
-X POST
|
||||
-H "Authorization:Bearer ${BL_TOKEN}"
|
||||
-d "{\"title\":\"${VERSION} rpm\",\"tags\":[\"RC_rpm\",\"RC_${VERSION_ESCAPED}\",\"RC\"],\"long_url\":\"${URL}\",\"group_guid\":\"Bj4p9bnMV93\"}"
|
||||
-d "{\"title\":\"${VERSION} rpm\",\"tags\":[\"RC_rpm\",\"RC_${VERSION_ESCAPED}\",\"RC\",\"tmp\"],\"long_url\":\"${URL}\",\"group_guid\":\"Bj4p9bnMV93\"}"
|
||||
"https://api-ssl.bitly.com/v4/bitlinks" > .rpm_res.json
|
||||
- cat .rpm_res.json | jq -e -r .link > .rpm_short_link
|
||||
- cat .rpm_short_link
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- .rpm_short_link
|
||||
- .rpm_res.json
|
||||
@ -268,8 +172,8 @@ create-deb-link:
|
||||
tags: [ docker ]
|
||||
image: alpine
|
||||
stage: release-note
|
||||
dependencies:
|
||||
- deb-installer
|
||||
needs:
|
||||
- installers
|
||||
- release-check
|
||||
only:
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
@ -282,10 +186,12 @@ create-deb-link:
|
||||
- curl -H "Content-Type:application/json"
|
||||
-X POST
|
||||
-H "Authorization:Bearer ${BL_TOKEN}"
|
||||
-d "{\"title\":\"${VERSION} deb\",\"tags\":[\"RC_deb\",\"RC_${VERSION_ESCAPED}\",\"RC\"],\"long_url\":\"${URL}\",\"group_guid\":\"Bj4p9bnMV93\"}"
|
||||
-d "{\"title\":\"${VERSION} deb\",\"tags\":[\"RC_deb\",\"RC_${VERSION_ESCAPED}\",\"RC\",\"tmp\"],\"long_url\":\"${URL}\",\"group_guid\":\"Bj4p9bnMV93\"}"
|
||||
"https://api-ssl.bitly.com/v4/bitlinks" > .deb_res.json
|
||||
- cat .deb_res.json | jq -e -r .link > .deb_short_link
|
||||
- cat .deb_short_link
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- .deb_short_link
|
||||
- .deb_res.json
|
||||
@ -294,8 +200,8 @@ create-exe-link:
|
||||
tags: [ docker ]
|
||||
image: alpine
|
||||
stage: release-note
|
||||
dependencies:
|
||||
- exe-installer
|
||||
needs:
|
||||
- installers
|
||||
- release-check
|
||||
only:
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
@ -308,10 +214,12 @@ create-exe-link:
|
||||
- curl -H "Content-Type:application/json"
|
||||
-X POST
|
||||
-H "Authorization:Bearer ${BL_TOKEN}"
|
||||
-d "{\"title\":\"${VERSION} exe\",\"tags\":[\"RC_exe\",\"RC_${VERSION_ESCAPED}\",\"RC\"],\"long_url\":\"${URL}\",\"group_guid\":\"Bj4p9bnMV93\"}"
|
||||
-d "{\"title\":\"${VERSION} exe\",\"tags\":[\"RC_exe\",\"RC_${VERSION_ESCAPED}\",\"RC\",\"tmp\"],\"long_url\":\"${URL}\",\"group_guid\":\"Bj4p9bnMV93\"}"
|
||||
"https://api-ssl.bitly.com/v4/bitlinks" > .exe_res.json
|
||||
- cat .exe_res.json | jq -e -r .link > .exe_short_link
|
||||
- cat .exe_short_link
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- .exe_short_link
|
||||
- .exe_res.json
|
||||
@ -338,7 +246,7 @@ publish:
|
||||
only:
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
stage: release
|
||||
dependencies:
|
||||
needs:
|
||||
- release-check
|
||||
- node
|
||||
- create-rpm-link
|
||||
@ -347,7 +255,18 @@ publish:
|
||||
- update-changelog
|
||||
script:
|
||||
- VERSION=`cat .next_version`
|
||||
- release --ci-commit-tag ${VERSION} commit-and-tag CHANGELOG.md package.json package-lock.json
|
||||
- release --ci-commit-tag ${VERSION} commit-and-tag CHANGELOG.md package.json
|
||||
- release --ci-commit-tag ${VERSION} add-download-link -d "Debian installer (and variant)" -n "ryzencontroller_${VERSION}_amd64.deb" -u "`cat .deb_short_link`"
|
||||
- release --ci-commit-tag ${VERSION} add-download-link -d "Redhat installer (and variant)" -n "ryzencontroller_${VERSION}.amd64.rpm" -u "`cat .rpm_short_link`"
|
||||
- release --ci-commit-tag ${VERSION} add-download-link -d "Windows installer" -n "RyzenControllerInstaller.exe" -u "`cat .exe_short_link`"
|
||||
|
||||
no-publish:
|
||||
tags: [ docker ]
|
||||
image: registry.gitlab.com/juhani/go-semrel-gitlab:v0.20.4
|
||||
only:
|
||||
- schedules@ryzen-controller-team/ryzen-controller
|
||||
stage: release
|
||||
when: on_failure
|
||||
dependencies: []
|
||||
script:
|
||||
- echo "Publish step not needed."
|
||||
|
15
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Chrome",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}/src",
|
||||
"sourceMapPathOverrides": {
|
||||
"webpack:///src/*": "${webRoot}/*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
10
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
{ "language": "typescript", "autoFix": true },
|
||||
{ "language": "typescriptreact", "autoFix": true }
|
||||
],
|
||||
"prettier.packageManager": "yarn",
|
||||
"editor.tabSize": 2
|
||||
}
|
37
.vscode/tasks.json
vendored
@ -1,37 +0,0 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "package-win32",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "package-linux",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build-exe",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build-deb",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build-rpm",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
@ -89,4 +89,4 @@
|
||||
- Wider window as there is a new tab. (67fc8ae650218cc2b7a7305b4a9d5835f581041b)
|
||||
- Avoid Sentry noise by creating a random user id. (1a1990b9ad4ed670525e0ffb6f337104c0558bff)
|
||||
- Better build-exe alive message. (56c9d827bbbc58104ab931ad92b26226d4564aa6)
|
||||
- Release tab content is now appearing after opening windows from minized start. (b079a535dabe5667d586da0a537ed0c0a934339b)
|
||||
- Release tab content is now appearing after opening windows from minized start. (b079a535dabe5667d586da0a537ed0c0a934339b)
|
87
CONTRIBUTING.md
Normal file
@ -0,0 +1,87 @@
|
||||
# How to contribute
|
||||
|
||||
## 1. For everyone
|
||||
|
||||
You can help us in many ways:
|
||||
|
||||
Without beeing a developer
|
||||
- By [opening an issue](https://gitlab.com/ryzen-controller-team/ryzen-controller/issues/new) when:
|
||||
- you found a bug
|
||||
- have a suggestion
|
||||
- or just to tell us thanks
|
||||
- By testing new features an give feedbacks
|
||||
- on merge requests labeled `[to-test]`, take a look at the pipelines, you'll see some some green "bubbles". The one named _installers_ allow you to download artifacts.
|
||||
|
||||
If you have some developer/IT knowledge
|
||||
- By doing code reviews:
|
||||
- on merge requests labeled `[to-review]`, by commenting the code.
|
||||
- on the [whole codebase](https://gitlab.com/ryzen-controller-team/ryzen-controller/tree/v2-react/src), by [opening an issue](https://gitlab.com/ryzen-controller-team/ryzen-controller/issues/new)
|
||||
|
||||
If you want to be a part of Ryzen Controller Team, ask to become a member by [opening an issue](https://gitlab.com/ryzen-controller-team/ryzen-controller/issues/new) ;)
|
||||
|
||||
## 2. To members or Ryzen Controller Team
|
||||
|
||||
### 2.1. Git workflow
|
||||
|
||||
This project is fast forward commit only. It means the [commit graph](https://gitlab.com/ryzen-controller-team/ryzen-controller/-/network/master) will stay on one line. It also means that every merge request must be up-to-date with the target branch before beeing merged.
|
||||
|
||||
This allow us to avoid any side effect due to merge commits.
|
||||
|
||||
A little article about this: [A git workflow using rebase](https://medium.com/singlestone/a-git-workflow-using-rebase-1b1210de83e5)
|
||||
|
||||
#### 2.1.1. Quick example
|
||||
|
||||
```bash
|
||||
git clone git@gitlab.com:ryzen-controller-team/ryzen-controller.git ryzencontroller
|
||||
cd ryzencontroller
|
||||
git checkout -b my-bugfix
|
||||
# Doing stuff with files
|
||||
git add .
|
||||
git commit -m "fix: Ensure people know what to do."
|
||||
git push origin my-bugfix
|
||||
```
|
||||
Then you'll see a like to create a merge request
|
||||
|
||||
### 2.2. Automation
|
||||
|
||||
This project contains a [`.gitlab-ci.yml`](https://gitlab.com/ryzen-controller-team/ryzen-controller/blob/master/.gitlab-ci.yml) file.
|
||||
|
||||
This file allow us to auotmatically test, build, package and publish Ryzen Controller app.
|
||||
|
||||
It's executed for each merge request update and each week on the master branch.
|
||||
|
||||
Here a list of what's done by stage.
|
||||
|
||||
#### 2.2.1. Tests
|
||||
|
||||
Some jobs to enhance gitlab features like [code quality review](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html), [security dashboard](https://gitlab.com/ryzen-controller-team/ryzen-controller/security/dashboard/), [Static Application Security Testing](https://docs.gitlab.com/ee/user/application_security/sast/).
|
||||
|
||||
And a release-check ([sample job](https://gitlab.com/ryzen-controller-team/ryzen-controller/-/jobs/400164480)), this analyse commit list to detect if a new version must be released.
|
||||
|
||||
#### 2.2.2. Install
|
||||
|
||||
`yarn install --frozen-lockfile`: https://yarnpkg.com/lang/en/docs/cli/install/#toc-yarn-install-frozen-lockfile
|
||||
|
||||
Don’t generate a yarn.lock lockfile and fail if an update is needed.
|
||||
|
||||
#### 2.2.3. Build
|
||||
|
||||
`yarn build`: Compile projects files into `build/` folder, production ready.
|
||||
|
||||
#### 2.2.4. Installers
|
||||
|
||||
`yarn dist-pack-all`: Create a electron package and the installers that goes with it.
|
||||
|
||||
#### 2.2.5. Release note
|
||||
|
||||
Only executed on scheduled pipelines
|
||||
|
||||
`create-rpm-link`, `create-deb-link`, `create-exe-link`: Create bitly links so we can count downloads =)
|
||||
`update-changelog`: Update [CHANGELOG.md](https://gitlab.com/ryzen-controller-team/ryzen-controller/blob/master/CHANGELOG.md) files, will be used for the release description.
|
||||
|
||||
#### 2.2.6. Release
|
||||
|
||||
Only executed on scheduled pipelines
|
||||
|
||||
`publish`: Will create a new [release](https://gitlab.com/ryzen-controller-team/ryzen-controller/-/releases)
|
||||
`no-publish`: Avoid the red flag when no release has to be published.
|
68
CRA-README.md
Normal file
@ -0,0 +1,68 @@
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.<br />
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.<br />
|
||||
You will also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.<br />
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.<br />
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.<br />
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
|
||||
### Code Splitting
|
||||
|
||||
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
|
||||
|
||||
### Analyzing the Bundle Size
|
||||
|
||||
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
|
||||
|
||||
### Making a Progressive Web App
|
||||
|
||||
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
|
||||
|
||||
### Deployment
|
||||
|
||||
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
|
||||
|
||||
### `npm run build` fails to minify
|
||||
|
||||
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
|
92
README.md
@ -1,6 +1,6 @@
|
||||
# Ryzen controller
|
||||
|
||||
*_I'm looking for maintainers and reviewers, if you know html/js and have some spare times, please open an issue or ping `@storm1er#0376` on [discord](https://discord.gg/EahayUv)._*
|
||||
*_I'm looking for maintainers and reviewers, if you know Electron/React and have some spare times, please open an issue or ping `@storm1er#0376` on [discord](https://discord.gg/EahayUv)._*
|
||||
|
||||
Thanks to https://github.com/FlyGoat/RyzenAdj and his author, ryzenadj.exe is included (windows only for now).
|
||||
|
||||
@ -9,33 +9,25 @@ Thanks to https://github.com/FlyGoat/RyzenAdj and his author, ryzenadj.exe is in
|
||||
- [Windows](#windows)
|
||||
- [Debian like](#debian-like)
|
||||
- [Redhat like](#redhat-like)
|
||||
- [Troubleshoot, Q&A](#troubleshoot-qa)
|
||||
- [Development](#development)
|
||||
- [Pre-requisite](#pre-requisite)
|
||||
- [Dev install](#dev-install)
|
||||
- [Building binaries](#building-binaries)
|
||||
- [Building binaries and installers](#building-binaries-and-installers)
|
||||
- [Using Windows](#using-windows)
|
||||
- [Using Linux](#using-linux)
|
||||
- [Building installers](#building-installers)
|
||||
- [For Windows](#for-windows)
|
||||
- [For Debian like](#for-debian-like)
|
||||
- [For Redhat like](#for-redhat-like)
|
||||
|
||||
## What's this?
|
||||
|
||||
![Ryzen Controller ScreenCast](vendor/screencast.webm)
|
||||
|
||||
- It's a little Ryzen Master for laptops.
|
||||
- Works best on 2xxx Ryzen series (3xxx series is experimental)
|
||||
- More videos:
|
||||
- https://www.youtube.com/watch?v=VYWiKQkT-8o
|
||||
- https://www.youtube.com/watch?v=fHnO_4k-Cs4
|
||||
|
||||
## Installation
|
||||
|
||||
### Windows
|
||||
|
||||
- Go to [release page](https://gitlab.com/le.storm1er/ryzen-controller/releases)
|
||||
- Download the latest `RyzenControllerInstaller.exe`
|
||||
- Download the latest `Ryzen Controller Setup X.X.X.exe`
|
||||
- Enjoy!
|
||||
|
||||
### Debian like
|
||||
@ -43,8 +35,8 @@ Thanks to https://github.com/FlyGoat/RyzenAdj and his author, ryzenadj.exe is in
|
||||
- Go to [RyzenAdj](https://github.com/FlyGoat/RyzenAdj) repo.
|
||||
- Download and build as explained in [Build requirements](https://github.com/FlyGoat/RyzenAdj#build-requirements).
|
||||
- Go to Ryzen Controller's [release page](https://gitlab.com/le.storm1er/ryzen-controller/releases)
|
||||
- Download the latest `ryzencontroller_VERSION_ARCH.deb` file.
|
||||
- `sudo dpkg -i ryzencontroller_VERSION_ARCH.deb`
|
||||
- Download the latest `ryzen-controller_X.X.X_ARCH.deb` file.
|
||||
- `sudo dpkg -i ryzen-controller_X.X.X_ARCH.deb`
|
||||
- Launch with `sudo ryzencontroller`
|
||||
- Set the path to your freshly builded `ryzenadj` binary into the "settings" tab.
|
||||
- Enjoy!
|
||||
@ -54,19 +46,39 @@ Thanks to https://github.com/FlyGoat/RyzenAdj and his author, ryzenadj.exe is in
|
||||
- Go to [RyzenAdj](https://github.com/FlyGoat/RyzenAdj) repo.
|
||||
- Download and build as explained in [Build requirements](https://github.com/FlyGoat/RyzenAdj#build-requirements).
|
||||
- Go to Ryzen Controller's [release page](https://gitlab.com/le.storm1er/ryzen-controller/releases)
|
||||
- Download the latest `ryzencontroller_VERSION_ARCH.rpm` file.
|
||||
- `sudo rpm -u ryzencontroller_VERSION_ARCH.rpm`
|
||||
- Download the latest `ryzen-controller-X.X.X.ARCH.rpm` file.
|
||||
- `sudo rpm -u ryzen-controller-X.X.X.ARCH.rpm`
|
||||
- Launch with `sudo ryzencontroller`
|
||||
- Set the path to your freshly builded `ryzenadj` binary into the "settings" tab.
|
||||
- Enjoy!
|
||||
|
||||
## Troubleshoot, Q&A
|
||||
|
||||
> I'm getting an error when installing ryzen controller on linux
|
||||
|
||||
_You may need to install `smartmontools` & `lm-sensors` packages to allow Ryzen Controller to work well._
|
||||
```bash
|
||||
# Install the app
|
||||
sudo dpkg -i ryzen-controller_x.x.x_amd64.deb
|
||||
# If you get error about missing dependencies
|
||||
sudo apt-get -f install
|
||||
# To ensure correct temperature and others sys-info
|
||||
sudo apt-get -y smartmontools lm-sensors
|
||||
```
|
||||
|
||||
> Why yarn?
|
||||
|
||||
_See https://github.com/electron-userland/electron-builder/issues/1147#issuecomment-276284477_
|
||||
|
||||
|
||||
|
||||
## Development
|
||||
|
||||
**THIS PART IS ONLY FOR DEVELOPMENT PURPOSE, IF YOU JUST WANT TO USE RYZEN CONTROLLER, SEE THE [INSTALLATION](#installation) PART.**
|
||||
|
||||
### Pre-requisite
|
||||
|
||||
- NodeJS v10.15.1 or newer.
|
||||
- NodeJS v10.18.0 or newer.
|
||||
- About building dependencies:
|
||||
- No dependencies for windows installer
|
||||
- See [electron-installer-debian requirements](https://github.com/electron-userland/electron-installer-debian#requirements)
|
||||
@ -75,49 +87,27 @@ Thanks to https://github.com/FlyGoat/RyzenAdj and his author, ryzenadj.exe is in
|
||||
### Dev install
|
||||
|
||||
```bash
|
||||
$> cd project
|
||||
$> npm install
|
||||
$> npm start
|
||||
cd project
|
||||
yarn install --frozen-lockfile # Please commit any change in yarn.lock/package.json in separated merge request
|
||||
yarn start # You may want to look at "start:*" scripts in package.json
|
||||
```
|
||||
|
||||
### Building binaries
|
||||
### Building binaries and installers
|
||||
|
||||
#### Using Windows
|
||||
|
||||
```bash
|
||||
$> cd project
|
||||
$> npm run-script package-win32
|
||||
cd project
|
||||
yarn docker # may not be needed, depends on your machine
|
||||
yarn clean # You may want to look at "clean:*" scripts in package.json
|
||||
yarn dist-pack-win # You may want to look at "dist-pack:*" scripts in package.json
|
||||
```
|
||||
|
||||
#### Using Linux
|
||||
|
||||
```bash
|
||||
$> cd project
|
||||
$> npm run-script package-linux
|
||||
```
|
||||
|
||||
### Building installers
|
||||
|
||||
#### For Windows
|
||||
|
||||
```bash
|
||||
$> cd project
|
||||
$> npm run-script package-win32
|
||||
$> npm run-script build-exe
|
||||
```
|
||||
|
||||
#### For Debian like
|
||||
|
||||
```bash
|
||||
$> cd project
|
||||
$> npm run-script package-linux
|
||||
$> npm run-script build-deb
|
||||
```
|
||||
|
||||
#### For Redhat like
|
||||
|
||||
```bash
|
||||
$> cd project
|
||||
$> npm run-script package-linux
|
||||
$> npm run-script build-rpm
|
||||
cd project
|
||||
yarn docker # may not be needed, depends on your machine
|
||||
yarn clean # You may want to look at "clean:*" scripts in package.json
|
||||
yarn dist-pack-linux # You may want to look at "dist-pack:*" scripts in package.json
|
||||
```
|
||||
|
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 13 KiB |
BIN
assets/icon.ico
Before Width: | Height: | Size: 50 KiB |
24
docker/Dockerfile
Normal file
@ -0,0 +1,24 @@
|
||||
# storm1er/electron-builder-wine-dubnium:1.0.2
|
||||
|
||||
FROM electronuserland/builder:wine
|
||||
|
||||
RUN rm `which node`
|
||||
RUN rm `which npm`
|
||||
|
||||
ENV NODE_VERSION=10.18.0
|
||||
RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
|
||||
|
||||
RUN echo 'export NVM_DIR="$HOME/.nvm"' >> "$HOME/.bashrc"
|
||||
RUN echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm' >> "$HOME/.bashrc"
|
||||
RUN echo '[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion" # This loads nvm bash_completion' >> "$HOME/.bashrc"
|
||||
|
||||
RUN bash -c 'source $HOME/.nvm/nvm.sh && \
|
||||
nvm install $NODE_VERSION && \
|
||||
nvm use $NODE_VERSION && \
|
||||
nvm alias default $NODE_VERSION'
|
||||
|
||||
ENV NVM_DIR /root/.nvm
|
||||
ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
|
||||
ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
|
||||
|
||||
RUN curl --compressed -o- -L https://yarnpkg.com/install.sh | bash
|
407
index-dark.html
@ -1,407 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title id="title">Ryzen Controller</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="./node_modules/uikit/dist/css/uikit.min.css" />
|
||||
<script src="https://browser.sentry-cdn.com/5.6.1/bundle.min.js" crossorigin="anonymous"></script>
|
||||
<script src="./node_modules/uikit/dist/js/uikit.min.js"></script>
|
||||
<script src="./node_modules/uikit/dist/js/uikit-icons.min.js"></script>
|
||||
<script src="https://kit.fontawesome.com/c7184454f2.js" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
background-color: black;
|
||||
color: white;
|
||||
border: 1.25px solid #222;
|
||||
min-width: 800px;
|
||||
height: 100%;
|
||||
-webkit-app-region: drag;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#titlebar {
|
||||
display: block;
|
||||
position: fixed;
|
||||
height: 32px;
|
||||
width: calc(100% - 2px);
|
||||
z-index: 9999999999;
|
||||
/*Compensate for body 1px border*/
|
||||
}
|
||||
|
||||
#main {
|
||||
height: calc(100% - 29px);
|
||||
margin-top: 29px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
background-color: #222;
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
-webkit-app-region: drag;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
nav #titleShown {
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
color: white;
|
||||
font-size: 1em;
|
||||
float: left;
|
||||
padding: 0 0 0 1em;
|
||||
line-height: 32px;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
nav #buttons {
|
||||
float: right;
|
||||
min-width: 150px;
|
||||
height: 100%;
|
||||
background-color: #333;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
#buttons #minimize,
|
||||
#buttons #maximize,
|
||||
#buttons #close {
|
||||
float: left;
|
||||
height: 100%;
|
||||
width: 33%;
|
||||
text-align: center;
|
||||
color: #eee;
|
||||
transition: all ease-in-out .2s;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#buttons #minimize:hover,
|
||||
#buttons #maximize:hover {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
#buttons #close:hover {
|
||||
background-color: #ff0000;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#release-tab.uk-active {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: calc(100vh - 110px);
|
||||
}
|
||||
|
||||
#main-container-selector {
|
||||
background-color: #222;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.uk-tab>.uk-active>a {
|
||||
color: white;
|
||||
border-color: #1e87f0;
|
||||
}
|
||||
|
||||
.uk-input,
|
||||
.uk-select,
|
||||
#logs {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.uk-textarea {
|
||||
background-color: #555;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#controller-tab h3 label,
|
||||
#experimental-tab h3 label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#donation {
|
||||
background: rgb(232, 91, 70);
|
||||
}
|
||||
|
||||
#discord {
|
||||
background: rgb(114, 137, 218);
|
||||
}
|
||||
|
||||
#dark {
|
||||
border: 2px solid;
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
#light {
|
||||
background-color: black;
|
||||
border: 2px solid;
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
#dark:hover {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
#light:hover {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
#rcon {
|
||||
height: 110px;
|
||||
width: auto;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h1{
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
tr {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
tr span.uk-text-lead {
|
||||
color: white;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.preset {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
td {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
#modal-import-preset div,
|
||||
#modal-new-preset div,
|
||||
#modal-export-preset div {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.uk-tab>* {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
button,
|
||||
ul,
|
||||
li,
|
||||
div {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.delete {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1086px) {
|
||||
|
||||
.delete {
|
||||
margin-bottom: 20px
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="uk-animation-fade">
|
||||
<header id="titlebar">
|
||||
<nav>
|
||||
<div id="titleShown"></div>
|
||||
<div id="buttons">
|
||||
<div id="minimize">
|
||||
<span>-</span>
|
||||
</div>
|
||||
<div id="maximize">
|
||||
<span>□</span>
|
||||
</div>
|
||||
<div id="close">
|
||||
<span>×</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div id="main">
|
||||
<h1>
|
||||
<img id="rcon" src="assets/icon.png">
|
||||
Ryzen Controller
|
||||
<span id="version" class="uk-badge"></span>
|
||||
<a class="uk-badge" id="donation" title="Buy us some beers ❤️" href="#" onClick="require('electron').shell.openExternal('https://www.patreon.com/ryzencontrollerteam')">Patreon</a>
|
||||
<a class="uk-badge" id="discord" title="Join us on discord" href="#" onClick="require('electron').shell.openExternal('https://discord.gg/EahayUv')">Discord</a>
|
||||
<a class="uk-badge" id="light" title="Light Mode" href="index.html"><i class="fas fa-sun"></i></a>
|
||||
<a class="uk-badge" id="dark" title="Dark Mode (BETA)" href="index-dark.html"><i class="fas fa-moon"></i></a>
|
||||
|
||||
</h1>
|
||||
<ul uk-switcher="animation: uk-animation-fade" uk-tab uk-tab uk-sticky="top: 56; offset: 32px; animation: uk-animation-slide-top;" class="uk-background-default uk-margin-remove" id="main-container-selector">
|
||||
<li><a href="#">Presets</a></li>
|
||||
<li><a href="#">Settings</a></li>
|
||||
<li><a href="#">Releases</a></li>
|
||||
<li><a href="#">Logs</a></li>
|
||||
<li><a href="#">System Info</a></li>
|
||||
<li><a href="#">Tutorial</a></li>
|
||||
</ul>
|
||||
<ul class="uk-switcher uk-margin-remove" id="main-container" uk-height-viewport="expand: true" style="position:relative;">
|
||||
<li class="uk-margin-top uk-margin-bottom uk-container">
|
||||
<div id="presetTab"></div>
|
||||
<p class="uk-margin">
|
||||
<button class="uk-button uk-button-secondary" uk-toggle="target: #modal-export-preset" type="button" onClick="preset_export()">
|
||||
Export
|
||||
</button>
|
||||
<button class="uk-button uk-button-secondary" uk-toggle="target: #modal-import-preset" type="button">
|
||||
Import
|
||||
</button>
|
||||
</p>
|
||||
</li>
|
||||
<li class="uk-margin-top uk-margin-bottom uk-container">
|
||||
<h3 class="windows-only">Auto start:</h3>
|
||||
<label class="windows-only"><input class="uk-checkbox" type="checkbox" id="start_at_boot"> When checked, Ryzen Controller will start on session start.</label>
|
||||
<h3>Auto apply on launch:</h3>
|
||||
<label><input class="uk-checkbox" type="checkbox" id="apply_last_settings_on_launch"> When checked, Ryzen Controller will try to apply latest used settings on launch.</label>
|
||||
<h3>Minimize to tray:</h3>
|
||||
<label><input class="uk-checkbox" type="checkbox" id="minimize_to_tray"> When checked, Ryzen Controller will minimize to tray instead of taskbar.</label><br>
|
||||
<label><input class="uk-checkbox" type="checkbox" id="start_minimized"> When checked, Ryzen Controller will start minimized when you launch it.</label>
|
||||
<!--
|
||||
<h3 class="windows-only">HPET:</h3>
|
||||
<p class="uk-margin windows-only">
|
||||
High Precision Event Timer: Allow application to get time with precision below microseconds, but is slower than most other other timer facilities.<br/>
|
||||
Try running benchmark with and without to see if you get any difference.<br/>
|
||||
If it doesn't make any difference, it's recommanded to let it enable.<br/>
|
||||
<em>You must reboot to apply changes.</em>
|
||||
</p>
|
||||
<p class="uk-margin windows-only">
|
||||
<button class="uk-button uk-button-primary" onClick="toggleHpet('true')">Enable</button>
|
||||
<button class="uk-button uk-button-secondary" onClick="toggleHpet('false')">Disable</button>
|
||||
</p>
|
||||
-->
|
||||
<h3>Re-apply ryzenadj periodically:</h3>
|
||||
<p>Ryzen Controller will re-apply ryzenadj every X seconds. Set to 0 to disable.</p>
|
||||
<div class="uk-grid-small" uk-grid>
|
||||
<div class="uk-width-1-6">
|
||||
<input class="uk-input" type="number" min="0" step="10" max="3600" value="0" id="reapply_periodically" repeat="reapply_periodically_range">
|
||||
</div>
|
||||
<div class="uk-width-expand">
|
||||
<input class="uk-range" type="range" min="0" step="10" max="3600" value="0" repeat="reapply_periodically" id="reapply_periodically_range">
|
||||
</div>
|
||||
</div>
|
||||
<h3>Ryzenadj path:</h3>
|
||||
<div class="uk-grid-small" uk-grid>
|
||||
<div class="uk-width-2-3@s">
|
||||
<input class="uk-input" type="text" id="ryzen_adj_path">
|
||||
</div>
|
||||
<div class="uk-width-1-3@s">
|
||||
<button class="uk-button uk-button-default uk-button-small" type="button" onClick="askingForRyzenAdjExecutablePath()">
|
||||
Select path to ryzenadj.exe
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li id="release-tab">
|
||||
<webview style="height:100%;" frameborder="0" src="https://gitlab.com/ryzen-controller-team/ryzen-controller/releases"></webview>
|
||||
</li>
|
||||
<li class="uk-margin-top uk-margin-bottom uk-container">
|
||||
<textarea class="uk-textarea" rows="20" id="logs" readonly></textarea>
|
||||
</li>
|
||||
<li class="uk-margin-top uk-margin-bottom uk-container">
|
||||
<div>
|
||||
<canvas id="glcanvas" width="0" height="0"></canvas>
|
||||
<h3>System Infomation</h3>
|
||||
<div id="cpu"></div>
|
||||
<div id="cores"></div>
|
||||
<div id="cpuspeed"></div>
|
||||
<div id="gpu1"></div>
|
||||
<div id="memory"></div>
|
||||
<div id="platform"></div>
|
||||
|
||||
</div>
|
||||
</li>
|
||||
<li class="uk-margin-top uk-margin-bottom uk-container">
|
||||
<div>
|
||||
<h2>Coming Soon</h2>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Add a preset modal -->
|
||||
<div id="modal-new-preset" uk-modal>
|
||||
<div class="uk-modal-dialog uk-modal-body">
|
||||
<h2 class="uk-modal-title">Save current settings to preset</h2>
|
||||
<p>The current settings will be saved to a new preset. Give it a name then you'll be able to reapply it fastly from the preset tab.</p>
|
||||
<div class="uk-margin">
|
||||
<input class="uk-input" type="text" id="new_preset_name" placeholder="Preset name: performance, low energy, unicorn power, ...">
|
||||
</div>
|
||||
<button class="uk-button uk-button-primary uk-modal-close" type="button" onClick="preset_createNewPreset()">Save</button>
|
||||
<button class="uk-button uk-button-secondary uk-modal-close" type="button">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Export preset modal -->
|
||||
<div id="modal-export-preset" uk-modal>
|
||||
<div class="uk-modal-dialog uk-modal-body">
|
||||
<h2 class="uk-modal-title">Export presets</h2>
|
||||
<p>This is the data to be used by Ryzen Controller to import presets.</p>
|
||||
<div class="uk-margin">
|
||||
<textarea class="uk-textarea" rows="5" id="modal-export-preset-textarea" readonly></textarea>
|
||||
</div>
|
||||
<button class="uk-button uk-button-secondary uk-modal-close" type="button">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Import preset modal -->
|
||||
<div id="modal-import-preset" uk-modal>
|
||||
<div class="uk-modal-dialog uk-modal-body">
|
||||
<h2 class="uk-modal-title">Import presets</h2>
|
||||
<p>Paste here the presets and valid, your current presets will be merged with this.</p>
|
||||
<div class="uk-margin">
|
||||
<textarea class="uk-textarea" rows="5" id="modal-import-preset-textarea"></textarea>
|
||||
</div>
|
||||
<button class="uk-button uk-button-primary uk-modal-close" type="button" onClick="preset_import()">Import</button>
|
||||
<button class="uk-button uk-button-secondary uk-modal-close" type="button">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
<script type="text/javascript" src="./js/preset.js"></script>
|
||||
<script type="text/javascript" src="./js/methods.js"></script>
|
||||
<script type="text/javascript" src="./js/app.js"></script>
|
||||
<script type="text/javascript" src="./js/init.js"></script>
|
||||
<script type="text/javascript" src="./js/menuHandler.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
366
index.html
@ -1,366 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title id="title">Ryzen Controller</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="./node_modules/uikit/dist/css/uikit.min.css" />
|
||||
<script src="https://browser.sentry-cdn.com/5.6.1/bundle.min.js" crossorigin="anonymous"></script>
|
||||
<script src="./node_modules/uikit/dist/js/uikit.min.js"></script>
|
||||
<script src="./node_modules/uikit/dist/js/uikit-icons.min.js"></script>
|
||||
<script src="https://kit.fontawesome.com/c7184454f2.js" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
border: 1.25px solid #ddd;
|
||||
min-width: 800px;
|
||||
height: 100%;
|
||||
-webkit-app-region: drag;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#titlebar {
|
||||
display: block;
|
||||
position: fixed;
|
||||
height: 32px;
|
||||
width: calc(100% - 2px);
|
||||
z-index: 9999999999;
|
||||
/*Compensate for body 1px border*/
|
||||
}
|
||||
#main {
|
||||
height: calc(100% - 29px);
|
||||
margin-top: 29px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
|
||||
nav {
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
background-color: #ddd;
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
-webkit-app-region: drag;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
nav #titleShown {
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
color: black;
|
||||
font-size: 1em;
|
||||
float: left;
|
||||
padding: 0 0 0 1em;
|
||||
line-height: 32px;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
nav #buttons {
|
||||
float: right;
|
||||
min-width: 150px;
|
||||
height: 100%;
|
||||
background-color: #bbb;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
#buttons #minimize,
|
||||
#buttons #maximize,
|
||||
#buttons #close {
|
||||
float: left;
|
||||
height: 100%;
|
||||
width: 33%;
|
||||
text-align: center;
|
||||
color: white;
|
||||
transition: all ease-in-out .2s;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#buttons #minimize:hover,
|
||||
#buttons #maximize:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
#buttons #close:hover {
|
||||
background-color: #ff0000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#release-tab.uk-active {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: calc(100vh - 110px);
|
||||
}
|
||||
|
||||
#controller-tab h3 label,
|
||||
#experimental-tab h3 label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#donation {
|
||||
background: rgb(232, 91, 70);
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
#discord {
|
||||
background: rgb(114, 137, 218);
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
#dark {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
#rcon {
|
||||
height: 110px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#dark {
|
||||
background-color: white;
|
||||
border: 2px solid;
|
||||
border-color: #333;
|
||||
color: black;
|
||||
-webkit-app-region: no-drag;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
#light {
|
||||
margin-right: 0;
|
||||
border: 2px solid;
|
||||
border-color: #333;
|
||||
-webkit-app-region: no-drag;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#dark:hover {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
#light:hover {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.uk-tab>* {
|
||||
padding-left: 10px;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
button,
|
||||
ul,
|
||||
li,
|
||||
div {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.delete {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1086px) {
|
||||
|
||||
.delete {
|
||||
margin-bottom: 20px
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="uk-animation-fade">
|
||||
<header id="titlebar">
|
||||
<nav>
|
||||
<div id="titleShown"></div>
|
||||
<div id="buttons">
|
||||
<div id="minimize">
|
||||
<span>-</span>
|
||||
</div>
|
||||
<div id="maximize">
|
||||
<span>□</span>
|
||||
</div>
|
||||
<div id="close">
|
||||
<span>×</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div id="main">
|
||||
<h1>
|
||||
<img id="rcon" src="assets/icon.png">
|
||||
Ryzen Controller
|
||||
<span id="version" class="uk-badge"></span>
|
||||
<a class="uk-badge" id="donation" title="Buy us some beers ❤️" href="#" onClick="require('electron').shell.openExternal('https://www.patreon.com/ryzencontrollerteam')">Patreon</a>
|
||||
<a class="uk-badge" id="discord" title="Join us on discord" href="#" onClick="require('electron').shell.openExternal('https://discord.gg/EahayUv')">Discord</a>
|
||||
<a class="uk-badge" id="light" title="Light Mode" href="index.html"><i class="fas fa-sun"></i></a>
|
||||
<a class="uk-badge" id="dark" title="Dark Mode (BETA)" href="index-dark.html"><i class="fas fa-moon"></i></a>
|
||||
|
||||
</h1>
|
||||
<ul uk-switcher="animation: uk-animation-fade" uk-tab uk-tab uk-sticky="top: 56; offset: 32px; animation: uk-animation-slide-top;" class="uk-background-default uk-margin-remove" id="main-container-selector">
|
||||
<li><a href="#">Presets</a></li>
|
||||
<li><a href="#">Settings</a></li>
|
||||
<li><a href="#">Releases</a></li>
|
||||
<li><a href="#">Logs</a></li>
|
||||
<li><a href="#">System Info</a></li>
|
||||
<li><a href="#">Tutorial</a></li>
|
||||
</ul>
|
||||
<ul class="uk-switcher uk-margin-remove" id="main-container" uk-height-viewport="expand: true" style="position:relative;">
|
||||
<li class="uk-margin-top uk-margin-bottom uk-container">
|
||||
<div id="presetTab"></div>
|
||||
<p class="uk-margin">
|
||||
<button class="uk-button uk-button-secondary" uk-toggle="target: #modal-export-preset" type="button" onClick="preset_export()">
|
||||
Export
|
||||
</button>
|
||||
<button class="uk-button uk-button-secondary" uk-toggle="target: #modal-import-preset" type="button">
|
||||
Import
|
||||
</button>
|
||||
</p>
|
||||
</li>
|
||||
<li class="uk-margin-top uk-margin-bottom uk-container">
|
||||
<h3 class="windows-only">Auto start:</h3>
|
||||
<label class="windows-only"><input class="uk-checkbox" type="checkbox" id="start_at_boot"> When checked, Ryzen Controller will start on session start.</label>
|
||||
<h3>Auto apply on launch:</h3>
|
||||
<label><input class="uk-checkbox" type="checkbox" id="apply_last_settings_on_launch"> When checked, Ryzen Controller will try to apply latest used settings on launch.</label>
|
||||
<h3>Minimize to tray:</h3>
|
||||
<label><input class="uk-checkbox" type="checkbox" id="minimize_to_tray"> When checked, Ryzen Controller will minimize to tray instead of taskbar.</label><br>
|
||||
<label><input class="uk-checkbox" type="checkbox" id="start_minimized"> When checked, Ryzen Controller will start minimized when you launch it.</label>
|
||||
<!--
|
||||
<h3 class="windows-only">HPET:</h3>
|
||||
<p class="uk-margin windows-only">
|
||||
High Precision Event Timer: Allow application to get time with precision below microseconds, but is slower than most other other timer facilities.<br/>
|
||||
Try running benchmark with and without to see if you get any difference.<br/>
|
||||
If it doesn't make any difference, it's recommanded to let it enable.<br/>
|
||||
<em>You must reboot to apply changes.</em>
|
||||
</p>
|
||||
<p class="uk-margin windows-only">
|
||||
<button class="uk-button uk-button-primary" onClick="toggleHpet('true')">Enable</button>
|
||||
<button class="uk-button uk-button-secondary" onClick="toggleHpet('false')">Disable</button>
|
||||
</p>
|
||||
-->
|
||||
<h3>Re-apply ryzenadj periodically:</h3>
|
||||
<p>Ryzen Controller will re-apply ryzenadj every X seconds. Set to 0 to disable.</p>
|
||||
<div class="uk-grid-small" uk-grid>
|
||||
<div class="uk-width-1-6">
|
||||
<input class="uk-input" type="number" min="0" step="10" max="3600" value="0" id="reapply_periodically" repeat="reapply_periodically_range">
|
||||
</div>
|
||||
<div class="uk-width-expand">
|
||||
<input class="uk-range" type="range" min="0" step="10" max="3600" value="0" repeat="reapply_periodically" id="reapply_periodically_range">
|
||||
</div>
|
||||
</div>
|
||||
<h3>Ryzenadj path:</h3>
|
||||
<div class="uk-grid-small" uk-grid>
|
||||
<div class="uk-width-2-3@s">
|
||||
<input class="uk-input" type="text" id="ryzen_adj_path">
|
||||
</div>
|
||||
<div class="uk-width-1-3@s">
|
||||
<button class="uk-button uk-button-default uk-button-small" type="button" onClick="askingForRyzenAdjExecutablePath()">
|
||||
Select path to ryzenadj.exe
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li id="release-tab">
|
||||
<webview style="height:100%;" frameborder="0" src="https://gitlab.com/ryzen-controller-team/ryzen-controller/releases"></webview>
|
||||
</li>
|
||||
<li class="uk-margin-top uk-margin-bottom uk-container">
|
||||
<textarea class="uk-textarea" rows="20" id="logs" readonly></textarea>
|
||||
</li>
|
||||
<li class="uk-margin-top uk-margin-bottom uk-container">
|
||||
<div>
|
||||
<canvas id="glcanvas" width="0" height="0"></canvas>
|
||||
<h3>System Infomation</h3>
|
||||
<div id="cpu"></div>
|
||||
<div id="cores"></div>
|
||||
<div id="cpuspeed"></div>
|
||||
<div id="gpu1"></div>
|
||||
<div id="memory"></div>
|
||||
<div id="platform"></div>
|
||||
|
||||
</div>
|
||||
</li>
|
||||
<li class="uk-margin-top uk-margin-bottom uk-container">
|
||||
<div>
|
||||
<h2>Coming Soon</h2>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Add a preset modal -->
|
||||
<div id="modal-new-preset" uk-modal>
|
||||
<div class="uk-modal-dialog uk-modal-body">
|
||||
<h2 class="uk-modal-title">Save current settings to preset</h2>
|
||||
<p>The current settings will be saved to a new preset. Give it a name then you'll be able to reapply it fastly from the preset tab.</p>
|
||||
<div class="uk-margin">
|
||||
<input class="uk-input" type="text" id="new_preset_name" placeholder="Preset name: performance, low energy, unicorn power, ...">
|
||||
</div>
|
||||
<button class="uk-button uk-button-primary uk-modal-close" type="button" onClick="preset_createNewPreset()">Save</button>
|
||||
<button class="uk-button uk-button-secondary uk-modal-close" type="button">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Export preset modal -->
|
||||
<div id="modal-export-preset" uk-modal>
|
||||
<div class="uk-modal-dialog uk-modal-body">
|
||||
<h2 class="uk-modal-title">Export presets</h2>
|
||||
<p>This is the data to be used by Ryzen Controller to import presets.</p>
|
||||
<div class="uk-margin">
|
||||
<textarea class="uk-textarea" rows="5" id="modal-export-preset-textarea" readonly></textarea>
|
||||
</div>
|
||||
<button class="uk-button uk-button-secondary uk-modal-close" type="button">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Import preset modal -->
|
||||
<div id="modal-import-preset" uk-modal>
|
||||
<div class="uk-modal-dialog uk-modal-body">
|
||||
<h2 class="uk-modal-title">Import presets</h2>
|
||||
<p>Paste here the presets and valid, your current presets will be merged with this.</p>
|
||||
<div class="uk-margin">
|
||||
<textarea class="uk-textarea" rows="5" id="modal-import-preset-textarea"></textarea>
|
||||
</div>
|
||||
<button class="uk-button uk-button-primary uk-modal-close" type="button" onClick="preset_import()">Import</button>
|
||||
<button class="uk-button uk-button-secondary uk-modal-close" type="button">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
<script type="text/javascript" src="./js/preset.js"></script>
|
||||
<script type="text/javascript" src="./js/methods.js"></script>
|
||||
<script type="text/javascript" src="./js/app.js"></script>
|
||||
<script type="text/javascript" src="./js/init.js"></script>
|
||||
<script type="text/javascript" src="./js/menuHandler.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,62 +0,0 @@
|
||||
const electron = require('electron')
|
||||
const app = electron.app
|
||||
|
||||
module.exports = {
|
||||
handleSquirrelEvent: function() {
|
||||
if (process.argv.length === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ChildProcess = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
const appFolder = path.resolve(process.execPath, '..');
|
||||
const rootAtomFolder = path.resolve(appFolder, '..');
|
||||
const updateDotExe = path.resolve(path.join(rootAtomFolder, 'Update.exe'));
|
||||
const exeName = path.basename(process.execPath);
|
||||
const spawn = function(command, args) {
|
||||
let spawnedProcess, error;
|
||||
|
||||
try {
|
||||
spawnedProcess = ChildProcess.spawn(command, args, {detached: true});
|
||||
} catch (error) {}
|
||||
|
||||
return spawnedProcess;
|
||||
};
|
||||
|
||||
const squirrelEvent = process.argv[1];
|
||||
switch (squirrelEvent) {
|
||||
case '--squirrel-install':
|
||||
case '--squirrel-updated':
|
||||
// Optionally do things such as:
|
||||
// - Add your .exe to the PATH
|
||||
// - Write to the registry for things like file associations and
|
||||
// explorer context menus
|
||||
|
||||
setTimeout(app.quit, 1000);
|
||||
return true;
|
||||
|
||||
case '--squirrel-uninstall':
|
||||
// Undo anything you did in the --squirrel-install and
|
||||
// --squirrel-updated handlers
|
||||
|
||||
// Removing desktop shortcut
|
||||
let fs = require('fs');
|
||||
var shortcut_path = app.getPath('desktop') + "\\Ryzen Controller";
|
||||
if (!fs.existsSync(shortcut_path)) {
|
||||
fs.unlink(shortcut_path, console.log);
|
||||
}
|
||||
|
||||
setTimeout(app.quit, 1000);
|
||||
return true;
|
||||
|
||||
case '--squirrel-obsolete':
|
||||
// This is called on the outgoing version of your app before
|
||||
// we update to the new version - it's the opposite of
|
||||
// --squirrel-updated
|
||||
|
||||
app.quit();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
const createWindowsInstaller = require('electron-winstaller').createWindowsInstaller
|
||||
const path = require('path')
|
||||
const lyrics = [
|
||||
"I'm awake",
|
||||
"I'm alive",
|
||||
"now",
|
||||
"I know",
|
||||
"what I",
|
||||
"believe inside",
|
||||
"NOW",
|
||||
"It's my time",
|
||||
"I'll do",
|
||||
"what I want",
|
||||
"'cause this",
|
||||
"is my life",
|
||||
"Here",
|
||||
"right now",
|
||||
"I will",
|
||||
"stand my ground",
|
||||
"and never back down",
|
||||
"I know",
|
||||
"what I",
|
||||
"believe inside",
|
||||
"I'm awake",
|
||||
"and",
|
||||
"I'm alive",
|
||||
"--",
|
||||
];
|
||||
var lyrics_pos = -1;
|
||||
var keepAlive = false;
|
||||
|
||||
getInstallerConfig()
|
||||
.then(createWindowsInstaller)
|
||||
.catch((error) => {
|
||||
console.error(error.message || error)
|
||||
process.exit(1)
|
||||
})
|
||||
.then(function(){
|
||||
clearInterval(keepAlive);
|
||||
console.log('Installer created.');
|
||||
})
|
||||
|
||||
function getInstallerConfig () {
|
||||
console.log('creating windows installer')
|
||||
keepAlive = setInterval(function(){
|
||||
lyrics_pos++;
|
||||
console.log(`${lyrics[ lyrics_pos % lyrics.length ]}`);
|
||||
}, 10000);
|
||||
const rootPath = path.join('./')
|
||||
|
||||
return Promise.resolve({
|
||||
appDirectory: path.join(rootPath, 'release-builds', 'RyzenController-win32-ia32/'),
|
||||
authors: 'Decaunes Quentin',
|
||||
outputDirectory: path.join(rootPath, 'installer-builds'),
|
||||
setupExe: 'RyzenControllerInstaller.exe',
|
||||
noMsi: true
|
||||
})
|
||||
}
|
111
js/app.js
@ -1,111 +0,0 @@
|
||||
ready(function(){
|
||||
const settings = require('electron-settings');
|
||||
const fixPath = require('fix-path');
|
||||
document.isStarting = true;
|
||||
if (isDevMode()) {
|
||||
addSentry();
|
||||
}
|
||||
fixPath();
|
||||
displayOptions();
|
||||
preFillSettings();
|
||||
isRyzenAdjPathValid();
|
||||
loadLatestUsedSettings();
|
||||
registerRepeaterForAllInput();
|
||||
registerEventListenerForSettingsInput();
|
||||
checkForAdminRights();
|
||||
displayVersion();
|
||||
reApplyPeriodically(require('electron-settings').get('settings.reapply_periodically'));
|
||||
handleAcStatusChanges();
|
||||
if (isWindows() && isDevMode()) {
|
||||
recreateShortcut();
|
||||
}
|
||||
handlePlatformSpecificDisplay();
|
||||
preset_updateList();
|
||||
checkForNewRelease();
|
||||
document.isStarting = false;
|
||||
appendLog(`################## UserId: ${settings.get('userid')}`);
|
||||
|
||||
settings.set('settings',
|
||||
Object.assign(
|
||||
{},
|
||||
settings.get('settings'),
|
||||
{ first_launch: false }
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Will create and handle ryzenadj.exe execution.
|
||||
*/
|
||||
function applyRyzenSettings() {
|
||||
const settings = getCurrentSettings("ryzenadjArgs");
|
||||
const appSettings = require('electron-settings');
|
||||
|
||||
const child = require('child_process').execFile;
|
||||
const executablePath = getRyzenAdjExecutablePath();
|
||||
|
||||
if (!isRyzenAdjPathValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options_data = require('./js/options_data.json');
|
||||
const ryzenAdjConvert = {
|
||||
"toHex": function (value) { return '0x' + decimalToHexString(value * 1000); },
|
||||
"toThousand": function (value) { return value * 1000; },
|
||||
"roundTen": function (value) { return parseInt(value / 10) * 10; },
|
||||
};
|
||||
|
||||
// Create a string to be used for CLI.
|
||||
var parameters = [];
|
||||
for (const option in settings) {
|
||||
if (settings.hasOwnProperty(option)) {
|
||||
let value = settings[option];
|
||||
let option_name = false;
|
||||
try {
|
||||
option_name = Object.keys(options_data).filter(function(cur_option_name){
|
||||
return options_data[cur_option_name].ryzenadj_arg === option;
|
||||
})[0];
|
||||
} catch (error) {
|
||||
notification('danger', `Unknown option "${option}".`);
|
||||
appendLog(`applyRyzenSettings(): ${error}`);
|
||||
Sentry.captureException(new Error(`applyRyzenSettings(): ${error}`));
|
||||
}
|
||||
if (options_data[option_name].ryzenadj_value_convert) {
|
||||
value = ryzenAdjConvert[
|
||||
options_data[option_name].ryzenadj_value_convert
|
||||
](value);
|
||||
}
|
||||
parameters.push('' + option + value);
|
||||
}
|
||||
}
|
||||
|
||||
if (parameters.length === 0) {
|
||||
notification('primary', 'Please add some options before applying ryzenAdj.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!appSettings.get('retry')) {
|
||||
appSettings.set('retry', 2);
|
||||
notification('warning', 'Applying settings...');
|
||||
} else {
|
||||
let retry = appSettings.get('retry') - 1;
|
||||
appSettings.set('retry', retry);
|
||||
}
|
||||
child(executablePath, parameters, function(err, data) {
|
||||
var output = data.toString();
|
||||
if (err) {
|
||||
if (appSettings.get('retry')) {
|
||||
return setTimeout(applyRyzenSettings, 500);
|
||||
}
|
||||
notification('danger', err + '<br/>' + output);
|
||||
Sentry.captureException(new Error(err + ' - ' + output));
|
||||
}
|
||||
else if (output) {
|
||||
notification('success', `RyzenAdj has been applied successfully.`);
|
||||
appendLog('Ryzenadj output:<br/>' + output);
|
||||
saveLatestUsedSettings();
|
||||
}
|
||||
appSettings.set('retry', false);
|
||||
});
|
||||
|
||||
}
|
78
js/init.js
@ -1,78 +0,0 @@
|
||||
var os = require("os");
|
||||
|
||||
|
||||
var title = document.getElementById('title').innerHTML;
|
||||
var cpus = os.cpus();
|
||||
var cpucount = cpus.length;
|
||||
var cpucores;
|
||||
var cpuman;
|
||||
var cpuspeed;
|
||||
var cpuspeedmhz;
|
||||
var platform;
|
||||
var mem;
|
||||
var totalmem;
|
||||
var gputest1;
|
||||
|
||||
cpus.forEach(function (cpu, i) {
|
||||
cpuman = "<strong>Processor Model: </strong>" + cpu.model;
|
||||
cpuspeed = cpu.speed;
|
||||
cpuspeedmhz = "<strong>Processor Frequency: </strong>" + cpuspeed.toString() + "MHz";
|
||||
platform = os.platform();
|
||||
mem = os.totalmem();
|
||||
mem = mem / 1000000000;
|
||||
mem = Math.trunc(mem);
|
||||
totalmem = "<strong>System Memory: </strong>" + mem.toString()+ " " + "GB";
|
||||
cpucores = "<strong>Processor Cores: </strong>" + cpucount.toString();
|
||||
if(platform == "win32"){
|
||||
platform = "<strong>Platform: </strong>" + "Windows " + os.release();
|
||||
} else if(platform == "linux"){
|
||||
platform = "<strong>Platform: </strong>" + "Linux " + os.release();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
var canvas;
|
||||
canvas = document.getElementById("glcanvas");
|
||||
var gl = canvas.getContext("experimental-webgl");
|
||||
|
||||
function getUnmaskedInfo(gl) {
|
||||
var unMaskedInfo = {
|
||||
renderer: '',
|
||||
vendor: ''
|
||||
};
|
||||
|
||||
var dbgRenderInfo = gl.getExtension("WEBGL_debug_renderer_info");
|
||||
if (dbgRenderInfo != null) {
|
||||
unMaskedInfo.renderer = gl.getParameter(dbgRenderInfo.UNMASKED_RENDERER_WEBGL);
|
||||
unMaskedInfo.vendor = gl.getParameter(dbgRenderInfo.UNMASKED_VENDOR_WEBGL);
|
||||
}
|
||||
|
||||
return unMaskedInfo;
|
||||
}
|
||||
|
||||
gputest1 = getUnmaskedInfo(gl).renderer;
|
||||
|
||||
if(gputest1 =="ANGLE (AMD Radeon(TM) RX Vega 10 Graphics Direct3D11 vs_5_0 ps_5_0)"){
|
||||
gputest1 = "AMD Radeon(TM) RX Vega 10 Graphics"
|
||||
} else if (gputest1 =="ANGLE (AMD Radeon(TM) RX Vega 8 Graphics Direct3D11 vs_5_0 ps_5_0)"){
|
||||
gputest1 = "AMD Radeon(TM) RX Vega 8 Graphics"
|
||||
} else if(gputest1 =="ANGLE (AMD Radeon(TM) RX Vega 8 Mobile Graphics Direct3D11 vs_5_0 ps_5_0)"){
|
||||
gputest1 = "AMD Radeon(TM) RX Vega 8 Graphics"
|
||||
} else if(gputest1 =="ANGLE (AMD Radeon(TM) RX Vega 3 Graphics Direct3D11 vs_5_0 ps_5_0)"){
|
||||
gputest1 = "AMD Radeon(TM) RX Vega 3 Graphics"
|
||||
} else if(gputest1 =="ANGLE (AMD Radeon(TM) RX Vega 3 Mobile Graphics Direct3D11 vs_5_0 ps_5_0)"){
|
||||
gputest1 = "AMD Radeon(TM) RX Vega 3 Graphics"
|
||||
}
|
||||
|
||||
|
||||
document.getElementById('titleShown').innerHTML = title;
|
||||
document.getElementById('cpu').innerHTML = cpuman;
|
||||
document.getElementById('cores').innerHTML = cpucores;
|
||||
document.getElementById('cpuspeed').innerHTML = cpuspeedmhz;
|
||||
document.getElementById('platform').innerHTML = platform;
|
||||
document.getElementById('memory').innerHTML = totalmem;
|
||||
document.getElementById('gpu1').innerHTML = "<strong>GPU: </strong>" + gputest1;
|
||||
|
@ -1,22 +0,0 @@
|
||||
const {remote} = require('electron');
|
||||
|
||||
document.getElementById('close').addEventListener('click', closeWindow);
|
||||
document.getElementById('minimize').addEventListener('click', minimizeWindow);
|
||||
document.getElementById('maximize').addEventListener('click', maximizeWindow);
|
||||
|
||||
|
||||
|
||||
function closeWindow () {
|
||||
var window = remote.getCurrentWindow();
|
||||
window.close();
|
||||
}
|
||||
|
||||
function minimizeWindow () {
|
||||
var window = remote.getCurrentWindow();
|
||||
window.minimize();
|
||||
}
|
||||
|
||||
function maximizeWindow () {
|
||||
var window = remote.getCurrentWindow()
|
||||
window.isMaximized() ? window.unmaximize() : window.maximize();
|
||||
}
|
709
js/methods.js
@ -1,709 +0,0 @@
|
||||
/**
|
||||
* Will enable Sentry.
|
||||
*/
|
||||
function addSentry() {
|
||||
const uuidv4 = require('uuid/v4');
|
||||
const settings = require('electron-settings');
|
||||
|
||||
var waitSentry = setInterval(() => {
|
||||
if (!Sentry) {
|
||||
console.log('waitSentry');
|
||||
return;
|
||||
}
|
||||
clearInterval(waitSentry);
|
||||
console.log('sentryOk');
|
||||
Sentry.init({
|
||||
dsn: 'https://f80fd3ea297141a8bdc04ce812762f39@sentry.io/1513427',
|
||||
release: require('./package.json').version,
|
||||
beforeSend: (event) => {
|
||||
event.exception.values = event.exception.values.map((value) => {
|
||||
if (value.stacktrace) {
|
||||
value.stacktrace.frames = value.stacktrace.frames.map((frame) => {
|
||||
frame.filename = frame.filename.replace(/^.*ryzen(|-)controller\//g, "");
|
||||
return frame;
|
||||
});
|
||||
}
|
||||
return value;
|
||||
});
|
||||
if (event.request.url) {
|
||||
event.request.url = event.request.url.replace(/^.*ryzen(|-)controller\//g, "");
|
||||
}
|
||||
return event;
|
||||
}
|
||||
});
|
||||
Sentry.configureScope((scope) => {
|
||||
if (!settings.get('userid')) {
|
||||
settings.set('userid', uuidv4());
|
||||
}
|
||||
const userid = settings.get('userid');
|
||||
scope.setUser({
|
||||
id: userid
|
||||
});
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will load options_data.json and display them into index.html.
|
||||
*/
|
||||
function displayOptions(){
|
||||
const options_data = require('./js/options_data.json');
|
||||
var tabs = {};
|
||||
for (const option_name in options_data) {
|
||||
if (!options_data.hasOwnProperty(option_name)) {
|
||||
appendLog(`Error while loading ${option_name} option.`);
|
||||
continue;
|
||||
}
|
||||
let option_data = options_data[option_name];
|
||||
if (!option_data.hasOwnProperty('tab')) {
|
||||
appendLog(`Error while loading ${option_name} tab property.`);
|
||||
continue;
|
||||
}
|
||||
let tab_name = option_data['tab'];
|
||||
let tab_name_css = tab_name.toLowerCase().replace(/ /g, "-");
|
||||
let tab = document.querySelector(`#${tab_name_css}-tab`);
|
||||
if (!tab) {
|
||||
let selectorContent = document.querySelector(`#main-container-selector`).innerHTML;
|
||||
let tabContent = document.querySelector(`#main-container`).innerHTML;
|
||||
|
||||
document.querySelector('#main-container-selector').innerHTML = /*html*/`
|
||||
<li><a href="#">${tab_name}</a></li>
|
||||
${selectorContent}
|
||||
`;
|
||||
document.querySelector(`#main-container`).innerHTML = /*html*/`
|
||||
<li class="uk-margin-top uk-margin-bottom uk-container" id="${tab_name_css}-tab"></li>
|
||||
${tabContent}
|
||||
`;
|
||||
tab = document.querySelector(`#${tab_name_css}-tab`);
|
||||
}
|
||||
tabs[tab_name_css] = true;
|
||||
tab.innerHTML += /*html*/`
|
||||
<h3 id="${option_name}-label" uk-tooltip="${option_data.description}"><label>
|
||||
<input class="uk-checkbox uk-margin-small-right" type="checkbox" id="apply_${option_name}"/>
|
||||
<span class="option-label">${option_data.label}</span>
|
||||
</label></h3>
|
||||
<div class="uk-grid-small" uk-grid>
|
||||
<div class="uk-width-1-6">
|
||||
<input
|
||||
class="uk-input"
|
||||
type="number"
|
||||
id="${option_name}"
|
||||
repeat="${option_name}_range"
|
||||
min="${option_data.min}"
|
||||
max="${option_data.max}"
|
||||
step="${option_data.step}"
|
||||
value="${option_data.default}"
|
||||
/>
|
||||
</div>
|
||||
<div class="uk-width-expand">
|
||||
<input
|
||||
class="uk-range"
|
||||
type="range"
|
||||
repeat="${option_name}"
|
||||
id="${option_name}_range"
|
||||
min="${option_data.min}"
|
||||
max="${option_data.max}"
|
||||
step="${option_data.step}"
|
||||
value="${option_data.default}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
UIkit.tooltip(document.querySelector(`#${option_name}-label`));
|
||||
}
|
||||
|
||||
for (const tab_name_css in tabs) {
|
||||
if (tabs.hasOwnProperty(tab_name_css)) {
|
||||
const tab = document.querySelector(`#${tab_name_css}-tab`);
|
||||
tab.innerHTML += /*html*/`
|
||||
<p class="uk-margin">
|
||||
<button class="uk-button uk-button-primary" onClick="applyRyzenSettings()">Apply</button>
|
||||
<button class="uk-button uk-button-secondary" uk-toggle="target: #modal-new-preset">Save to preset</button>
|
||||
</p>
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will create a nodes from an html string.
|
||||
* @param {string} str An html string
|
||||
*/
|
||||
function parseHTML(str) {
|
||||
var tmp = document.implementation.createHTMLDocument();
|
||||
tmp.body.innerHTML = str;
|
||||
return tmp.body.children;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the current working directory.
|
||||
*/
|
||||
function getCurrentWorkingDirectory() {
|
||||
const cwd = require('electron').remote.app.getAppPath()
|
||||
appendLog(`getCurrentWorkingDirectory(): ${cwd}`);
|
||||
return cwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion from int to hex.
|
||||
* @param {int} number A number.
|
||||
*/
|
||||
function decimalToHexString(number) {
|
||||
if (number < 0)
|
||||
{
|
||||
number = 0xFFFFFFFF + number + 1;
|
||||
}
|
||||
|
||||
return number.toString(16).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Will execute the given callback once document is ready.
|
||||
* @param {function} fn A callback to be executed.
|
||||
*/
|
||||
function ready(fn) {
|
||||
if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading"){
|
||||
fn();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will make sure inputs are repeated when needed.
|
||||
*
|
||||
* Use the "repeat" html attribute to define where the current value must be repeated.
|
||||
*/
|
||||
function registerRepeaterForAllInput() {
|
||||
var ranges = document.querySelectorAll('[repeat]');
|
||||
for (const range in ranges) {
|
||||
if (ranges.hasOwnProperty(range)) {
|
||||
const element = ranges[range];
|
||||
element.addEventListener('change', function(event) {
|
||||
var repeater = document.getElementById(event.target.attributes.repeat.value);
|
||||
repeater.value = event.target.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the app is running with admin right.
|
||||
*
|
||||
* Will display a warning if not.
|
||||
*/
|
||||
function checkForAdminRights() {
|
||||
if (!isWindows()) {
|
||||
const isRoot = process.getuid && process.getuid() === 0;
|
||||
if (!isRoot) {
|
||||
notification('danger',
|
||||
`Warning: you must launch this app as administrator.<br/>`
|
||||
+ `Use "sudo ryzencontroller" from terminal to fix this.`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const child = require('child_process').execFile;
|
||||
child('NET SESSION', function(err,so,se) {
|
||||
if (se.length !== 0) {
|
||||
notification('warning',
|
||||
`Warning: you should launch this app as administrator, `
|
||||
+ `ryzenadj.exe doesn't seems to work correctly without administrator rights.`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the app is running on Windows.
|
||||
*/
|
||||
function isWindows() {
|
||||
return require('os').platform() === 'win32';
|
||||
}
|
||||
|
||||
/**
|
||||
* Will display a notification in ".notification-zone".
|
||||
* @param {string} type "primary", "warning", "danger" or "success".
|
||||
* @param {string} message The message to be displayed, new line will be replaced by <br/>.
|
||||
*/
|
||||
function notification(type, message) {
|
||||
appendLog(`notification(): ${type}\n${message}`);
|
||||
|
||||
if (window.last_notification) {
|
||||
window.last_notification.close();
|
||||
window.last_notification = false;
|
||||
}
|
||||
window.last_notification = new Notification('Ryzen Controller', {
|
||||
body: message,
|
||||
requireInteraction: false,
|
||||
silent: true,
|
||||
});
|
||||
|
||||
if (!window.last_notification) {
|
||||
window.last_notification = false;
|
||||
UIkit.notification({
|
||||
message: (''+message).replace(/(?:\r\n|\r|\n)/g, '<br/>'),
|
||||
status: type,
|
||||
pos: 'top-right',
|
||||
timeout: 5000,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return the ryzenadj.exe path registered, or default one if not provided.
|
||||
*/
|
||||
function getRyzenAdjExecutablePath() {
|
||||
const settings = require('electron-settings');
|
||||
var ryzen_adj_path = settings.get('settings.ryzen_adj_path');
|
||||
if (!ryzen_adj_path && isWindows()) {
|
||||
ryzen_adj_path = getCurrentWorkingDirectory() + "\\bin\\ryzenadj.exe";
|
||||
}
|
||||
appendLog(`getRyzenAdjExecutablePath(): "${ryzen_adj_path}"`);
|
||||
return ryzen_adj_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will fill settings page on render with saved data.
|
||||
*/
|
||||
function preFillSettings() {
|
||||
const ryzen_adj_path = document.getElementById('ryzen_adj_path');
|
||||
const settings = require('electron-settings');
|
||||
|
||||
ryzen_adj_path.value = getRyzenAdjExecutablePath();
|
||||
document.getElementById('start_at_boot').checked = !!settings.get('settings.start_at_boot');
|
||||
document.getElementById('apply_last_settings_on_launch').checked = !!settings.get('settings.apply_last_settings_on_launch');
|
||||
document.getElementById('minimize_to_tray').checked = !!settings.get('settings.minimize_to_tray');
|
||||
document.getElementById('start_minimized').checked = !!settings.get('settings.start_minimized');
|
||||
|
||||
seconds = parseInt(settings.get('settings.reapply_periodically'));
|
||||
seconds = seconds >= 0 ? seconds : 0;
|
||||
document.getElementById('reapply_periodically').value = seconds;
|
||||
document.getElementById('reapply_periodically_range').value = seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if ryzenadj executable has been registered and is valid.
|
||||
* Will display a notification if not.
|
||||
*
|
||||
* @return {Boolean} True if ryzenadj executable exists.
|
||||
*/
|
||||
function isRyzenAdjPathValid() {
|
||||
var fs = require('fs');
|
||||
|
||||
if (!fs.existsSync(getRyzenAdjExecutablePath())) {
|
||||
notification('danger', "Path to ryzenadj.exe is wrong, please fix it in settings tab.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will open a dialog to let user choose where is ryzenadj.exe.
|
||||
*/
|
||||
function askingForRyzenAdjExecutablePath() {
|
||||
var remote = require('electron').remote;
|
||||
var dialog = remote.require('electron').dialog;
|
||||
|
||||
dialog.showOpenDialog({
|
||||
properties: ['openFile']
|
||||
}, function (filePaths) {
|
||||
const settings = require('electron-settings');
|
||||
if (typeof filePaths === 'undefined') {
|
||||
notification('warning', 'No path given, nothing changed.');
|
||||
return;
|
||||
}
|
||||
if (typeof filePaths[0] === 'undefined') {
|
||||
notification('warning', 'No path given, nothing changed.');
|
||||
return;
|
||||
}
|
||||
|
||||
settings.set("settings",
|
||||
Object.assign(
|
||||
{},
|
||||
settings.get('settings'),
|
||||
{ ryzen_adj_path: filePaths[0] }
|
||||
)
|
||||
);
|
||||
|
||||
notification('primary', 'Path to ryzenAdj executable has been saved.');
|
||||
appendLog(`askingForRyzenAdjExecutablePath(): ${filePaths[0]}`);
|
||||
preFillSettings();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Will append logs to the logs tab.
|
||||
* @param {string} message The message to be logged.
|
||||
*/
|
||||
function appendLog(message) {
|
||||
var log_area = document.getElementById('logs');
|
||||
log_area.value += message + "\n";
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will save the latest used settings.
|
||||
*/
|
||||
function saveLatestUsedSettings() {
|
||||
var inputs = document.querySelectorAll('#main-container *[id$="-tab"] input');
|
||||
var latest_controller_tabs_settings = {};
|
||||
inputs.forEach(element => {
|
||||
let id = element.id;
|
||||
let value = element.value;
|
||||
if (id.indexOf('apply_') === 0) {
|
||||
value = element.checked;
|
||||
}
|
||||
latest_controller_tabs_settings[id] = value;
|
||||
});
|
||||
const settings = require('electron-settings');
|
||||
let ret = settings.set("latest_controller_tabs_settings", latest_controller_tabs_settings);
|
||||
appendLog(`saveLatestUsedSettings(): ${JSON.stringify(latest_controller_tabs_settings)}`);
|
||||
appendLog(`saveLatestUsedSettings(): ${JSON.stringify(ret)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will load the latest settings and refresh the controller tab's values.
|
||||
*
|
||||
* Will also apply latest settings if settings.apply_last_settings_on_launch is true.
|
||||
*/
|
||||
function loadLatestUsedSettings() {
|
||||
const settings = require('electron-settings');
|
||||
var latest_controller_tabs_settings = settings.get("latest_controller_tabs_settings");
|
||||
appendLog(`loadLatestUsedSettings(): ${JSON.stringify(latest_controller_tabs_settings)}`);
|
||||
for (const id in latest_controller_tabs_settings) {
|
||||
if (latest_controller_tabs_settings.hasOwnProperty(id)) {
|
||||
const value = latest_controller_tabs_settings[id];
|
||||
let input = document.getElementById(id);
|
||||
if (input) {
|
||||
if (id.indexOf('apply_') === 0) {
|
||||
input.checked = value;
|
||||
} else {
|
||||
input.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (document.isStarting && settings.get('settings.apply_last_settings_on_launch')) {
|
||||
applyRyzenSettings();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will show or display options based on apply checkboxes value.
|
||||
*/
|
||||
function toggleOptionDisplayBasedOnApplyCheckbox() {
|
||||
var checkbox_toggle_options = document.querySelectorAll('#main-container *[id$="-tab"] input[id^=apply_]');
|
||||
const hideOptionBasedOnInput = function (input) {
|
||||
if (input.checked) {
|
||||
input.parentElement.parentElement.nextElementSibling.removeAttribute('hidden');
|
||||
} else {
|
||||
input.parentElement.parentElement.nextElementSibling.setAttribute('hidden', '');
|
||||
}
|
||||
}
|
||||
Array.from(checkbox_toggle_options).forEach(input => {
|
||||
hideOptionBasedOnInput(input);
|
||||
input.addEventListener('change', function(event) {
|
||||
hideOptionBasedOnInput(input);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen settings tab inputs to save their values.
|
||||
*/
|
||||
function registerEventListenerForSettingsInput() {
|
||||
const settings = require('electron-settings');
|
||||
|
||||
toggleOptionDisplayBasedOnApplyCheckbox();
|
||||
|
||||
var apply_last_settings_on_launch = document.getElementById('apply_last_settings_on_launch');
|
||||
apply_last_settings_on_launch.addEventListener('change', function() {
|
||||
|
||||
settings.set(
|
||||
"settings",
|
||||
Object.assign(
|
||||
{},
|
||||
settings.get('settings'),
|
||||
{ apply_last_settings_on_launch: !!apply_last_settings_on_launch.checked }
|
||||
)
|
||||
);
|
||||
|
||||
});
|
||||
var minimize_to_tray = document.getElementById('minimize_to_tray');
|
||||
minimize_to_tray.addEventListener('change', function() {
|
||||
|
||||
settings.set(
|
||||
"settings",
|
||||
Object.assign(
|
||||
{},
|
||||
settings.get('settings'),
|
||||
{ minimize_to_tray: !!minimize_to_tray.checked }
|
||||
)
|
||||
);
|
||||
|
||||
});
|
||||
var start_minimized = document.getElementById('start_minimized');
|
||||
start_minimized.addEventListener('change', function() {
|
||||
|
||||
settings.set(
|
||||
"settings",
|
||||
Object.assign(
|
||||
{},
|
||||
settings.get('settings'),
|
||||
{ start_minimized: !!start_minimized.checked }
|
||||
)
|
||||
);
|
||||
|
||||
});
|
||||
var reapply_periodically = document.getElementById('reapply_periodically');
|
||||
reapply_periodically.addEventListener('change', function() {
|
||||
reApplyPeriodically(reapply_periodically.value);
|
||||
|
||||
settings.set(
|
||||
"settings",
|
||||
Object.assign(
|
||||
{},
|
||||
settings.get('settings'),
|
||||
{ reapply_periodically: reapply_periodically.value }
|
||||
)
|
||||
);
|
||||
|
||||
});
|
||||
var start_at_boot = document.getElementById('start_at_boot');
|
||||
start_at_boot.addEventListener('change', function() {
|
||||
|
||||
settings.set(
|
||||
"settings",
|
||||
Object.assign(
|
||||
{},
|
||||
settings.get('settings'),
|
||||
{ start_at_boot: !!start_at_boot.checked }
|
||||
)
|
||||
);
|
||||
|
||||
updateScheduledStartOnBoot(!!start_at_boot.checked);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply display version in appropriate zone.
|
||||
*/
|
||||
function displayVersion() {
|
||||
const pjson = require('./package.json');
|
||||
document.getElementById('version').innerHTML = `v${pjson.version}${isDevMode() ? '-dev' : ''}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-apply flow for "reapply_periodically" settings.
|
||||
* @param {number} seconds Interval in seconds between each apply.
|
||||
*/
|
||||
function reApplyPeriodically(seconds) {
|
||||
seconds = parseInt(seconds) >= 0 ? parseInt(seconds) : 0;
|
||||
appendLog(`reApplyPeriodically(): seconds = ${seconds}`);
|
||||
appendLog(`reApplyPeriodically(): document.reapplyLoop = ${document.reapplyLoop}`);
|
||||
clearInterval(document.reapplyLoop);
|
||||
document.reapplyLoop = false;
|
||||
|
||||
if (seconds <= 0) {
|
||||
if (!document.isStarting) {
|
||||
notification('primary', "Ryzen Controller will stop applying RyzenAdj periodically.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
document.reapplyLoop = setInterval(applyRyzenSettings, seconds * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will recreate shortcut on launch ... no other solution for now :(
|
||||
*/
|
||||
function recreateShortcut() {
|
||||
const settings = require('electron-settings');
|
||||
|
||||
if (!!settings.get('settings.first_launch')) {
|
||||
let app = require('electron').remote.app;
|
||||
let fs = require('fs');
|
||||
try {
|
||||
var shortcut_path = app.getPath('desktop') + "\\Ryzen Controller";
|
||||
fs.unlink(shortcut_path, console.log);
|
||||
fs.symlink(app.getPath('exe'), shortcut_path, function (err) {
|
||||
if (err) {
|
||||
notification("danger", "Shortcut can't be created, please check log tabs for more info.");
|
||||
appendLog(`recreateShortcut(): ${err}`);
|
||||
}
|
||||
else {
|
||||
notification('primary', "A shortcut has been created on desktop.");
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
appendLog(`recreateShortcut() ${error}`);
|
||||
Sentry.captureException(`recreateShortcut() ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return an object completed with the current settings from inputs.
|
||||
* @param {string} keyType "inputId" or "ryzenadjArgs"
|
||||
*/
|
||||
function getCurrentSettings(keyType) {
|
||||
if (keyType === "ryzenadjArgs") {
|
||||
|
||||
const options_data = require('./js/options_data.json');
|
||||
var settingsToBeUsed = {};
|
||||
for (const elementId in options_data) {
|
||||
if (options_data.hasOwnProperty(elementId)) {
|
||||
const optionData = options_data[elementId];
|
||||
const ryzenadjArg = optionData.ryzenadj_arg;
|
||||
if (document.getElementById('apply_' + elementId).checked) {
|
||||
settingsToBeUsed[ryzenadjArg] = document.getElementById(elementId).value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appendLog('getCurrentSettings(): ' + JSON.stringify(settingsToBeUsed));
|
||||
return settingsToBeUsed;
|
||||
|
||||
} else {
|
||||
var inputs = document.querySelectorAll('#main-container *[id$="-tab"] input');
|
||||
var currentSettings = {};
|
||||
inputs.forEach(element => {
|
||||
let id = element.id;
|
||||
let value = element.value;
|
||||
if (id.indexOf('apply_') === 0) {
|
||||
value = element.checked;
|
||||
}
|
||||
currentSettings[id] = value;
|
||||
});
|
||||
return currentSettings;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will check for new release.
|
||||
*/
|
||||
function checkForNewRelease() {
|
||||
var request = new XMLHttpRequest();
|
||||
const version = require('./package.json').version;
|
||||
|
||||
try {
|
||||
request.open('GET', 'https://gitlab.com/api/v4/projects/11046417/releases', true);
|
||||
|
||||
request.onload = function() {
|
||||
if (this.status >= 200 && this.status < 400) {
|
||||
var resp = {};
|
||||
try {
|
||||
resp = JSON.parse(this.response);
|
||||
} catch (error) {
|
||||
console.log('WARNING: unable to check if a new version is available.', error);
|
||||
return;
|
||||
}
|
||||
if (resp[0].tag_name !== version) {
|
||||
notification('primary', "A new vesion is available, please check the release tab.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
request.send();
|
||||
} catch (error) {
|
||||
console.log('Unable to check if new release is available.', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will change the BCD entry value for userplatformclock.
|
||||
*
|
||||
* @param {string} value BCD entry value for userplatformclock: "true" or "false".
|
||||
*/
|
||||
function toggleHpet(value) {
|
||||
value = value === "true" ? "true" : "false";
|
||||
const BcdeditExecutablePath = "C:\\Windows\\System32\\bcdedit.exe";
|
||||
const parameters = [
|
||||
"/set",
|
||||
"useplatformclock",
|
||||
value,
|
||||
];
|
||||
|
||||
const child = require('child_process').execFile;
|
||||
child(BcdeditExecutablePath, parameters, function(err, data) {
|
||||
var output = data.toString();
|
||||
if (err) {
|
||||
notification('danger', err + '<br/>' + output);
|
||||
}
|
||||
else if (output) {
|
||||
notification('success', 'Bcdedit output:<br/>' + output);
|
||||
saveLatestUsedSettings();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Any element containing class "windows-only" will be hidden on other platform.
|
||||
*/
|
||||
function handlePlatformSpecificDisplay() {
|
||||
var windows_only_elements = document.getElementsByClassName('windows-only');
|
||||
if (!isWindows()) {
|
||||
for (const item of windows_only_elements) {
|
||||
item.setAttribute('hidden', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will delete scheduled task to start ryzen controller on session start then recreate it if isEnable is true.
|
||||
*
|
||||
* @param {bool} toBeEnabled Is auto launch should be enabled?
|
||||
*/
|
||||
function updateScheduledStartOnBoot(toBeEnabled) {
|
||||
const app = require('electron').remote.app;
|
||||
window.app = app;
|
||||
const AutoLaunch = require('auto-launch');
|
||||
let autoLaunch = new AutoLaunch({
|
||||
name: 'Ryzen Controller'
|
||||
});
|
||||
autoLaunch.isEnabled().then((isEnabled) => {
|
||||
console.log(`toBeEnabled: ${toBeEnabled} isEnabled: ${isEnabled}`);
|
||||
|
||||
try {
|
||||
if (isEnabled) {
|
||||
autoLaunch.disable();
|
||||
}
|
||||
if (toBeEnabled) {
|
||||
autoLaunch.enable();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("WARNING: Unable to manage start on boot.", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if dev mode.
|
||||
*/
|
||||
function isDevMode() {
|
||||
return !require('electron').remote.app.isPackaged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will listen for system events and handle status for it.
|
||||
*/
|
||||
function handleAcStatusChanges() {
|
||||
const powerMonitor = require('electron').remote.powerMonitor;
|
||||
const settings = require('electron-settings');
|
||||
|
||||
let applyPresetOnAcStatusChange = function(presetName) {
|
||||
appendLog(`applyPresetOnAcStatusChange(${presetName})`);
|
||||
if (!presetName) {
|
||||
return;
|
||||
}
|
||||
preset_apply(presetName);
|
||||
};
|
||||
|
||||
powerMonitor.on('on-ac', () => {
|
||||
applyPresetOnAcStatusChange(settings.get(`auto-apply.update-ac-plugged-in`));
|
||||
});
|
||||
powerMonitor.on('on-battery', () => {
|
||||
applyPresetOnAcStatusChange(settings.get(`auto-apply.update-ac-plugged-out`));
|
||||
});
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
{
|
||||
"slow_ppt_constant_time": {
|
||||
"description": "This define the period to be used out of boost period to deliver a constant power to be delivered to the socket.",
|
||||
"label": "Package Power Tracking (PPT) - Slow period",
|
||||
"tab": "Power Settings",
|
||||
"min": "1",
|
||||
"max": "3600",
|
||||
"step": "1",
|
||||
"default": "900",
|
||||
"ryzenadj_arg": "--slow-time=",
|
||||
"ryzenadj_value_convert": "toThousand"
|
||||
},
|
||||
"psi0_current_limit": {
|
||||
"description": "The limit of current we let the motherboard deliver to the PSI0.",
|
||||
"label": "PSI0 Current Limit (mA)",
|
||||
"tab": "Power Settings",
|
||||
"min": "20",
|
||||
"max": "100",
|
||||
"step": "1",
|
||||
"default": "20",
|
||||
"ryzenadj_arg": "--psi0-current=",
|
||||
"ryzenadj_value_convert": "toHex"
|
||||
},
|
||||
"vrm_current_m_a": {
|
||||
"description": "The limit of current we let the motherboard deliver to the CPU.",
|
||||
"label": "VRM Current (A)",
|
||||
"tab": "Power Settings",
|
||||
"min": "20",
|
||||
"max": "75",
|
||||
"step": "1",
|
||||
"default": "30",
|
||||
"ryzenadj_arg": "--vrmmax-current=",
|
||||
"ryzenadj_value_convert": "toHex"
|
||||
},
|
||||
"min_gfxclk_frequency": {
|
||||
"description": "The minimum clock speed the integrated (Vega) GPU is allowed to run at.",
|
||||
"label": "Minimum Vega iGPU Clock Frequency (Mhz)",
|
||||
"tab": "GPU Settings",
|
||||
"min": "400",
|
||||
"max": "1300",
|
||||
"step": "1",
|
||||
"default": "400",
|
||||
"ryzenadj_arg": "--min-gfxclk=",
|
||||
"ryzenadj_value_convert": "roundTen"
|
||||
},
|
||||
"max_gfxclk_frequency": {
|
||||
"description": "The maximum clock speed the integrated (Vega) GPU is allowed to run at.",
|
||||
"label": "Maximum Vega iGPU Clock Frequency (Mhz)",
|
||||
"tab": "GPU Settings",
|
||||
"min": "400",
|
||||
"max": "1300",
|
||||
"step": "1",
|
||||
"default": "1100",
|
||||
"ryzenadj_arg": "--max-gfxclk=",
|
||||
"ryzenadj_value_convert": "roundTen"
|
||||
},
|
||||
"min_fclk_frequency": {
|
||||
"description": "Infinity Fabric is AMD's marketing term for the bus connection that connects processor dies (GPU/CPU). This define the bus's min. clock limit.",
|
||||
"label": "Minimum Infinity Fabric frequency (Mhz)",
|
||||
"tab": "GPU Settings",
|
||||
"min": "800",
|
||||
"max": "1600",
|
||||
"step": "1",
|
||||
"default": "800",
|
||||
"ryzenadj_arg": "--min-fclk-frequency=",
|
||||
"ryzenadj_value_convert": false
|
||||
},
|
||||
"max_fclk_frequency": {
|
||||
"description": "Infinity Fabric is AMD's marketing term for the bus connection that connects processor dies (GPU/CPU). This define the bus's max. clock limit.",
|
||||
"label": "Maximum Infinity Fabric frequency (Mhz)",
|
||||
"tab": "GPU Settings",
|
||||
"min": "800",
|
||||
"max": "1600",
|
||||
"step": "1",
|
||||
"default": "1200",
|
||||
"ryzenadj_arg": "--max-fclk-frequency=",
|
||||
"ryzenadj_value_convert": false
|
||||
},
|
||||
"temperature_limit_c": {
|
||||
"description": "The temperature the CPU can reach before boost levels off.",
|
||||
"label": "Temperature Limit (°C)",
|
||||
"tab": "CPU Settings",
|
||||
"min": "50",
|
||||
"max": "100",
|
||||
"step": "1",
|
||||
"default": "75",
|
||||
"ryzenadj_arg": "--tctl-temp=",
|
||||
"ryzenadj_value_convert": false
|
||||
},
|
||||
"stapm_limit_w": {
|
||||
"description": "Skin Temperature Aware Power Management. This will define the socket power package limit which is used to manage the device boost period.",
|
||||
"label": "CPU TDP (W)",
|
||||
"tab": "CPU Settings",
|
||||
"min": "5",
|
||||
"max": "60",
|
||||
"step": "1",
|
||||
"default": "20",
|
||||
"ryzenadj_arg": "--stapm-limit=",
|
||||
"ryzenadj_value_convert": "toThousand"
|
||||
},
|
||||
"stapm_time_ms": {
|
||||
"description": "Skin Temperature Aware Power Management. This will define the boost period to be used.",
|
||||
"label": "CPU Boost Period",
|
||||
"tab": "CPU Settings",
|
||||
"min": "1",
|
||||
"max": "3600",
|
||||
"step": "1",
|
||||
"default": "900",
|
||||
"ryzenadj_arg": "--stapm-time=",
|
||||
"ryzenadj_value_convert": "toThousand"
|
||||
},
|
||||
"ppt_fast_limit_w": {
|
||||
"description": "The amount of power the CPU can draw while boost levels on.",
|
||||
"label": "CPU Boost TDP (W)",
|
||||
"tab": "CPU Settings",
|
||||
"min": "5",
|
||||
"max": "60",
|
||||
"step": "1",
|
||||
"default": "25",
|
||||
"ryzenadj_arg": "--fast-limit=",
|
||||
"ryzenadj_value_convert": "toThousand"
|
||||
},
|
||||
"ppt_slow_limit_w": {
|
||||
"description": "The amount of power the CPU can draw while boost levels off.",
|
||||
"label": "CPU Min TDP (W)",
|
||||
"tab": "CPU Settings",
|
||||
"min": "5",
|
||||
"max": "60",
|
||||
"step": "1",
|
||||
"default": "10",
|
||||
"ryzenadj_arg": "--slow-limit=",
|
||||
"ryzenadj_value_convert": "toThousand"
|
||||
}
|
||||
}
|
278
js/preset.js
@ -1,278 +0,0 @@
|
||||
/**
|
||||
* Will fill the export preset modal textarea with the preset data.
|
||||
*/
|
||||
function preset_export() {
|
||||
const modalTextArea = document.getElementById('modal-export-preset-textarea');
|
||||
const settings = require('electron-settings');
|
||||
var presets = settings.get('presets');
|
||||
|
||||
presets = JSON.stringify(presets);
|
||||
modalTextArea.innerHTML = btoa(unescape(encodeURIComponent(presets)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Will import the preset from the export preset modal textarea.
|
||||
*/
|
||||
function preset_import() {
|
||||
const modalTextArea = document.getElementById('modal-import-preset-textarea');
|
||||
const settings = require('electron-settings');
|
||||
var currentPresets = settings.get('presets');
|
||||
var presetsToBeImported;
|
||||
|
||||
try {
|
||||
presetsToBeImported = decodeURIComponent(escape(atob(modalTextArea.value)));
|
||||
presetsToBeImported = JSON.parse(presetsToBeImported);
|
||||
} catch (e) {
|
||||
notification('danger', 'Unable to import presets, malformed data.');
|
||||
appendLog(`preset_import() ${e}`);
|
||||
return;
|
||||
}
|
||||
|
||||
var updatedPresets = Object.assign(
|
||||
{},
|
||||
currentPresets,
|
||||
presetsToBeImported
|
||||
);
|
||||
settings.set('presets', updatedPresets);
|
||||
preset_updateList();
|
||||
modalTextArea.innerText = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Will save the current settings to a new preset.
|
||||
*/
|
||||
function preset_createNewPreset() {
|
||||
const settingsToBeSaved = getCurrentSettings("inputId");
|
||||
const currentPresets = require('electron-settings').get('presets') || {};
|
||||
var newPresetName = document.getElementById('new_preset_name').value;
|
||||
|
||||
if (!newPresetName) {
|
||||
notification('danger', 'You must provide a preset name.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof currentPresets[newPresetName] !== "undefined") {
|
||||
newPresetName = preset_findUnusedPresetName(newPresetName);
|
||||
notification('warning', `This preset name already exist, your preset has been saved with the name "${newPresetName}".`);
|
||||
}
|
||||
|
||||
const newPresetList = Object.assign(
|
||||
{},
|
||||
currentPresets,
|
||||
{ [newPresetName]: settingsToBeSaved }
|
||||
);
|
||||
|
||||
require('electron-settings').set('presets', newPresetList);
|
||||
appendLog(`preset_createNewPreset(): Saved preset ${newPresetName}, ${JSON.stringify(newPresetList)}`);
|
||||
preset_updateList();
|
||||
if (newPresetName === document.getElementById('new_preset_name').value) {
|
||||
notification('success', `The preset ${newPresetName} has been saved.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This recursive function will return an available preset name to be used to save a preset.
|
||||
*
|
||||
* @param {string} newPresetName The preset name to be edited.
|
||||
* @param {number} suffix The preset name suffix
|
||||
*/
|
||||
function preset_findUnusedPresetName(newPresetName, suffix = 1) {
|
||||
const currentPresets = require('electron-settings').get('presets') || {};
|
||||
if (typeof currentPresets[`${newPresetName}${suffix}`] !== "undefined") {
|
||||
suffix++;
|
||||
return preset_findUnusedPresetName(newPresetName, suffix);
|
||||
}
|
||||
return `${newPresetName}${suffix}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will save the preset to be enabled on AC plugged out.
|
||||
*/
|
||||
function preset_enableAutoApplyOnAcStatusChange(statusName, presetName) {
|
||||
const settings = require('electron-settings');
|
||||
const status = {
|
||||
"update-ac-plugged-in": `will be applied on AC plugged in.`,
|
||||
"update-ac-plugged-out": `will be applied on AC plugged out.`,
|
||||
};
|
||||
|
||||
if (typeof status[statusName] === "undefined") {
|
||||
let message = `Error while updating auto apply on AC status change.`;
|
||||
notification('danger', message);
|
||||
console.log(`preset_enableAutoApplyOnAcStatusChange(statusName:"${statusName}", presetName:"${presetName}")`);
|
||||
Sentry.captureException(new Error(message));
|
||||
return;
|
||||
}
|
||||
|
||||
settings.set(`auto-apply.${statusName}`, presetName);
|
||||
|
||||
if (presetName) {
|
||||
notification('primary', `Preset "${presetName}" ${status[statusName]}`);
|
||||
} else {
|
||||
notification('primary', `No preset ${status[statusName]}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will update the preset tab based on saved presets.
|
||||
*/
|
||||
function preset_updateList() {
|
||||
var presetTab = document.getElementById('presetTab');
|
||||
const currentPresets = require('electron-settings').get('presets') || {};
|
||||
|
||||
var content = '';
|
||||
content += /*html*/`
|
||||
<table class="uk-table uk-table-striped uk-table-hover uk-table-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th colspan="2">Apply on</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
if (Object.keys(currentPresets).length === 0) {
|
||||
content += /*html*/`<li>No preset has been created yet, import them or use the "Save to preset" button on Controller tab to create one.</li>`;
|
||||
}
|
||||
|
||||
for (const presetName in currentPresets) {
|
||||
if (currentPresets.hasOwnProperty(presetName)) {
|
||||
const preset = currentPresets[presetName];
|
||||
|
||||
let valueSummary = [];
|
||||
for (const key in preset) {
|
||||
if (preset.hasOwnProperty(key) && key.indexOf('_range') !== -1 && key.indexOf('apply_') != 0) {
|
||||
const value = preset[key];
|
||||
valueSummary.push(value);
|
||||
}
|
||||
}
|
||||
valueSummary.join(', ');
|
||||
|
||||
content += /*html*/`
|
||||
<tr class="uk-margin">
|
||||
<td class="preset">
|
||||
<span class="uk-text-lead">${presetName}</span>
|
||||
<i class="uk-text-small">${valueSummary}</i>
|
||||
</td>
|
||||
<td class="uk-table-expand preset">
|
||||
<label style="cursor: pointer;">
|
||||
<input
|
||||
value="${presetName}"
|
||||
class="uk-radio onAcStatusChange"
|
||||
type="radio"
|
||||
name="update-ac-plugged-in"
|
||||
${presetName === require('electron-settings').get('auto-apply.update-ac-plugged-in') ? 'checked' : ''}
|
||||
/>
|
||||
AC plugged in
|
||||
</label>
|
||||
</td>
|
||||
<td class="uk-table-expand preset">
|
||||
<label style="cursor: pointer;">
|
||||
<input
|
||||
value="${presetName}"
|
||||
class="uk-radio onAcStatusChange"
|
||||
type="radio"
|
||||
name="update-ac-plugged-out"
|
||||
${presetName === require('electron-settings').get('auto-apply.update-ac-plugged-out') ? 'checked' : ''}
|
||||
/>
|
||||
AC plugged out
|
||||
</label>
|
||||
</td>
|
||||
<td class="preset">
|
||||
<button class="uk-button uk-button-danger delete" type="button" onClick="preset_deletion('${presetName}')">
|
||||
Delete
|
||||
</button>
|
||||
<button class="uk-button uk-button-primary" type="button" onClick="preset_apply('${presetName}')">
|
||||
Apply
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
}
|
||||
content += /*html*/`
|
||||
<tr class="autoapply">
|
||||
<td><span class="uk-align-right">Disable auto apply</span></td>
|
||||
<td>
|
||||
<label style="cursor: pointer;">
|
||||
<input
|
||||
value=""
|
||||
class="uk-radio onAcStatusChange"
|
||||
type="radio"
|
||||
name="update-ac-plugged-in"
|
||||
${!require('electron-settings').get('auto-apply.update-ac-plugged-in') ? 'checked' : ''}
|
||||
/>
|
||||
On charging
|
||||
</label>
|
||||
</td>
|
||||
<td>
|
||||
<label style="cursor: pointer;">
|
||||
<input
|
||||
value=""
|
||||
class="uk-radio onAcStatusChange"
|
||||
type="radio"
|
||||
name="update-ac-plugged-out"
|
||||
${!require('electron-settings').get('auto-apply.update-ac-plugged-out') ? 'checked' : ''}
|
||||
/>
|
||||
On discharging
|
||||
</label>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tbody>
|
||||
</table>
|
||||
`;
|
||||
presetTab.innerHTML = content;
|
||||
var acStatusChangeRadios = document.querySelectorAll('.onAcStatusChange');
|
||||
|
||||
Array.from(acStatusChangeRadios).forEach(radio => {
|
||||
radio.addEventListener('click', function(event) {
|
||||
preset_enableAutoApplyOnAcStatusChange(this.name, this.value);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Will check if the preset exists.
|
||||
*
|
||||
* @param {String} name The preset name to look for.
|
||||
*/
|
||||
function preset_isExist(name) {
|
||||
const preset = require('electron-settings').get(`presets`)[name];
|
||||
return !!preset;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will apply the preset you asked for.
|
||||
* @param {string} presetName The preset name to be applied.
|
||||
*/
|
||||
function preset_apply(presetName) {
|
||||
if (!preset_isExist(presetName)) {
|
||||
notification('danger', `Unable to apply unexisting preset "${presetName}".`);
|
||||
return;
|
||||
}
|
||||
|
||||
const preset = require('electron-settings').get(`presets`)[presetName];
|
||||
appendLog(`preset_apply(): preset ${presetName}: ${JSON.stringify(preset)}`);
|
||||
|
||||
var ret = require('electron-settings').set("latest_controller_tabs_settings", preset);
|
||||
appendLog(`preset_apply(): saved preset: ${JSON.stringify(ret)}`);
|
||||
|
||||
loadLatestUsedSettings();
|
||||
applyRyzenSettings();
|
||||
toggleOptionDisplayBasedOnApplyCheckbox();
|
||||
}
|
||||
|
||||
/**
|
||||
* This will delete the preset you asked for.
|
||||
* @param {string} presetName The preset name to be deleted.
|
||||
*/
|
||||
function preset_deletion(presetName) {
|
||||
var presets = require('electron-settings').get(`presets`);
|
||||
delete presets[presetName];
|
||||
require('electron-settings').set(`presets`, presets);
|
||||
notification('success', `The preset ${presetName} has been deleted.`);
|
||||
preset_updateList();
|
||||
}
|
249
main.js
@ -1,249 +0,0 @@
|
||||
// Handle setupevents as quickly as possible
|
||||
const setupEvents = require('./installers/setupEvents')
|
||||
|
||||
|
||||
// squirrel event handled and app will exit in 1000ms, so don't do anything else
|
||||
if (!setupEvents.handleSquirrelEvent()) {
|
||||
|
||||
// Modules to control application life and create native browser window
|
||||
const {app, BrowserWindow, Menu, Tray} = require('electron')
|
||||
const settings = require('electron-settings');
|
||||
|
||||
// Check for latest used version and clear settings if needed.
|
||||
const old_version = settings.get('settings.last_used_version');
|
||||
const new_version = require('./package.json').version;
|
||||
if (old_version && old_version !== new_version) {
|
||||
var compareVersions = require('compare-versions');
|
||||
|
||||
/**
|
||||
* Since 1.4.0, ryzenadj is included in the windows package.
|
||||
* So we are removing ryzenadj path as it can be included.
|
||||
*/
|
||||
if (compareVersions(old_version, '1.4.0') <= 0) {
|
||||
settings.delete('settings.ryzen_adj_path');
|
||||
}
|
||||
|
||||
/**
|
||||
* Since 1.11.0 we added new settings and apply checkbox,
|
||||
* We need to add new settings to presets.
|
||||
*/
|
||||
if (compareVersions(old_version, '1.11.0') <= 0) {
|
||||
const update_latest_settings_to_1_11_0 = function(settings) {
|
||||
var updated_settings = {};
|
||||
for (const setting_name in settings) {
|
||||
if (settings.hasOwnProperty(setting_name)) {
|
||||
const setting_value = settings[setting_name];
|
||||
// Register current setting.
|
||||
updated_settings[setting_name] = setting_value;
|
||||
// Add apply checkbox to any non-range settings.
|
||||
if (setting_name.indexOf('_range') <= 0) {
|
||||
if (setting_name.indexOf('apply_') <= 0) {
|
||||
updated_settings[`apply_${setting_name}`] = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Adding missing options.
|
||||
updated_settings['apply_stapm_time_ms'] = false;
|
||||
updated_settings['apply_psi0_current_limit'] = false;
|
||||
return updated_settings;
|
||||
};
|
||||
const update_presets_to_1_11_0 = function(preset_list) {
|
||||
var updated_preset_list = {};
|
||||
// For each preset.
|
||||
for (const preset_name in preset_list) {
|
||||
if (preset_list.hasOwnProperty(preset_name)) {
|
||||
const preset_settings = preset_list[preset_name];
|
||||
updated_preset_list[preset_name] = {};
|
||||
// For each setting.
|
||||
for (const setting_name in preset_settings) {
|
||||
if (preset_settings.hasOwnProperty(setting_name)) {
|
||||
const setting_value = preset_settings[setting_name];
|
||||
// Register current setting.
|
||||
updated_preset_list[preset_name][setting_name] = setting_value;
|
||||
if (setting_name.indexOf('_range') <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (setting_name.indexOf('apply_') <= 0) {
|
||||
// Add apply checkbox.
|
||||
updated_preset_list[preset_name][`apply_${setting_name}`] = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Adding missing options.
|
||||
updated_preset_list[preset_name]['apply_stapm_time_ms'] = false;
|
||||
updated_preset_list[preset_name]['apply_psi0_current_limit'] = false;
|
||||
}
|
||||
}
|
||||
return updated_preset_list;
|
||||
};
|
||||
settings.set('presets', update_presets_to_1_11_0(settings.get('presets')));
|
||||
settings.set('latest_controller_tabs_settings', update_latest_settings_to_1_11_0(settings.get('latest_controller_tabs_settings')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Since 1.12.0, new option to ryzenadj.
|
||||
*/
|
||||
if (compareVersions(old_version, '1.12.0') <= 0) {
|
||||
const update_preset_to_1_12_0 = function(settings) {
|
||||
// Adding missing options.
|
||||
settings['apply_max_gfxclk_frequency'] = false;
|
||||
settings['apply_min_gfxclk_frequency'] = false;
|
||||
settings['apply_min_socclk_frequency'] = false;
|
||||
settings['apply_max_socclk_frequency'] = false;
|
||||
return settings;
|
||||
};
|
||||
const update_presets_to_1_12_0 = function(preset_list) {
|
||||
// For each preset.
|
||||
for (const preset_name in preset_list) {
|
||||
if (preset_list.hasOwnProperty(preset_name)) {
|
||||
preset_list[preset_name] = update_preset_to_1_12_0(preset_list[preset_name]);
|
||||
}
|
||||
}
|
||||
return preset_list;
|
||||
};
|
||||
settings.set('presets', update_presets_to_1_12_0(settings.get('presets')));
|
||||
settings.set('latest_controller_tabs_settings', update_preset_to_1_12_0(settings.get('latest_controller_tabs_settings')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Since 1.14.0, login managed through s=windows scheduler
|
||||
*/
|
||||
if (compareVersions(old_version, '1.14.0') <= 0) {
|
||||
// Ensure login on start false.
|
||||
app.setLoginItemSettings({ openAtLogin: false });
|
||||
}
|
||||
|
||||
settings.set('settings',
|
||||
Object.assign(
|
||||
{},
|
||||
settings.get('settings'),
|
||||
{
|
||||
last_used_version: require('./package.json').version,
|
||||
first_launch: true,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
if (!old_version) {
|
||||
settings.set('settings',
|
||||
Object.assign(
|
||||
{},
|
||||
settings.get('settings'),
|
||||
{
|
||||
last_used_version: require('./package.json').version,
|
||||
first_launch: true,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
let tray
|
||||
|
||||
function createWindow () {
|
||||
let appIcon = '';
|
||||
if (require('os').platform() === 'win32') {
|
||||
appIcon = __dirname + '/assets/icon.ico';
|
||||
} else {
|
||||
appIcon = __dirname + '/assets/icon.png';
|
||||
}
|
||||
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1000,
|
||||
height: 600,
|
||||
minWidth: 950,
|
||||
minHeight: 400,
|
||||
frame: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
webviewTag: true,
|
||||
},
|
||||
icon: appIcon,
|
||||
show: false,
|
||||
});
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
mainWindow.setResizable(true);
|
||||
|
||||
if (!settings.get('settings.start_minimized')) {
|
||||
mainWindow.show();
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
}
|
||||
|
||||
mainWindow.setOpacity(0.98);
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
|
||||
mainWindow.on('minimize',function(event){
|
||||
if (settings.get('settings.minimize_to_tray')) {
|
||||
event.preventDefault();
|
||||
mainWindow.hide();
|
||||
}
|
||||
});
|
||||
|
||||
var contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'Show App',
|
||||
click: function () {
|
||||
mainWindow.show();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
click: function () {
|
||||
app.isQuiting = true;
|
||||
app.quit();
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
tray = new Tray(appIcon);
|
||||
tray.setContextMenu(contextMenu);
|
||||
tray.setIgnoreDoubleClickEvents(true);
|
||||
tray.on('click', function() {
|
||||
mainWindow.show();
|
||||
});
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
}
|
2521
package-lock.json
generated
160
package.json
@ -1,42 +1,150 @@
|
||||
{
|
||||
"name": "ryzencontroller",
|
||||
"productName": "RyzenController",
|
||||
"version": "1.17.0",
|
||||
"name": "ryzen-controller",
|
||||
"version": "2.0.0",
|
||||
"description": "A minimal Electron application to use ryzenAdj through a friendly interface.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"package-win32": "electron-packager . --overwrite --platform=win32 --arch=ia32 --prune=true --out=release-builds --ignore=\".*-builds\" --icon=\"assets/icon.ico\" --win32metadata.requested-execution-level=requireAdministrator",
|
||||
"package-linux": "electron-packager . ryzencontroller --overwrite --platform=linux --arch=x64 --out release-builds --ignore=\".*-builds\" --icon=\"assets/icon.ico\"",
|
||||
"build-exe": "node installers/windows/createinstaller.js",
|
||||
"build-deb": "electron-installer-debian --src=release-builds/ryzencontroller-linux-x64/ --dest=installer-builds/installers/ --arch=amd64",
|
||||
"build-rpm": "electron-installer-redhat --src=release-builds/ryzencontroller-linux-x64/ --dest=installer-builds/installers/ --arch=amd64"
|
||||
"all": "yarn clean && yarn dist-pack",
|
||||
"1______________________________________________________________": "echo Basic scripts",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"lint": "prettier --write src/**/*.{js,jsx,ts,tsx,json,css,scss,md}",
|
||||
"2______________________________________________________________": "echo Cleaning scripts",
|
||||
"clean": "yarn clean:dist && yarn clean:node && yarn clean:build && yarn clean:yarn",
|
||||
"clean:dist": "rm -rf dist",
|
||||
"clean:node": "rm -rf node_modules/* node_modules/.bin node_modules/.cache node_modules/.yarn-integrity",
|
||||
"clean:build": "rm -rf build",
|
||||
"clean:yarn": "yarn install --frozen-lockfile",
|
||||
"3______________________________________________________________": "echo Start dev env",
|
||||
"start": "run-p start:*",
|
||||
"start:electron": "wait-on http://localhost:3000/ && electron --no-sandbox --enable-transparent-visuals .",
|
||||
"start:react": "react-scripts start",
|
||||
"4______________________________________________________________": "echo To create unpacked app",
|
||||
"dist-unpack": "run-s dist-unpack:*",
|
||||
"dist-unpack:build": "yarn build",
|
||||
"dist-unpack:electron": "electron-builder --dir",
|
||||
"5______________________________________________________________": "echo To create packed app",
|
||||
"dist-pack": "run-s dist-pack:*",
|
||||
"dist-pack:build": "yarn build",
|
||||
"dist-pack:all": "electron-builder -wl",
|
||||
"dist-pack-win": "electron-builder -w",
|
||||
"dist-pack-linux": "electron-builder -l",
|
||||
"6______________________________________________________________": "echo Yarn scripts into docker (for linux)",
|
||||
"docker": "bash -c \"docker run --rm -ti -v node_modules:/project/node_modules -v /${PWD}:/project storm1er/electron-builder-wine-dubnium:1.0.2 bash\"",
|
||||
"docker-permission-fix": "docker run --rm -ti -v ${PWD}:/project storm1er/electron-builder-wine-dubnium:1.0.2 bash -c \"chown `id -u`:`id -g` -R .\""
|
||||
},
|
||||
"repository": "https://gitlab.com/le.storm1er/ryzen-controller",
|
||||
"main": "public/electron.js",
|
||||
"private": false,
|
||||
"author": {
|
||||
"name": "Ryzen Controller Team",
|
||||
"email": "incoming+ryzen-controller-team-ryzen-controller-11046417-issue-@incoming.gitlab.com",
|
||||
"url": "https://gitlab.com/ryzen-controller-team/ryzen-controller/"
|
||||
},
|
||||
"homepage": "./",
|
||||
"email": "quentin.decaunes@gmail.com",
|
||||
"keywords": [
|
||||
"Controller",
|
||||
"Mobile",
|
||||
"Ryzen",
|
||||
"Overclock"
|
||||
],
|
||||
"author": "le.storm1er",
|
||||
"license": "CC0-1.0",
|
||||
"optionalDependencies": {
|
||||
"electron-installer-debian": "^1.1.1",
|
||||
"electron-installer-redhat": "^1.0.1",
|
||||
"electron-winstaller": "^3.0.4"
|
||||
"repository": "https://gitlab.com/ryzen-controller-team/ryzen-controller",
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.4.0",
|
||||
"@testing-library/user-event": "^7.2.1",
|
||||
"@trodi/electron-splashscreen": "^1.0.0",
|
||||
"@types/jest": "^24.0.24",
|
||||
"@types/node": "^13.1.0",
|
||||
"@types/react": "^16.9.17",
|
||||
"@types/react-dom": "^16.9.4",
|
||||
"@types/react-router-dom": "^5.1.3",
|
||||
"@types/webpack-env": "^1.14.1",
|
||||
"auto-launch": "^5.0.5",
|
||||
"compare-versions": "^3.5.1",
|
||||
"electron-is-dev": "^1.1.0",
|
||||
"electron-react-devtools": "^0.5.3",
|
||||
"electron-settings": "^3.2.0",
|
||||
"fibers": "^4.0.2",
|
||||
"husky": "^3.1.0",
|
||||
"lint-staged": "^9.5.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"object-hash": "^2.0.1",
|
||||
"prettier": "^1.19.1",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-scripts": "3.3.0",
|
||||
"sass": "^1.24.0",
|
||||
"systeminformation": "^4.16.1",
|
||||
"typescript": "^3.7.4",
|
||||
"uikit": "^3.2.6",
|
||||
"uuidv4": "^6.0.0",
|
||||
"windows-scheduler": "https://github.com/Ryzen-Controller-Team/windows-scheduler.git#1e2cc67db8efb1474ba0fe780967f86ed68f36a3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^5.0.2",
|
||||
"electron-packager": "^13.1.0"
|
||||
"electron": "^7.1.7",
|
||||
"electron-builder": "20.*",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"wait-on": "^3.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"auto-launch": "^5.0.5",
|
||||
"compare-versions": "^3.4.0",
|
||||
"electron-settings": "^3.2.0",
|
||||
"fix-path": "^2.1.0",
|
||||
"jquery": "^3.4.1",
|
||||
"uikit": "^3.1.5",
|
||||
"uuid": "^3.3.2"
|
||||
"build": {
|
||||
"appId": "ryzen-team.app.ryzen-controller",
|
||||
"productName": "Ryzen Controller",
|
||||
"icon": "./build/icon.ico",
|
||||
"asarUnpack": [
|
||||
"**/build/bin/*"
|
||||
],
|
||||
"linux": {
|
||||
"category": "Utility",
|
||||
"icon": "./build/icon.png",
|
||||
"target": [
|
||||
{
|
||||
"target": "deb"
|
||||
},
|
||||
{
|
||||
"target": "rpm"
|
||||
}
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"perMachine": true,
|
||||
"allowToChangeInstallationDirectory": true
|
||||
},
|
||||
"win": {
|
||||
"requestedExecutionLevel": "requireAdministrator"
|
||||
}
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"prettier": {
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"printWidth": 120
|
||||
}
|
||||
}
|
||||
|
1
public/bin/auto-start-ryzen-controller.bat
Normal file
@ -0,0 +1 @@
|
||||
"%~dp0..\..\..\elevate.exe" "%~dp0..\..\..\..\Ryzen Controller.exe"
|
@ -1,2 +1,2 @@
|
||||
%~dp0\ryzenadj.exe --stapm-limit=40000 --fast-limit=45000 --slow-limit=45000 --tctl-temp=90
|
||||
%~dp0\ryzenadj.exe --stapm-limit=40000 --fast-limit=45000 --slow-limit=45000 --tctl-temp=90
|
||||
pause
|
@ -1,60 +1,60 @@
|
||||
/* SPDX-License-Identifier: LGPL */
|
||||
/* Copyright (C) 2019 Jiaxun Yang <jiaxun.yang@flygoat.com> */
|
||||
/* RyzenAdj API */
|
||||
|
||||
#ifndef RYZENADJ_H
|
||||
#define RYZENADJ_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "nb_smu_ops.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define EXP __declspec(dllexport)
|
||||
#define CALL __stdcall
|
||||
#else
|
||||
#define EXP
|
||||
#define CALL
|
||||
#endif
|
||||
|
||||
#define RYZENADJ_VER 5
|
||||
|
||||
typedef struct {
|
||||
nb_t nb;
|
||||
pci_obj_t pci_obj;
|
||||
smu_t mp1_smu;
|
||||
smu_t psmu;
|
||||
} *ryzen_access;
|
||||
|
||||
EXP ryzen_access CALL init_ryzenadj();
|
||||
|
||||
EXP void CALL cleanup_ryzenadj(ryzen_access ry);
|
||||
|
||||
EXP int CALL set_stapm_limit(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_fast_limit(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_slow_limit(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_slow_time(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_stapm_time(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_tctl_temp(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_vrm_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_vrmsoc_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_vrmmax_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_vrmsocmax_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_psi0_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_psi0soc_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_max_gfxclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_min_gfxclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_max_socclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_min_socclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_max_fclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_min_fclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_max_vcn(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_min_vcn(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_max_lclk(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_min_lclk(ryzen_access, uint32_t value);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
/* SPDX-License-Identifier: LGPL */
|
||||
/* Copyright (C) 2019 Jiaxun Yang <jiaxun.yang@flygoat.com> */
|
||||
/* RyzenAdj API */
|
||||
|
||||
#ifndef RYZENADJ_H
|
||||
#define RYZENADJ_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "nb_smu_ops.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define EXP __declspec(dllexport)
|
||||
#define CALL __stdcall
|
||||
#else
|
||||
#define EXP
|
||||
#define CALL
|
||||
#endif
|
||||
|
||||
#define RYZENADJ_VER 5
|
||||
|
||||
typedef struct {
|
||||
nb_t nb;
|
||||
pci_obj_t pci_obj;
|
||||
smu_t mp1_smu;
|
||||
smu_t psmu;
|
||||
} *ryzen_access;
|
||||
|
||||
EXP ryzen_access CALL init_ryzenadj();
|
||||
|
||||
EXP void CALL cleanup_ryzenadj(ryzen_access ry);
|
||||
|
||||
EXP int CALL set_stapm_limit(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_fast_limit(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_slow_limit(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_slow_time(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_stapm_time(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_tctl_temp(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_vrm_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_vrmsoc_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_vrmmax_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_vrmsocmax_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_psi0_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_psi0soc_current(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_max_gfxclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_min_gfxclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_max_socclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_min_socclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_max_fclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_min_fclk_freq(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_max_vcn(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_min_vcn(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_max_lclk(ryzen_access, uint32_t value);
|
||||
EXP int CALL set_min_lclk(ryzen_access, uint32_t value);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
135
public/electron.js
Normal file
@ -0,0 +1,135 @@
|
||||
const Splashscreen = require("@trodi/electron-splashscreen");
|
||||
|
||||
const electron = require("electron");
|
||||
const path = require("path");
|
||||
const isDev = require("electron-is-dev");
|
||||
const electronSettings = require("electron-settings");
|
||||
|
||||
const app = electron.app;
|
||||
const Tray = electron.Tray;
|
||||
const Menu = electron.Menu;
|
||||
const app_version_as_string = app.getVersion().replace(/\./g, "_") + (isDev ? "-dev" : "");
|
||||
|
||||
let mainWindow;
|
||||
let tray;
|
||||
|
||||
const currentSettings = () => {
|
||||
const localStorage = electronSettings.get(app_version_as_string);
|
||||
if (localStorage) {
|
||||
return localStorage.settings;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const createWindow = () => {
|
||||
let appIcon = "";
|
||||
if (require("os").platform() === "win32") {
|
||||
appIcon = __dirname + "/icon.ico";
|
||||
} else {
|
||||
appIcon = __dirname + "/icon.png";
|
||||
}
|
||||
|
||||
const mainWindowOpt = {
|
||||
width: 900,
|
||||
height: 680,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
sandbox: false,
|
||||
},
|
||||
icon: appIcon,
|
||||
show: false,
|
||||
transparent: true,
|
||||
};
|
||||
|
||||
let showOnStart = true;
|
||||
if (currentSettings()) {
|
||||
if (currentSettings().minimizeOnLaunch === true) {
|
||||
showOnStart = false;
|
||||
}
|
||||
}
|
||||
const splashTimeOut = 4000;
|
||||
if (showOnStart) {
|
||||
mainWindow = Splashscreen.initSplashScreen({
|
||||
windowOpts: mainWindowOpt,
|
||||
templateUrl: path.join(__dirname, "..", isDev ? "public" : "build", "splash.html"),
|
||||
delay: 0,
|
||||
minVisible: splashTimeOut,
|
||||
splashScreenOpts: {
|
||||
height: 900,
|
||||
width: 900,
|
||||
transparent: true,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
mainWindow = new electron.BrowserWindow(mainWindowOpt);
|
||||
mainWindow.hide();
|
||||
}
|
||||
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
mainWindow.loadURL(isDev ? "http://localhost:3000" : `file://${path.join(__dirname, "../build/index.html")}`);
|
||||
|
||||
var contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: "Show App",
|
||||
click: function() {
|
||||
mainWindow.show();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Quit",
|
||||
click: function() {
|
||||
app.isQuiting = true;
|
||||
app.quit();
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
tray = new Tray(appIcon);
|
||||
tray.setContextMenu(contextMenu);
|
||||
tray.setIgnoreDoubleClickEvents(true);
|
||||
tray.on("click", function() {
|
||||
mainWindow.show();
|
||||
});
|
||||
|
||||
mainWindow.on("minimize", function(event) {
|
||||
if (currentSettings()) {
|
||||
if (currentSettings().minimizeToTray) {
|
||||
event.preventDefault();
|
||||
// Weird behavior on linux where the "minimize" event is triggered when re-opening the app from tray icon.
|
||||
setTimeout(() => {
|
||||
mainWindow.show();
|
||||
mainWindow.hide();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.on("closed", () => (mainWindow = null));
|
||||
app.getWindow = function() {
|
||||
return mainWindow;
|
||||
};
|
||||
};
|
||||
|
||||
let isPrimaryInstance = app.requestSingleInstanceLock();
|
||||
if (!isPrimaryInstance) {
|
||||
app.quit();
|
||||
}
|
||||
|
||||
app.on("second-instance", () => {
|
||||
mainWindow.show();
|
||||
mainWindow.focus();
|
||||
});
|
||||
|
||||
app.on("ready", () => setTimeout(createWindow, 400));
|
||||
|
||||
app.on("window-all-closed", () => {
|
||||
if (process.platform !== "darwin") {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
if (mainWindow === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
public/icon.ico
Normal file
After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
38
public/index.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" style="min-height: 100%;">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Ryzen Controller</title>
|
||||
</head>
|
||||
<body style="min-height:100vh;">
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
25
public/manifest.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
6
public/splash.html
Normal file
@ -0,0 +1,6 @@
|
||||
<html style="margin:0 0 0 0;padding:0 0 0 0;">
|
||||
<head></head>
|
||||
<body style="margin:0 0 0 0;padding:0 0 0 0;">
|
||||
<img style="margin:0 0 0 0;padding:0 0 0 0;" width="900px" height="900px" src="./splash.png"/>
|
||||
</body>
|
||||
</html>
|
BIN
public/splash.png
Normal file
After Width: | Height: | Size: 376 KiB |
9
src/App.test.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
import App from "./App";
|
||||
|
||||
test("renders learn react link", () => {
|
||||
const { getByText } = render(<App />);
|
||||
const linkElement = getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
130
src/App.tsx
Normal file
@ -0,0 +1,130 @@
|
||||
import * as React from "react";
|
||||
import TopBar from "./components/TopBar";
|
||||
import SceneSelector from "./components/SceneSelector";
|
||||
import Scene from "./scenes/Scene";
|
||||
import { HashRouter as Router } from "react-router-dom";
|
||||
import SysInfoContext, { createMachineSignature, SysInfoState } from "./contexts/SysInfoContext";
|
||||
import LightModeContext from "./contexts/LightModeContext";
|
||||
import { checkNewVersion } from "./contexts/RyzenControllerAppContext";
|
||||
import LocaleContext, { getTranslation } from "./contexts/LocaleContext";
|
||||
import LocaleSelectorModal from "./components/LocaleSelectorModal";
|
||||
const si = window.require("systeminformation");
|
||||
|
||||
type AppState = {
|
||||
sysinfo: SysInfoState;
|
||||
lightMode: {
|
||||
mode: "light" | "dark";
|
||||
switch(): void;
|
||||
};
|
||||
locale: {
|
||||
is: AvailableLanguages;
|
||||
change(to: AvailableLanguages): void;
|
||||
getTranslation(id: string, fallback?: string, variables?: Record<string, string>): string;
|
||||
};
|
||||
};
|
||||
|
||||
class App extends React.Component<{}, AppState> {
|
||||
_isMounted = false;
|
||||
|
||||
_defaultSysinfo: SysInfoState = {
|
||||
cpu: false,
|
||||
graphics: false,
|
||||
mem: false,
|
||||
memLayout: false,
|
||||
system: false,
|
||||
bios: false,
|
||||
signature: false,
|
||||
};
|
||||
|
||||
state = {
|
||||
sysinfo: this._defaultSysinfo,
|
||||
lightMode: {
|
||||
mode: this.getLatestLightMode(),
|
||||
switch: this.switchLightMode.bind(this),
|
||||
},
|
||||
locale: {
|
||||
is: this.getLatestLocale(),
|
||||
change: this.changeLocale.bind(this),
|
||||
getTranslation: getTranslation,
|
||||
},
|
||||
};
|
||||
|
||||
switchLightMode(): void {
|
||||
let lightMode = window.require("electron-settings").get("lightMode") || "light";
|
||||
lightMode = lightMode === "light" ? "dark" : "light";
|
||||
window.require("electron-settings").set("lightMode", lightMode);
|
||||
this.setState({
|
||||
lightMode: {
|
||||
mode: lightMode,
|
||||
switch: this.switchLightMode.bind(this),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
changeLocale(to: AvailableLanguages): void {
|
||||
window.require("electron-settings").set("locale", to);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
getLatestLightMode(): "light" | "dark" {
|
||||
return window.require("electron-settings").get("lightMode") || "light";
|
||||
}
|
||||
|
||||
getLatestLocale(): AvailableLanguages {
|
||||
return window.require("electron-settings").get("locale") || "en";
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
si.getAllData()
|
||||
.then((data: SysInfoState) => {
|
||||
data.signature = createMachineSignature(data);
|
||||
this._isMounted && this.setState({ sysinfo: data });
|
||||
})
|
||||
.catch((error: string) => {
|
||||
this._isMounted &&
|
||||
this.setState({
|
||||
sysinfo: {
|
||||
...this.state.sysinfo,
|
||||
error: error,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
checkNewVersion();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const classes =
|
||||
this.state.lightMode.mode === "light" ? "uk-dark uk-background-default" : "uk-light uk-background-secondary";
|
||||
const body = window.document.getElementsByTagName("body").item(0);
|
||||
if (body) {
|
||||
body.className = classes;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<LocaleContext.Provider value={this.state.locale}>
|
||||
<SysInfoContext.Provider value={this.state.sysinfo}>
|
||||
<LightModeContext.Provider value={this.state.lightMode}>
|
||||
<Router>
|
||||
<div className="uk-card uk-margin-bottom">
|
||||
<TopBar />
|
||||
<SceneSelector />
|
||||
</div>
|
||||
<Scene />
|
||||
</Router>
|
||||
</LightModeContext.Provider>
|
||||
</SysInfoContext.Provider>
|
||||
<LocaleSelectorModal />
|
||||
</LocaleContext.Provider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
BIN
src/assets/icon.png
Normal file
After Width: | Height: | Size: 86 KiB |
29
src/components/Badge.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import * as React from "react";
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
cursor: "pointer",
|
||||
color: "#FFFFFF",
|
||||
};
|
||||
|
||||
type BadgeProps = {
|
||||
value: string | React.ReactNode;
|
||||
onClick?(e: React.MouseEvent<HTMLElement>): void;
|
||||
className?: string;
|
||||
background?: string | number;
|
||||
color?: string;
|
||||
};
|
||||
|
||||
const Badge: React.SFC<BadgeProps> = props => {
|
||||
let _style = { ...style };
|
||||
if (props.background) {
|
||||
_style.background = props.background;
|
||||
}
|
||||
|
||||
return (
|
||||
<span style={_style} className={`uk-badge ${props.className}`} onClick={props.onClick}>
|
||||
{props.value}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default Badge;
|
31
src/components/Card.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import * as React from "react";
|
||||
import LightModeContext from "../contexts/LightModeContext";
|
||||
|
||||
type CardProps = {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
type?: "small" | "normal";
|
||||
};
|
||||
|
||||
function Card(props: CardProps) {
|
||||
const type = props.type || "normal";
|
||||
|
||||
return (
|
||||
<LightModeContext.Consumer>
|
||||
{lm => {
|
||||
const defaultClasses =
|
||||
"uk-margin-top uk-margin-small-right uk-margin-small-left uk-card uk-card-default uk-card-body";
|
||||
const lightClasses = lm.mode === "light" ? "uk-dark uk-background-default" : "uk-light uk-background-primary";
|
||||
const typeClasses = type === "normal" ? "" : "uk-padding-small";
|
||||
return (
|
||||
<div className={`${defaultClasses} ${lightClasses} ${typeClasses}`}>
|
||||
<h3 className="uk-text-nowrap uk-card-title">{props.title}</h3>
|
||||
<div>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</LightModeContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
export default Card;
|
30
src/components/LocaleSelectorModal.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import * as React from "react";
|
||||
import LocaleContext, { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
export default function LocaleSelectorModal() {
|
||||
return (
|
||||
<LocaleContext.Consumer>
|
||||
{locale => (
|
||||
<div id="locale-selector-modal" uk-modal="">
|
||||
<div className="uk-modal-dialog uk-modal-body">
|
||||
<h2 className="uk-modal-title">{getTranslation("app.localeSelectorModalTitle", "Change language")}</h2>
|
||||
<select
|
||||
defaultValue={locale.is}
|
||||
className="uk-select"
|
||||
onChange={event => {
|
||||
locale.change(event.target.value as AvailableLanguages);
|
||||
}}
|
||||
>
|
||||
<option value="en">English</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="ch">简体中文</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="tr">Türkçe</option>
|
||||
{/* Add language here with the name of the language in his native name */}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</LocaleContext.Consumer>
|
||||
);
|
||||
}
|
127
src/components/PresetAutoApplyCards.tsx
Normal file
@ -0,0 +1,127 @@
|
||||
import * as React from "react";
|
||||
import Card from "./Card";
|
||||
import RyzenControllerAppContext from "../contexts/RyzenControllerAppContext";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
class PresetAutoApplyCards extends React.PureComponent {
|
||||
updateOnLaptopPluggedIn(ryzenControllerAppContext: RyzenControllerAppContextType) {
|
||||
return function(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
ryzenControllerAppContext.updateSettings({
|
||||
onLaptopPluggedIn: event.target.value,
|
||||
});
|
||||
};
|
||||
}
|
||||
updateOnLaptopPluggedOut(ryzenControllerAppContext: RyzenControllerAppContextType) {
|
||||
return function(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
ryzenControllerAppContext.updateSettings({
|
||||
onLaptopPluggedOut: event.target.value,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
updateOnRCStart(ryzenControllerAppContext: RyzenControllerAppContextType) {
|
||||
return function(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
ryzenControllerAppContext.updateSettings({
|
||||
onRCStart: event.target.value,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
updateOnSessionResume(ryzenControllerAppContext: RyzenControllerAppContextType) {
|
||||
return function(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
ryzenControllerAppContext.updateSettings({
|
||||
onSessionResume: event.target.value,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RyzenControllerAppContext.Consumer>
|
||||
{ryzenControllerAppContext => {
|
||||
const presetNames = Object.keys(ryzenControllerAppContext.presets);
|
||||
return (
|
||||
<div className="uk-flex uk-flex-wrap">
|
||||
{window.require("os").platform() === "win32" ? (
|
||||
<React.Fragment>
|
||||
<Card
|
||||
title={getTranslation("presetAutoApply.whenLaptopPluggedIn", "When laptop plugged in")}
|
||||
type="small"
|
||||
>
|
||||
<select
|
||||
onChange={this.updateOnLaptopPluggedIn(ryzenControllerAppContext)}
|
||||
className="uk-select"
|
||||
value={ryzenControllerAppContext.settings.onLaptopPluggedIn || ""}
|
||||
>
|
||||
<option value="">{getTranslation("presetAutoApply.nonePreset", "None")}</option>
|
||||
{presetNames.map(presetName => {
|
||||
return (
|
||||
<option key={`1_${presetName}`} value={presetName}>
|
||||
{presetName}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</Card>
|
||||
<Card
|
||||
title={getTranslation("presetAutoApply.whenLaptopPluggedOut", "When laptop plugged out")}
|
||||
type="small"
|
||||
>
|
||||
<select
|
||||
onChange={this.updateOnLaptopPluggedOut(ryzenControllerAppContext)}
|
||||
className="uk-select"
|
||||
value={ryzenControllerAppContext.settings.onLaptopPluggedOut || ""}
|
||||
>
|
||||
<option value="">{getTranslation("presetAutoApply.nonePreset", "None")}</option>
|
||||
{presetNames.map(presetName => {
|
||||
return (
|
||||
<option key={`2_${presetName}`} value={presetName}>
|
||||
{presetName}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</Card>
|
||||
</React.Fragment>
|
||||
) : null}
|
||||
<Card title={getTranslation("presetAutoApply.whenSessionResume", "When session resume")} type="small">
|
||||
<select
|
||||
onChange={this.updateOnSessionResume(ryzenControllerAppContext)}
|
||||
className="uk-select"
|
||||
value={ryzenControllerAppContext.settings.onSessionResume || ""}
|
||||
>
|
||||
<option value="">{getTranslation("presetAutoApply.nonePreset", "None")}</option>
|
||||
{presetNames.map(presetName => {
|
||||
return (
|
||||
<option key={`2_${presetName}`} value={presetName}>
|
||||
{presetName}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</Card>
|
||||
<Card title={getTranslation("presetAutoApply.whenRCStart", "When Ryzen Controller starts")} type="small">
|
||||
<select
|
||||
onChange={this.updateOnRCStart(ryzenControllerAppContext)}
|
||||
className="uk-select"
|
||||
value={ryzenControllerAppContext.settings.onRCStart || ""}
|
||||
>
|
||||
<option value="">{getTranslation("presetAutoApply.nonePreset", "None")}</option>
|
||||
{presetNames.map(presetName => {
|
||||
return (
|
||||
<option key={`2_${presetName}`} value={presetName}>
|
||||
{presetName}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</RyzenControllerAppContext.Consumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PresetAutoApplyCards;
|
158
src/components/PresetButtons.tsx
Normal file
@ -0,0 +1,158 @@
|
||||
import * as React from "react";
|
||||
import NotificationContext from "../contexts/NotificationContext";
|
||||
import RyzenControllerAppContext, { executeRyzenAdjUsingPreset } from "../contexts/RyzenControllerAppContext";
|
||||
import SysInfoContext from "../contexts/SysInfoContext";
|
||||
import PresetsOnlineContext from "../contexts/PresetsOnline";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
type PresetButtonsProps = {
|
||||
presetName: string;
|
||||
preset: RyzenAdjOptionListType;
|
||||
};
|
||||
|
||||
class PresetButtons extends React.Component<PresetButtonsProps, {}> {
|
||||
render() {
|
||||
return (
|
||||
<RyzenControllerAppContext.Consumer>
|
||||
{(ryzenControllerAppContext: RyzenControllerAppContextType) => (
|
||||
<div className="uk-flex uk-flex-right uk-flex-middle uk-height-1-1 uk-flex-wrap">
|
||||
<div className="uk-button-group uk-margin-right">
|
||||
<button
|
||||
className="uk-button uk-button-small uk-button-primary"
|
||||
uk-tooltip={`title: ${getTranslation(
|
||||
"presetButtons.applyPresetTooltip",
|
||||
"The preset will be loaded in RyzenAdj's tabs and applied."
|
||||
)}`}
|
||||
onClick={this.applyPreset(ryzenControllerAppContext)}
|
||||
>
|
||||
{getTranslation("presetButtons.apply", "Apply")}
|
||||
</button>
|
||||
<button
|
||||
className="uk-button uk-button-small uk-button-danger"
|
||||
onClick={this.removePreset(ryzenControllerAppContext)}
|
||||
>
|
||||
{getTranslation("presetButtons.delete", "Delete")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="uk-button-group uk-margin-right">
|
||||
<button
|
||||
className="uk-button uk-button-small uk-button-default"
|
||||
uk-tooltip={`title: ${getTranslation(
|
||||
"presetButtons.loadPresetTooltip",
|
||||
"The preset will be loaded in RyzenAdj's tabs but not applied."
|
||||
)}`}
|
||||
onClick={this.loadPreset(ryzenControllerAppContext)}
|
||||
>
|
||||
{getTranslation("presetButtons.load", "Load")}
|
||||
</button>
|
||||
<SysInfoContext.Consumer>
|
||||
{sysinfo => (
|
||||
<PresetsOnlineContext.Consumer>
|
||||
{(presetsOnlineContext: PresetsOnlineContextType) => (
|
||||
<button
|
||||
className="uk-button uk-button-small uk-button-default"
|
||||
onClick={this.uploadPreset(presetsOnlineContext, sysinfo.signature)}
|
||||
>
|
||||
{getTranslation("presetButtons.upload", "Upload")}
|
||||
</button>
|
||||
)}
|
||||
</PresetsOnlineContext.Consumer>
|
||||
)}
|
||||
</SysInfoContext.Consumer>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</RyzenControllerAppContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
applyPreset(
|
||||
ryzenControllerAppContext: RyzenControllerAppContextType
|
||||
): ((event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void) | undefined {
|
||||
return () => {
|
||||
ryzenControllerAppContext.updateCurrentSettings(this.props.preset);
|
||||
executeRyzenAdjUsingPreset(this.props.presetName);
|
||||
};
|
||||
}
|
||||
|
||||
uploadPreset(
|
||||
presetsOnlineContext: PresetsOnlineContextType,
|
||||
signature: string | false
|
||||
): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void {
|
||||
return () => {
|
||||
if (!signature) {
|
||||
NotificationContext.warning(
|
||||
getTranslation("presetButtons.mustWaitForSignatureGen", "You must wait for laptop signature to be generated")
|
||||
);
|
||||
return;
|
||||
}
|
||||
let presetsWithSameName = presetsOnlineContext.list.filter(preset => {
|
||||
return preset.name === this.props.presetName && preset.systemHash === signature;
|
||||
});
|
||||
if (presetsWithSameName.length > 0) {
|
||||
NotificationContext.warning(
|
||||
getTranslation(
|
||||
"presetButtons.presetWithSameNameAlreadyExistOnline",
|
||||
"A preset with the same name already exist online"
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
window
|
||||
.require("uikit")
|
||||
.modal.confirm(
|
||||
getTranslation("presetButtons.uploadPresetConfirmation", "Are you sure to upload the preset {preset}?", {
|
||||
preset: this.props.presetName,
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
presetsOnlineContext
|
||||
.uploadPreset({
|
||||
name: this.props.presetName,
|
||||
systemHash: signature,
|
||||
ryzenAdjArguments: this.props.preset,
|
||||
})
|
||||
.then(value => {
|
||||
NotificationContext.success(
|
||||
getTranslation("presetButtons.uploadSucceed", "Preset {preset} has been uploaded", {
|
||||
preset: value.name,
|
||||
})
|
||||
);
|
||||
presetsOnlineContext.update();
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
loadPreset(
|
||||
ryzenControllerAppContext: RyzenControllerAppContextType
|
||||
): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void {
|
||||
return () => {
|
||||
ryzenControllerAppContext.updateCurrentSettings(this.props.preset);
|
||||
NotificationContext.talk(
|
||||
getTranslation("presetButtons.loadedPreset", "Preset {preset} has been loaded.", {
|
||||
preset: this.props.presetName,
|
||||
})
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
removePreset(
|
||||
ryzenControllerAppContext: RyzenControllerAppContextType
|
||||
): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void {
|
||||
return () => {
|
||||
require("uikit")
|
||||
.modal.confirm(
|
||||
getTranslation("presetButtons.confirmDeletion", 'Are you sure to delete "{preset}"?', {
|
||||
preset: this.props.presetName,
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
ryzenControllerAppContext.removePreset(this.props.presetName);
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default PresetButtons;
|
28
src/components/PresetLine.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import * as React from "react";
|
||||
import PresetSummary from "../components/PresetSummary";
|
||||
import PresetButtons from "../components/PresetButtons";
|
||||
|
||||
type PresetLineProps = {
|
||||
presetName: string;
|
||||
preset: RyzenAdjOptionListType;
|
||||
};
|
||||
|
||||
class PresetLine extends React.PureComponent<PresetLineProps, {}> {
|
||||
render() {
|
||||
return (
|
||||
<li className="uk-position-relative">
|
||||
<div className="uk-grid">
|
||||
<div className="uk-width-1-1 uk-width-1-2@s uk-width-2-3@l uk-width-3-4@xl">
|
||||
{this.props.presetName}
|
||||
<PresetSummary preset={this.props.preset} />
|
||||
</div>
|
||||
<div className="uk-width-1-1 uk-width-1-2@s uk-width-1-3@l uk-width-1-4@xl">
|
||||
<PresetButtons preset={this.props.preset} presetName={this.props.presetName} />
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PresetLine;
|
34
src/components/PresetListEmpty.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import * as React from "react";
|
||||
import LightModeContext from "../contexts/LightModeContext";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
function PresetListEmpty() {
|
||||
return (
|
||||
<LightModeContext.Consumer>
|
||||
{lm => {
|
||||
const classes = lm.mode === "light" ? "uk-dark uk-background-default" : "uk-light uk-background-primary";
|
||||
return (
|
||||
<div className={`uk-flex uk-flex-center uk-margin-left uk-margin-right`}>
|
||||
<div className={`uk-card uk-card-default uk-card-body uk-width-1-2@m ${classes}`}>
|
||||
<h3 className="uk-card-title">
|
||||
{getTranslation("presetListEmpty.youDontHaveAny", "You don't have any preset yet")}
|
||||
</h3>
|
||||
<p>
|
||||
{getTranslation("presetListEmpty.sentencePart1", "You can create one by using the")}
|
||||
<button className="uk-button uk-button-default uk-margin-small-left uk-margin-small-right">
|
||||
{getTranslation("presetListEmpty.createPresetBtn", "Create preset")}
|
||||
</button>
|
||||
{getTranslation(
|
||||
"presetListEmpty.sentencePart2",
|
||||
"button available on Ryzen Adj settings tabs (CPU, GPU, ...)."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</LightModeContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
export default PresetListEmpty;
|
62
src/components/PresetOnline.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import * as React from "react";
|
||||
import PresetsOnlineContext from "../contexts/PresetsOnline";
|
||||
import Card from "./Card";
|
||||
import SysInfoContext, { SysInfoState } from "../contexts/SysInfoContext";
|
||||
import PresetOnlineLine from "../components/PresetOnlineLine";
|
||||
import { isPresetValid } from "../contexts/RyzenControllerAppContext";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
function PresetOnline() {
|
||||
return (
|
||||
<SysInfoContext.Consumer>
|
||||
{(sysInfoContext: SysInfoState) => (
|
||||
<PresetsOnlineContext.Consumer>
|
||||
{(presetsOnlineContext: PresetsOnlineContextType) => {
|
||||
return presetsOnlineContext.list
|
||||
.filter(preset => isPresetValid(preset.ryzenAdjArguments))
|
||||
.filter(preset => preset.systemHash === sysInfoContext.signature).length && sysInfoContext?.signature ? (
|
||||
<ul className="uk-margin uk-list uk-list-large uk-list-striped">
|
||||
{presetsOnlineContext.list
|
||||
.filter(preset => isPresetValid(preset.ryzenAdjArguments))
|
||||
.filter(preset => preset.systemHash === sysInfoContext.signature)
|
||||
.map((preset: ApiPreset, index) => {
|
||||
const presetName = preset.name;
|
||||
return <PresetOnlineLine preset={preset} key={`online_${index}_${presetName}_btn`} />;
|
||||
})}
|
||||
</ul>
|
||||
) : presetsOnlineContext.loading || !sysInfoContext?.signature ? (
|
||||
<div className="uk-flex uk-flex-center">
|
||||
<div uk-spinner="ratio: 2"></div>
|
||||
</div>
|
||||
) : (
|
||||
<Card
|
||||
title={getTranslation(
|
||||
"PresetOnline.listNotLoadedYet",
|
||||
"List hasn't been loaded or there is no online preset yet."
|
||||
)}
|
||||
>
|
||||
<button
|
||||
className="uk-margin-small-bottom uk-button uk-button-small uk-button-default"
|
||||
onClick={() => presetsOnlineContext.update()}
|
||||
>
|
||||
{getTranslation("PresetOnline.loadPresetListBtn", "Load preset list")}
|
||||
</button>
|
||||
<br />
|
||||
{getTranslation("PresetOnline.sentencePart1", "You can share your own preset by clicking on the")}
|
||||
<button
|
||||
className="uk-margin-small-right uk-margin-small-left uk-button uk-button-small uk-button-default"
|
||||
onClick={() => false}
|
||||
>
|
||||
{getTranslation("PresetOnline.uploadBtn", "Upload")}
|
||||
</button>
|
||||
{getTranslation("PresetOnline.sentencePart2", "button available on your presets.")}
|
||||
</Card>
|
||||
);
|
||||
}}
|
||||
</PresetsOnlineContext.Consumer>
|
||||
)}
|
||||
</SysInfoContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
export default PresetOnline;
|
111
src/components/PresetOnlineButtons.tsx
Normal file
@ -0,0 +1,111 @@
|
||||
import * as React from "react";
|
||||
import RyzenControllerAppContext, { isPresetValid } from "../contexts/RyzenControllerAppContext";
|
||||
import NotificationContext from "../contexts/NotificationContext";
|
||||
import PresetsOnlineContext from "../contexts/PresetsOnline";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
type PresetOnlineButtonsProps = {
|
||||
presetName: string;
|
||||
presetId: number;
|
||||
preset: RyzenAdjOptionListType;
|
||||
upvote: number;
|
||||
downvote: number;
|
||||
};
|
||||
|
||||
function PresetOnlineButtons(props: PresetOnlineButtonsProps) {
|
||||
return (
|
||||
<div className="uk-flex uk-flex-right uk-flex-middle uk-height-1-1 uk-flex-wrap">
|
||||
<RyzenControllerAppContext.Consumer>
|
||||
{(ryzenControllerAppContext: RyzenControllerAppContextType) => (
|
||||
<div className="uk-button-group uk-margin-right">
|
||||
<button
|
||||
className="uk-button uk-button-small uk-button-primary"
|
||||
uk-tooltip={`title: ${getTranslation(
|
||||
"presetOnlineBtn.downloadTooltip",
|
||||
"Will save the preset to your local preset."
|
||||
)}`}
|
||||
onClick={() => {
|
||||
if (ryzenControllerAppContext.presets.hasOwnProperty(props.presetName)) {
|
||||
NotificationContext.warning(
|
||||
getTranslation(
|
||||
"presetOnlineBtn.presetSameNameExist",
|
||||
"You already have a preset with the same name"
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!isPresetValid(props.preset)) {
|
||||
const presetInvalidOrObsoleteMessage = getTranslation(
|
||||
"presetOnlineBtn.presetInvalidOrObsolete",
|
||||
'Preset "{presetName}" is invalid or obsolete',
|
||||
{ presetName: props.presetName }
|
||||
);
|
||||
NotificationContext.error(presetInvalidOrObsoleteMessage);
|
||||
return;
|
||||
}
|
||||
ryzenControllerAppContext.addPreset(props.presetName, props.preset);
|
||||
const presetDownloadedMessage = getTranslation(
|
||||
"presetOnlineBtn.presetDownloaded",
|
||||
'Preset "{presetName}" has been downloaded',
|
||||
{ presetName: props.presetName }
|
||||
);
|
||||
NotificationContext.talk(presetDownloadedMessage);
|
||||
return;
|
||||
}}
|
||||
>
|
||||
{getTranslation("presetOnlineBtn.download", "Download")}
|
||||
</button>
|
||||
<button
|
||||
className="uk-button uk-button-small uk-button-default"
|
||||
uk-tooltip={`title: ${getTranslation(
|
||||
"presetOnlineBtn.loadTooltip",
|
||||
"Without saving the preset, it will be loaded in RyzenAdj's tabs but not applied."
|
||||
)}`}
|
||||
onClick={() => {
|
||||
ryzenControllerAppContext.updateCurrentSettings(props.preset);
|
||||
const presetloadedMessage = getTranslation(
|
||||
"presetOnlineBtn.presetDownloaded",
|
||||
'Preset "{presetName}" has been loaded',
|
||||
{ presetName: props.presetName }
|
||||
);
|
||||
NotificationContext.talk(presetloadedMessage);
|
||||
}}
|
||||
>
|
||||
{getTranslation("presetOnlineBtn.load", "Load")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</RyzenControllerAppContext.Consumer>
|
||||
<PresetsOnlineContext.Consumer>
|
||||
{(presetsOnlineContext: PresetsOnlineContextType) => (
|
||||
<div className="uk-button-group uk-margin-right">
|
||||
<button
|
||||
className="uk-button uk-button-small uk-button-default"
|
||||
onClick={() => {
|
||||
presetsOnlineContext.upvote(props.presetId);
|
||||
}}
|
||||
>
|
||||
<span className="uk-margin-small-right" role="img" aria-label="upvote">
|
||||
👍
|
||||
</span>
|
||||
(+{props.upvote})
|
||||
</button>
|
||||
<button
|
||||
className="uk-button uk-button-small uk-button-default"
|
||||
onClick={() => {
|
||||
presetsOnlineContext.downvote(props.presetId);
|
||||
}}
|
||||
>
|
||||
<span className="uk-margin-small-right" role="img" aria-label="downvote">
|
||||
👎
|
||||
</span>
|
||||
({props.downvote})
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</PresetsOnlineContext.Consumer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PresetOnlineButtons;
|
33
src/components/PresetOnlineLine.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import * as React from "react";
|
||||
import PresetSummary from "../components/PresetSummary";
|
||||
import PresetOnlineButtons from "../components/PresetOnlineButtons";
|
||||
|
||||
type PresetOnlineLineProps = {
|
||||
preset: ApiPreset;
|
||||
};
|
||||
|
||||
class PresetOnlineLine extends React.PureComponent<PresetOnlineLineProps, {}> {
|
||||
render() {
|
||||
return (
|
||||
<li className="uk-position-relative">
|
||||
<div className="uk-grid">
|
||||
<div className="uk-width-1-1 uk-width-1-2@s uk-width-2-3@l uk-width-3-4@xl">
|
||||
{this.props.preset.name}
|
||||
<PresetSummary preset={this.props.preset.ryzenAdjArguments} />
|
||||
</div>
|
||||
<div className="uk-width-1-1 uk-width-1-2@s uk-width-1-3@l uk-width-1-4@xl">
|
||||
<PresetOnlineButtons
|
||||
presetId={this.props.preset.id}
|
||||
presetName={this.props.preset.name}
|
||||
preset={this.props.preset.ryzenAdjArguments}
|
||||
upvote={this.props.preset.upvote}
|
||||
downvote={this.props.preset.downvote}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PresetOnlineLine;
|
15
src/components/PresetSummary.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import * as React from "react";
|
||||
import { createRyzenAdjCommandLine } from "../contexts/RyzenAdjContext";
|
||||
|
||||
function PresetSummary(props: { preset: RyzenAdjOptionListType }) {
|
||||
return (
|
||||
<p
|
||||
className="uk-text-small uk-text-truncate uk-text-italic"
|
||||
uk-tooltip={`title: ${createRyzenAdjCommandLine(props.preset).join("<br/>")}`}
|
||||
>
|
||||
{createRyzenAdjCommandLine(props.preset).join(" ")}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
export default PresetSummary;
|
102
src/components/RyzenAdjBottomBar.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import * as React from "react";
|
||||
import RyzenControllerAppContext, { defaultPreset, isPresetValid } from "../contexts/RyzenControllerAppContext";
|
||||
import Notification from "../contexts/NotificationContext";
|
||||
import { createRyzenAdjCommandLine, executeRyzenAdj } from "../contexts/RyzenAdjContext";
|
||||
import LightModeContext from "../contexts/LightModeContext";
|
||||
import NotificationContext from "../contexts/NotificationContext";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
const UIkit = require("uikit");
|
||||
|
||||
class RyzenAdjBottomBar extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<LightModeContext.Consumer>
|
||||
{lightModeContext => {
|
||||
return (
|
||||
<RyzenControllerAppContext.Consumer>
|
||||
{ryzenControllerAppContext => {
|
||||
const classes =
|
||||
lightModeContext.mode === "light"
|
||||
? "uk-dark uk-background-default uk-card uk-card-default"
|
||||
: "uk-light uk-background-primary";
|
||||
return (
|
||||
<div className={`uk-padding-small uk-position-fixed uk-position-bottom-right ${classes}`}>
|
||||
<button className="uk-button uk-button-primary" onClick={this.apply(ryzenControllerAppContext)}>
|
||||
{getTranslation("ryzenAdjBottomBar.apply", "Apply")}
|
||||
</button>
|
||||
<button
|
||||
className="uk-button uk-button-default uk-margin-left"
|
||||
onClick={this.createNewPreset(ryzenControllerAppContext)}
|
||||
>
|
||||
{getTranslation("ryzenAdjBottomBar.createPreset", "Create preset")}
|
||||
</button>
|
||||
<button
|
||||
className="uk-button uk-button-default uk-margin-left"
|
||||
onClick={this.reset(ryzenControllerAppContext)}
|
||||
>
|
||||
{getTranslation("ryzenAdjBottomBar.reset", "Reset")}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</RyzenControllerAppContext.Consumer>
|
||||
);
|
||||
}}
|
||||
</LightModeContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
createNewPreset(
|
||||
ryzenControllerAppContext: RyzenControllerAppContextType
|
||||
): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void {
|
||||
return function() {
|
||||
const promptMessage = getTranslation("ryzenAdjBottomBar.prompt", "New preset name");
|
||||
const mustProvideNameMessage = getTranslation("ryzenAdjBottomBar.mustProvideName", "You must provide a name");
|
||||
|
||||
UIkit.modal.prompt(promptMessage, "").then((newPresetName: string | null) => {
|
||||
if (newPresetName === null) {
|
||||
return;
|
||||
} else if (newPresetName.length <= 0) {
|
||||
Notification.warning(mustProvideNameMessage);
|
||||
} else if (ryzenControllerAppContext.presets.hasOwnProperty(newPresetName)) {
|
||||
const presetWithSameNameExistMessage = getTranslation(
|
||||
"ryzenAdjBottomBar.presetWithSameNameExist",
|
||||
'A preset with the name "{newPresetName}" already exist',
|
||||
{ newPresetName: newPresetName }
|
||||
);
|
||||
Notification.warning(presetWithSameNameExistMessage);
|
||||
} else {
|
||||
ryzenControllerAppContext.addPreset(newPresetName, ryzenControllerAppContext.currentSettings);
|
||||
const presetCreatedMessage = getTranslation(
|
||||
"ryzenAdjBottomBar.presetCreated",
|
||||
'Preset "{newPresetName}" created',
|
||||
{ newPresetName: newPresetName }
|
||||
);
|
||||
Notification.success(presetCreatedMessage);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
apply(ryzenControllerAppContext: RyzenControllerAppContextType) {
|
||||
return function() {
|
||||
if (!isPresetValid(ryzenControllerAppContext.currentSettings)) {
|
||||
NotificationContext.warning(
|
||||
getTranslation("ryzenAdjBottomBar.invalidPreset", "Unable to apply invalid preset")
|
||||
);
|
||||
return;
|
||||
}
|
||||
ryzenControllerAppContext.updateLatestSettings();
|
||||
executeRyzenAdj(createRyzenAdjCommandLine(ryzenControllerAppContext.currentSettings));
|
||||
};
|
||||
}
|
||||
|
||||
reset(ryzenControllerAppContext: RyzenControllerAppContextType) {
|
||||
return function() {
|
||||
ryzenControllerAppContext.updateCurrentSettings(defaultPreset);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default RyzenAdjBottomBar;
|
84
src/components/RyzenAdjOptionForm.tsx
Normal file
@ -0,0 +1,84 @@
|
||||
import * as React from "react";
|
||||
import RyzenControllerAppContext from "../contexts/RyzenControllerAppContext";
|
||||
|
||||
type RyzenAdjOptionFormState = {
|
||||
value: number;
|
||||
};
|
||||
|
||||
type RyzenAdjOptionFormProps = {
|
||||
option: RyzenAdjOptionDefinition;
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
class RyzenAdjOptionForm extends React.PureComponent<RyzenAdjOptionFormProps, RyzenAdjOptionFormState> {
|
||||
state = {
|
||||
value: this.props.option.default,
|
||||
};
|
||||
|
||||
render() {
|
||||
const onChange = this.onChange.bind(this);
|
||||
if (!this.props.enabled) return null;
|
||||
|
||||
return (
|
||||
<RyzenControllerAppContext.Consumer>
|
||||
{ryzenControllerAppContext => {
|
||||
const isEnabled = ryzenControllerAppContext.currentSettings[this.props.option.ryzenadj_arg]?.enabled;
|
||||
const value =
|
||||
ryzenControllerAppContext.currentSettings[this.props.option.ryzenadj_arg].value || this.state.value;
|
||||
if (!isEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="uk-grid-small" uk-grid="">
|
||||
<div className="uk-width-1-6">
|
||||
<input
|
||||
onChange={onChange(ryzenControllerAppContext).bind(this)}
|
||||
className="uk-input"
|
||||
type="number"
|
||||
value={value}
|
||||
min={this.props.option.min}
|
||||
max={this.props.option.max}
|
||||
step={this.props.option.step}
|
||||
/>
|
||||
</div>
|
||||
<div className="uk-width-5-6">
|
||||
<input
|
||||
onChange={onChange(ryzenControllerAppContext).bind(this)}
|
||||
className="uk-range"
|
||||
type="range"
|
||||
value={value}
|
||||
min={this.props.option.min}
|
||||
max={this.props.option.max}
|
||||
step={this.props.option.step}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</RyzenControllerAppContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
onChange(ryzenControllerAppContext: RyzenControllerAppContextType) {
|
||||
const name: RyzenAdjArguments = this.props.option.ryzenadj_arg;
|
||||
const _this = this;
|
||||
return function(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
let value = parseInt(event.target.value);
|
||||
if (value < _this.props.option.min) {
|
||||
value = _this.props.option.min;
|
||||
}
|
||||
if (value > _this.props.option.max) {
|
||||
value = _this.props.option.max;
|
||||
}
|
||||
ryzenControllerAppContext.updateCurrentSettings({
|
||||
[name]: {
|
||||
enabled: true,
|
||||
value: value,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default RyzenAdjOptionForm;
|
73
src/components/RyzenAdjOptionLabel.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import * as React from "react";
|
||||
import RyzenControllerAppContext from "../contexts/RyzenControllerAppContext";
|
||||
|
||||
type RyzenAdjOptionLabelProps = {
|
||||
option: RyzenAdjOptionDefinition;
|
||||
};
|
||||
|
||||
type RyzenAdjOptionLabelState = {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
class RyzenAdjOptionLabel extends React.PureComponent<RyzenAdjOptionLabelProps, RyzenAdjOptionLabelState> {
|
||||
state = {
|
||||
enabled: false,
|
||||
};
|
||||
|
||||
isEnabled(ryzenControllerAppContext: RyzenControllerAppContextType): boolean {
|
||||
const isEnabled: boolean =
|
||||
ryzenControllerAppContext.currentSettings[this.props.option.ryzenadj_arg]?.enabled || false;
|
||||
if (this.state.enabled !== isEnabled) {
|
||||
this.setState({
|
||||
enabled: isEnabled,
|
||||
});
|
||||
}
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RyzenControllerAppContext.Consumer>
|
||||
{ryzenControllerAppContext => (
|
||||
<div className="uk-grid-small" uk-grid="">
|
||||
<div className="uk-width-expend">
|
||||
<h2>
|
||||
<label className="uk-pointer">
|
||||
<input
|
||||
className="uk-margin-right uk-checkbox"
|
||||
type="checkbox"
|
||||
checked={this.isEnabled(ryzenControllerAppContext)}
|
||||
onChange={this.handleChange(ryzenControllerAppContext).bind(this)}
|
||||
/>
|
||||
{this.props.option.label}
|
||||
</label>
|
||||
<span
|
||||
uk-icon="info"
|
||||
className="uk-margin-left"
|
||||
uk-tooltip={`title: ${this.props.option.description}`}
|
||||
/>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</RyzenControllerAppContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
handleChange(ryzenControllerAppContext: RyzenControllerAppContextType): Function {
|
||||
return (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
this.setState({
|
||||
enabled: event.target.checked,
|
||||
});
|
||||
let newCurrentSettings: PartialRyzenAdjOptionListType = {
|
||||
[this.props.option.ryzenadj_arg]: {
|
||||
enabled: event.target.checked,
|
||||
value: ryzenControllerAppContext.currentSettings[this.props.option.ryzenadj_arg].value,
|
||||
},
|
||||
};
|
||||
ryzenControllerAppContext.updateCurrentSettings(newCurrentSettings);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default RyzenAdjOptionLabel;
|
31
src/components/RyzenAdjOptionList.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import * as React from "react";
|
||||
import RyzenAdjOptions from "../components/RyzenAdjOptions";
|
||||
import RyzenAdjOptionForm from "../components/RyzenAdjOptionForm";
|
||||
import RyzenAdjOptionLabel from "../components/RyzenAdjOptionLabel";
|
||||
|
||||
type RyzenAdjOptionListProps = {
|
||||
filter: RyzenControllerTabForRyzenAdj;
|
||||
};
|
||||
|
||||
class RyzenAdjOptionList extends React.PureComponent<RyzenAdjOptionListProps, {}> {
|
||||
render() {
|
||||
return <RyzenAdjOptions tab={this.props.filter} render={this.renderRyzenAdjOptionFormList.bind(this)} />;
|
||||
}
|
||||
|
||||
renderRyzenAdjOptionFormList(options: Array<RyzenAdjOptionDefinition>): React.ReactNode {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{options.map((option: RyzenAdjOptionDefinition) => {
|
||||
return (
|
||||
<div key={option.ryzenadj_arg} className="uk-margin">
|
||||
<RyzenAdjOptionLabel option={option} />
|
||||
<RyzenAdjOptionForm enabled={true} option={option} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RyzenAdjOptionList;
|
19
src/components/RyzenAdjOptions.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import * as React from "react";
|
||||
import { RyzenAdjOptionDefinitions } from "../contexts/RyzenAdjContext";
|
||||
|
||||
type RyzenAdjOptionsProps = {
|
||||
tab?: RyzenControllerTabForRyzenAdj;
|
||||
render(options: Array<RyzenAdjOptionDefinition>): React.ReactNode;
|
||||
};
|
||||
|
||||
function RyzenAdjOptions(props: RyzenAdjOptionsProps) {
|
||||
let currentTabs: Array<RyzenAdjOptionDefinition> = RyzenAdjOptionDefinitions;
|
||||
if (props.tab) {
|
||||
currentTabs = RyzenAdjOptionDefinitions.filter(option => option.tab === props.tab);
|
||||
}
|
||||
|
||||
return <React.Fragment>{props.render(currentTabs)}</React.Fragment>;
|
||||
}
|
||||
|
||||
export default RyzenAdjOptions;
|
||||
export { RyzenAdjOptionDefinitions };
|
92
src/components/SceneSelector.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
import * as React from "react";
|
||||
import { Link, Switch, Route } from "react-router-dom";
|
||||
import LightModeContext from "../contexts/LightModeContext";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
function Tabs(props: { tabName: string; tabLocation: string; currentLocation: string }) {
|
||||
let isActive = "";
|
||||
if (props.currentLocation === props.tabLocation) {
|
||||
isActive = "uk-active";
|
||||
}
|
||||
|
||||
return (
|
||||
<li className={isActive}>
|
||||
<Link to={props.tabLocation}>{props.tabName}</Link>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
function SceneSelector() {
|
||||
return (
|
||||
<LightModeContext.Consumer>
|
||||
{lm => {
|
||||
const classes = lm.mode === "light" ? "uk-dark uk-background-default" : "uk-light uk-background-secondary";
|
||||
return (
|
||||
<nav className={classes} uk-sticky="sel-target: .uk-navbar-container; cls-active: uk-navbar-sticky">
|
||||
<Switch>
|
||||
<Route
|
||||
render={props => {
|
||||
const currentLocation = props.location.pathname;
|
||||
|
||||
return (
|
||||
<ul className="uk-tab uk-margin-remove-bottom">
|
||||
<Tabs
|
||||
tabName={getTranslation("sceneSelector.cpuTitle", "CPU")}
|
||||
tabLocation="/cpu"
|
||||
currentLocation={currentLocation}
|
||||
/>
|
||||
<Tabs
|
||||
tabName={getTranslation("sceneSelector.gpuTitle", "GPU")}
|
||||
tabLocation="/gpu"
|
||||
currentLocation={currentLocation}
|
||||
/>
|
||||
<Tabs
|
||||
tabName={getTranslation("sceneSelector.powerTitle", "Power")}
|
||||
tabLocation="/power"
|
||||
currentLocation={currentLocation}
|
||||
/>
|
||||
<Tabs
|
||||
tabName={getTranslation("sceneSelector.presetsTitle", "Presets")}
|
||||
tabLocation="/presets"
|
||||
currentLocation={currentLocation}
|
||||
/>
|
||||
<Tabs
|
||||
tabName={getTranslation("sceneSelector.settingsTitle", "Settings")}
|
||||
tabLocation="/settings"
|
||||
currentLocation={currentLocation}
|
||||
/>
|
||||
<li>
|
||||
<a
|
||||
href="https://gitlab.com/ryzen-controller-team/ryzen-controller/-/releases"
|
||||
onClick={openExternal("https://gitlab.com/ryzen-controller-team/ryzen-controller/-/releases")}
|
||||
>
|
||||
<span uk-icon="link"></span>
|
||||
{getTranslation("sceneSelector.releasesTitle", "Releases")}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
}}
|
||||
></Route>
|
||||
</Switch>
|
||||
</nav>
|
||||
);
|
||||
}}
|
||||
</LightModeContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method open the given URL using external browser.
|
||||
* @param url The URL to be opened.
|
||||
* @return function
|
||||
*/
|
||||
function openExternal(url: string) {
|
||||
return function openExternalNow(e: React.MouseEvent<HTMLAnchorElement>) {
|
||||
e.preventDefault();
|
||||
window.require("electron").remote.shell.openExternal(url);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
export default SceneSelector;
|
7
src/components/SceneTitle.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import * as React from "react";
|
||||
|
||||
function SceneTitle(props: { title: string; className?: string }) {
|
||||
return <h2 className={`uk-margin uk-margin-left uk-margin-right ${props.className}`}>{props.title}</h2>;
|
||||
}
|
||||
|
||||
export default SceneTitle;
|
90
src/components/SettingForm.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
import * as React from "react";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
type ElectronFileDialogType = {
|
||||
filePaths?: Array<string>;
|
||||
};
|
||||
|
||||
type SettingFormProps = {
|
||||
setting: RyzenControllerSettingDefinition;
|
||||
value: boolean | string | number;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
};
|
||||
|
||||
class SettingForm extends React.PureComponent<SettingFormProps> {
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<div uk-tooltip={this.props.setting.description || ""} className="uk-form-controls uk-form-controls-text">
|
||||
{this.props.setting.displayTitle ? <h4 className="uk-margin-top">{this.props.setting.name}</h4> : null}
|
||||
<label className="uk-form-label uk-pointer">
|
||||
{this.renderType(this.props.setting.type)} {this.props.setting.short_description}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderType(type: "boolean" | "range" | "path"): React.ReactNode {
|
||||
switch (type) {
|
||||
case "boolean":
|
||||
const checked = !!this.props.value;
|
||||
return <input onChange={this.props.onChange} checked={checked} className="uk-checkbox" type="checkbox" />;
|
||||
|
||||
case "range":
|
||||
const number = parseInt(`${this.props.value}`) || 0;
|
||||
return (
|
||||
<div className="uk-inline uk-margin-small-right">
|
||||
<input
|
||||
onChange={this.props.onChange}
|
||||
defaultValue={number}
|
||||
className="uk-input uk-form-width-small"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
case "path":
|
||||
const path = `${this.props.value}`;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<p className="uk-margin-small-bottom">
|
||||
{getTranslation("settingForm.currentPath", "Current path:")} <code>{path || "default"}</code>
|
||||
</p>
|
||||
<button onClick={this.findFile.bind(this)} className="uk-button uk-button-default">
|
||||
{getTranslation("settingForm.browseBtn", "Browse")}
|
||||
</button>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
default:
|
||||
throw new Error("Unknown setting type.");
|
||||
}
|
||||
}
|
||||
|
||||
findFile(event: React.MouseEvent<HTMLButtonElement>): void {
|
||||
event.preventDefault();
|
||||
const allowedFiles =
|
||||
window.require("electron").remote.process.platform === "linux"
|
||||
? []
|
||||
: [
|
||||
{
|
||||
name: getTranslation("settingForm.windowsBinFileType", "Windows Binary"),
|
||||
extensions: ["exe"],
|
||||
},
|
||||
];
|
||||
window
|
||||
.require("electron")
|
||||
.remote.dialog.showOpenDialog({
|
||||
properties: ["onpenFile"],
|
||||
filters: allowedFiles,
|
||||
})
|
||||
.then((data: ElectronFileDialogType) => {
|
||||
if (!data.filePaths) {
|
||||
return;
|
||||
}
|
||||
// @ts-ignore
|
||||
this.props.onChange({ target: { value: data.filePaths[0] } });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingForm;
|
62
src/components/SettingsList.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import * as React from "react";
|
||||
import RyzenControllerAppContext, {
|
||||
RyzenControllerSettingsDefinitions,
|
||||
getSettingDefinition,
|
||||
} from "../contexts/RyzenControllerAppContext";
|
||||
import SettingForm from "./SettingForm";
|
||||
|
||||
class SettingsList extends React.PureComponent {
|
||||
state = {
|
||||
settings: {},
|
||||
};
|
||||
|
||||
render() {
|
||||
const platform: "win32" | "linux" = window.require("os").platform();
|
||||
return (
|
||||
<form className="uk-margin-left uk-margin-right">
|
||||
<RyzenControllerAppContext.Consumer>
|
||||
{(ryzenControllerAppContext: RyzenControllerAppContextType) => (
|
||||
<fieldset className="uk-fieldset">
|
||||
{Object.keys(RyzenControllerSettingsDefinitions).map(
|
||||
(key: string): React.ReactNode => {
|
||||
// @ts-ignore
|
||||
const settingKey: RyzenControllerSettingsNames = key;
|
||||
if (!RyzenControllerSettingsDefinitions[settingKey].compatibility[platform]) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div key={settingKey}>
|
||||
<SettingForm
|
||||
setting={RyzenControllerSettingsDefinitions[settingKey]}
|
||||
value={ryzenControllerAppContext.settings[settingKey]}
|
||||
onChange={this.updateSetting(ryzenControllerAppContext, settingKey)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</fieldset>
|
||||
)}
|
||||
</RyzenControllerAppContext.Consumer>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
updateSetting(
|
||||
ryzenControllerAppContext: RyzenControllerAppContextType,
|
||||
settingKey: RyzenControllerSettingsNames
|
||||
): (e: React.ChangeEvent<HTMLInputElement>) => void {
|
||||
return function(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||
const def = getSettingDefinition(settingKey);
|
||||
if (!def) {
|
||||
return;
|
||||
}
|
||||
const value = def.type === "boolean" ? e.target.checked : e.target.value;
|
||||
ryzenControllerAppContext.updateSettings({
|
||||
[settingKey]: value,
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingsList;
|
90
src/components/SysInfoCards.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
import * as React from "react";
|
||||
import Card from "./Card";
|
||||
import SysInfoContext from "../contexts/SysInfoContext";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
function SysInfoCards() {
|
||||
return (
|
||||
<SysInfoContext.Consumer>
|
||||
{sysInfoContext => {
|
||||
if (sysInfoContext.error) {
|
||||
return (
|
||||
<Card title="Error">
|
||||
{getTranslation("sysInfoCards.unableToGetSysInfo", "Unable to get system info:")}
|
||||
<br />
|
||||
{sysInfoContext.error}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
const system = sysInfoContext.system;
|
||||
const cpu = sysInfoContext.cpu;
|
||||
const mem = sysInfoContext.mem;
|
||||
const gpu = sysInfoContext.graphics;
|
||||
const bios = sysInfoContext.bios;
|
||||
return (
|
||||
<div className="uk-margin-small-right uk-margin-small-left uk-flex uk-flex-center uk-flex-around uk-flex-wrap">
|
||||
<Card title={getTranslation("sysInfoCards.basicInfoTitle", "Basic Information")}>
|
||||
{system === false || mem === false || bios === false ? (
|
||||
<div uk-spinner=""></div>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
{system.manufacturer} {system.model} {system.version}
|
||||
<br />
|
||||
{getTranslation("sysInfoCards.biosVersion", "Bios version:")} {bios.version}{" "}
|
||||
{bios.revision ? `rev${bios.revision}` : ""}
|
||||
<br />
|
||||
{(mem.total / (1024 * 1024 * 1024)).toFixed(2)}Gb Ram
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Card>
|
||||
<Card title={getTranslation("sysInfoCards.CPUInfoTitle", "CPU Information")}>
|
||||
{cpu === false ? (
|
||||
<div uk-spinner=""></div>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
{cpu.manufacturer} {cpu.brand}
|
||||
<br />
|
||||
{getTranslation(
|
||||
"sysInfoCards.cpuPerfDesc",
|
||||
"{speedmax}Ghz on {physicalCores} cores, {cores} threads.",
|
||||
{
|
||||
speedmax: cpu.speedmax,
|
||||
physicalCores: cpu.physicalCores.toString(),
|
||||
cores: cpu.cores.toString(),
|
||||
}
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Card>
|
||||
{gpu === false ? (
|
||||
<Card title={getTranslation("sysInfoCards.GPUInfoTitle", "GPU #{index} Information", { index: "0" })}>
|
||||
<div uk-spinner=""></div>
|
||||
</Card>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
{gpu.controllers.map((controller, index) => (
|
||||
<Card
|
||||
key={`gpu-${index}`}
|
||||
title={getTranslation("sysInfoCards.GPUInfoTitle", "GPU #{index} Information", {
|
||||
index: `${index}`,
|
||||
})}
|
||||
>
|
||||
{controller.vendor} {controller.model}
|
||||
<br />
|
||||
{getTranslation("sysInfoCards.gpuPerfDesc", "{ram}Mb (Dynamic vram: {dyn}).", {
|
||||
ram: controller.vram.toString(),
|
||||
dyn: controller.vramDynamic.toString(),
|
||||
})}
|
||||
</Card>
|
||||
))}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</SysInfoContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
export default SysInfoCards;
|
72
src/components/TopBar.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import * as React from "react";
|
||||
import logo from "../assets/icon.png";
|
||||
import Badge from "./Badge";
|
||||
import LightModeContext from "../contexts/LightModeContext";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
function TopBar() {
|
||||
return (
|
||||
<header>
|
||||
<div
|
||||
style={{
|
||||
width: "128px",
|
||||
height: "128px",
|
||||
display: "inline-block",
|
||||
}}
|
||||
>
|
||||
<img src={logo} alt="Ryzen Controller" width="128px" height="128px" />
|
||||
</div>
|
||||
<Badge
|
||||
className="uk-margin-left"
|
||||
value={process.env.REACT_APP_VERSION || "dev"}
|
||||
onClick={openExternal("https://gitlab.com/ryzen-controller-team/ryzen-controller/releases")}
|
||||
background="#EE0000"
|
||||
/>
|
||||
<Badge
|
||||
className="uk-margin-left"
|
||||
value={getTranslation("topbar.beer", "Buy us some beers ❤️")}
|
||||
onClick={openExternal("https://www.patreon.com/ryzencontrollerteam")}
|
||||
background="#888888"
|
||||
/>
|
||||
<Badge
|
||||
className="uk-margin-left"
|
||||
value={getTranslation("topbar.discord", "Join us on discord")}
|
||||
onClick={openExternal("https://discord.gg/EahayUv")}
|
||||
background="#7289da"
|
||||
/>
|
||||
<LightModeContext.Consumer>
|
||||
{mode => (
|
||||
<Badge
|
||||
className="uk-margin-left"
|
||||
value={mode.mode === "dark" ? "☀️" : "🌙"}
|
||||
onClick={mode.switch}
|
||||
background={mode.mode === "dark" ? "#FFF" : "#000"}
|
||||
/>
|
||||
)}
|
||||
</LightModeContext.Consumer>
|
||||
<Badge
|
||||
className="uk-margin-left"
|
||||
value="🇧🇱"
|
||||
onClick={() => {
|
||||
require("uikit")
|
||||
.modal(document.getElementById("locale-selector-modal"))
|
||||
.show();
|
||||
}}
|
||||
background="rgba(0, 0, 0, 0)"
|
||||
/>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method open the given URL using external browser.
|
||||
* @param url The URL to be opened.
|
||||
* @return function
|
||||
*/
|
||||
function openExternal(url: string) {
|
||||
return function openExternalNow() {
|
||||
window.require("electron").remote.shell.openExternal(url);
|
||||
};
|
||||
}
|
||||
|
||||
export default TopBar;
|
9
src/contexts/LightModeContext.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
const LightModeContext = createContext({
|
||||
mode: "light",
|
||||
switch: (): void => {},
|
||||
});
|
||||
LightModeContext.displayName = "LightModeContext";
|
||||
|
||||
export default LightModeContext;
|
103
src/contexts/LocaleContext.tsx
Normal file
@ -0,0 +1,103 @@
|
||||
import { createContext } from "react";
|
||||
import LocaleTranslations from "../locales/LocaleTranslations";
|
||||
const fs = window.require("fs");
|
||||
|
||||
const LocaleContext = createContext({
|
||||
is: "en",
|
||||
change: (to: AvailableLanguages): void => {},
|
||||
getTranslation: (id: string, fallback?: string, variables?: Record<string, string>): string => "",
|
||||
});
|
||||
|
||||
LocaleContext.displayName = "LocaleContext";
|
||||
|
||||
/**
|
||||
* Will add the key to the current locale file.
|
||||
*
|
||||
* @param id The message id
|
||||
* @param currentLocale The current locale
|
||||
* @param fallback The fallback message for the given message id
|
||||
*/
|
||||
function addKeyToLocale(_id: string, _currentLocale: string, _fallback: string | undefined): void {
|
||||
let id = _id;
|
||||
let currentLocale = _currentLocale;
|
||||
let fallback = _fallback;
|
||||
let localeFile = `src/locales/${_currentLocale}.json`;
|
||||
|
||||
let inter = setInterval(() => {
|
||||
let lock = window.require("electron-settings").get("lock");
|
||||
if (lock) {
|
||||
return;
|
||||
}
|
||||
clearInterval(inter);
|
||||
window.require("electron-settings").set("lock", true);
|
||||
console.log(`Writting key ${id} to locale ${currentLocale}...`);
|
||||
fs.readFile(localeFile, (err: string | null, data: string) => {
|
||||
if (err) {
|
||||
console.warn(err);
|
||||
return;
|
||||
}
|
||||
let localeTranslation = JSON.parse(data);
|
||||
localeTranslation[id] = "";
|
||||
if (currentLocale === "en" && fallback) {
|
||||
localeTranslation[id] = fallback;
|
||||
}
|
||||
fs.writeFile(localeFile, JSON.stringify(localeTranslation, null, 4), function(err: string | null) {
|
||||
window.require("electron-settings").delete("lock");
|
||||
if (err) {
|
||||
console.log("error", err);
|
||||
return;
|
||||
}
|
||||
console.log(`Written key ${id} to locale ${currentLocale}.`);
|
||||
});
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return the translated message.
|
||||
*
|
||||
* Exemple:
|
||||
* - Given the message id "test" which give "Hello {firstname}!" as translation
|
||||
* - Usage would be getTranslation("test", {firstname: "Bob"})
|
||||
* - Would return "Hello Bob!"
|
||||
*
|
||||
* Warning:
|
||||
* - For "en" locale, fallback prevail over locales/en.json
|
||||
* - Using dev, id are added in the current locale
|
||||
* - Using dev and "en" locale, en.json will be updated with fallback content
|
||||
*
|
||||
* @param id The message id
|
||||
* @param fallback The fallback message for the given message id
|
||||
* @param variables Variables to replace in the sentence
|
||||
*/
|
||||
function getTranslation(id: string, fallback?: string, variables?: Record<string, string>): string {
|
||||
const electronSettings = window.require("electron-settings");
|
||||
const currentLocale = electronSettings.get("locale") ? (electronSettings.get("locale") as AvailableLanguages) : "en";
|
||||
var sentence: string | undefined = LocaleTranslations[currentLocale][id];
|
||||
|
||||
if (!sentence && sentence !== "") {
|
||||
console.warn(`Missing translation for ${id} in locale ${currentLocale}.`);
|
||||
|
||||
if (process.env.REACT_APP_VERSION?.indexOf("-dev") !== -1) {
|
||||
addKeyToLocale(id, currentLocale, fallback);
|
||||
}
|
||||
}
|
||||
|
||||
if (!sentence || currentLocale === "en") {
|
||||
sentence = fallback ? fallback : id;
|
||||
}
|
||||
|
||||
if (variables) {
|
||||
for (const variable in variables) {
|
||||
if (variables.hasOwnProperty(variable)) {
|
||||
const value = variables[variable];
|
||||
sentence = sentence.replace(new RegExp(`{${variable}}`, "g"), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sentence;
|
||||
}
|
||||
|
||||
export { getTranslation };
|
||||
export default LocaleContext;
|
104
src/contexts/NotificationContext.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
const UIkit = require("uikit");
|
||||
const app = window.require("electron").remote.app;
|
||||
|
||||
type SystemNotifications = Array<Notification>;
|
||||
type GroupedSystemNotifications = {
|
||||
[groupName: string]: SystemNotifications;
|
||||
};
|
||||
let systemNotifications: GroupedSystemNotifications = {};
|
||||
|
||||
type NotificationSettingStatusType = "primary" | "success" | "warning" | "danger";
|
||||
type NotificationSettingPosType =
|
||||
| "top-left"
|
||||
| "top-center"
|
||||
| "top-right"
|
||||
| "bottom-left"
|
||||
| "bottom-center"
|
||||
| "bottom-right";
|
||||
type NotificationSettingsType = {
|
||||
message: string;
|
||||
status?: NotificationSettingStatusType;
|
||||
timeout?: number;
|
||||
group?: string;
|
||||
pos?: NotificationSettingPosType;
|
||||
};
|
||||
|
||||
const custom = function(settings: NotificationSettingsType): void {
|
||||
const window = app.getWindow();
|
||||
if (window.isVisible() && !window.isMinimized()) {
|
||||
if (settings.group) {
|
||||
UIkit.notification.closeAll(settings.group);
|
||||
}
|
||||
UIkit.notification({
|
||||
status: "primary",
|
||||
timeout: 4000,
|
||||
group: null,
|
||||
pos: "top-center",
|
||||
...settings,
|
||||
});
|
||||
} else {
|
||||
let notif;
|
||||
let group = "none";
|
||||
if (settings.group) {
|
||||
group = settings.group;
|
||||
}
|
||||
if (!systemNotifications.hasOwnProperty(group)) {
|
||||
systemNotifications[group] = [];
|
||||
}
|
||||
while ((notif = systemNotifications[group].pop())) {
|
||||
notif.close();
|
||||
}
|
||||
notif = new Notification("Ryzen Controller", {
|
||||
body: settings.message,
|
||||
silent: group !== "none",
|
||||
requireInteraction: group === "none",
|
||||
});
|
||||
notif.onclick = () => {
|
||||
const window = app.getWindow();
|
||||
window.show();
|
||||
if (window.isMinimized()) {
|
||||
window.restore();
|
||||
}
|
||||
if (window.isVisible()) {
|
||||
window.focus();
|
||||
}
|
||||
};
|
||||
systemNotifications[group].push(notif);
|
||||
}
|
||||
};
|
||||
|
||||
const success = function(message: string, group?: string): void {
|
||||
custom({
|
||||
message: message + " 😃",
|
||||
status: "success",
|
||||
group,
|
||||
});
|
||||
};
|
||||
|
||||
const talk = function(message: string, group?: string): void {
|
||||
custom({ message: message, group });
|
||||
};
|
||||
|
||||
const warning = function(message: string, group?: string): void {
|
||||
custom({
|
||||
message: message + " 🙁",
|
||||
status: "warning",
|
||||
group,
|
||||
});
|
||||
};
|
||||
|
||||
const error = function(message: string, group?: string): void {
|
||||
custom({
|
||||
message: message + " 😬",
|
||||
status: "danger",
|
||||
group,
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
success,
|
||||
talk,
|
||||
warning,
|
||||
error,
|
||||
custom,
|
||||
};
|
27
src/contexts/PresetsOnline.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
let context: PresetsOnlineContextType = {
|
||||
loading: true,
|
||||
list: [],
|
||||
update() {},
|
||||
uploadPreset(preset) {
|
||||
return new Promise(res => {
|
||||
res();
|
||||
});
|
||||
},
|
||||
upvote() {
|
||||
return new Promise(res => {
|
||||
res();
|
||||
});
|
||||
},
|
||||
downvote() {
|
||||
return new Promise(res => {
|
||||
res();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const PresetsOnlineContext = createContext(context);
|
||||
PresetsOnlineContext.displayName = "PresetsOnlineContext";
|
||||
|
||||
export default PresetsOnlineContext;
|
273
src/contexts/RyzenAdjContext.tsx
Normal file
@ -0,0 +1,273 @@
|
||||
import NotificationContext from "./NotificationContext";
|
||||
import { getRyzenAdjExecutablePath } from "./RyzenControllerAppContext";
|
||||
import { getTranslation } from "./LocaleContext";
|
||||
|
||||
const RyzenAdjOptionDefinitions: Array<RyzenAdjOptionDefinition> = [
|
||||
{
|
||||
description: getTranslation(
|
||||
"ryzenAdj.slowTime.desc",
|
||||
"This define the period to be used out of boost period to deliver a constant power to be delivered to the socket."
|
||||
),
|
||||
label: getTranslation("ryzenAdj.slowTime.label", "Package Power Tracking (PPT) - Slow period"),
|
||||
tab: "power",
|
||||
min: 1,
|
||||
max: 3600,
|
||||
step: 1,
|
||||
default: 900,
|
||||
ryzenadj_arg: "--slow-time=",
|
||||
ryzenadj_value_convert: "toThousand",
|
||||
},
|
||||
{
|
||||
description: getTranslation(
|
||||
"ryzenAdj.psi0Current.desc",
|
||||
"The limit of current we let the motherboard deliver to the PSI0."
|
||||
),
|
||||
label: getTranslation("ryzenAdj.psi0Current.label", "PSI0 Current Limit (mA)"),
|
||||
tab: "power",
|
||||
min: 20,
|
||||
max: 100,
|
||||
step: 1,
|
||||
default: 20,
|
||||
ryzenadj_arg: "--psi0-current=",
|
||||
ryzenadj_value_convert: "toHex",
|
||||
},
|
||||
{
|
||||
description: getTranslation(
|
||||
"ryzenAdj.vrmmaxCurrent.desc",
|
||||
"The limit of current we let the motherboard deliver to the CPU."
|
||||
),
|
||||
label: getTranslation("ryzenAdj.vrmmaxCurrent.label", "VRM Current (A)"),
|
||||
tab: "power",
|
||||
min: 20,
|
||||
max: 75,
|
||||
step: 1,
|
||||
default: 30,
|
||||
ryzenadj_arg: "--vrmmax-current=",
|
||||
ryzenadj_value_convert: "toThousand",
|
||||
},
|
||||
{
|
||||
description: getTranslation(
|
||||
"ryzenAdj.minGfxclk.desc",
|
||||
"The minimum clock speed the integrated (Vega) GPU is allowed to run at."
|
||||
),
|
||||
label: getTranslation("ryzenAdj.minGfxclk.label", "Minimum Vega iGPU Clock Frequency (Mhz)"),
|
||||
tab: "gpu",
|
||||
min: 400,
|
||||
max: 1300,
|
||||
step: 1,
|
||||
default: 400,
|
||||
ryzenadj_arg: "--min-gfxclk=",
|
||||
ryzenadj_value_convert: "roundTen",
|
||||
},
|
||||
{
|
||||
description: getTranslation(
|
||||
"ryzenAdj.maxGfxclk.desc",
|
||||
"The maximum clock speed the integrated (Vega) GPU is allowed to run at."
|
||||
),
|
||||
label: getTranslation("ryzenAdj.maxGfxclk.label", "Maximum Vega iGPU Clock Frequency (Mhz)"),
|
||||
tab: "gpu",
|
||||
min: 400,
|
||||
max: 1300,
|
||||
step: 1,
|
||||
default: 1100,
|
||||
ryzenadj_arg: "--max-gfxclk=",
|
||||
ryzenadj_value_convert: "roundTen",
|
||||
},
|
||||
{
|
||||
description: getTranslation(
|
||||
"ryzenAdj.minFclkFrequency.desc",
|
||||
"Infinity Fabric is AMD's marketing term for the bus connection that connects processor dies (GPU/CPU). This define the bus's min. clock limit."
|
||||
),
|
||||
label: getTranslation("ryzenAdj.minFclkFrequency.label", "Minimum Infinity Fabric frequency (Mhz)"),
|
||||
tab: "gpu",
|
||||
min: 800,
|
||||
max: 1600,
|
||||
step: 1,
|
||||
default: 800,
|
||||
ryzenadj_arg: "--min-fclk-frequency=",
|
||||
ryzenadj_value_convert: null,
|
||||
},
|
||||
{
|
||||
description: getTranslation(
|
||||
"ryzenAdj.maxFclkFrequency.desc",
|
||||
"Infinity Fabric is AMD's marketing term for the bus connection that connects processor dies (GPU/CPU). This define the bus's max. clock limit."
|
||||
),
|
||||
label: getTranslation("ryzenAdj.maxFclkFrequency.label", "Maximum Infinity Fabric frequency (Mhz)"),
|
||||
tab: "gpu",
|
||||
min: 800,
|
||||
max: 1600,
|
||||
step: 1,
|
||||
default: 1200,
|
||||
ryzenadj_arg: "--max-fclk-frequency=",
|
||||
ryzenadj_value_convert: null,
|
||||
},
|
||||
{
|
||||
description: getTranslation("ryzenAdj.tctlTemp.desc", "The temperature the CPU can reach before boost levels off."),
|
||||
label: getTranslation("ryzenAdj.tctlTemp.label", "Temperature Limit (°C)"),
|
||||
tab: "cpu",
|
||||
min: 50,
|
||||
max: 100,
|
||||
step: 1,
|
||||
default: 75,
|
||||
ryzenadj_arg: "--tctl-temp=",
|
||||
ryzenadj_value_convert: null,
|
||||
},
|
||||
{
|
||||
description: getTranslation(
|
||||
"ryzenAdj.stapmLimit.desc",
|
||||
"Skin Temperature Aware Power Management. This will define the socket power package limit which is used to manage the device boost period."
|
||||
),
|
||||
label: getTranslation("ryzenAdj.stapmLimit.label", "CPU TDP (W)"),
|
||||
tab: "cpu",
|
||||
min: 5,
|
||||
max: 60,
|
||||
step: 1,
|
||||
default: 20,
|
||||
ryzenadj_arg: "--stapm-limit=",
|
||||
ryzenadj_value_convert: "toThousand",
|
||||
},
|
||||
{
|
||||
description: getTranslation(
|
||||
"ryzenAdj.stapmTime.desc",
|
||||
"Skin Temperature Aware Power Management. This will define the boost period to be used."
|
||||
),
|
||||
label: getTranslation("ryzenAdj.stapmTime.label", "CPU Boost Period"),
|
||||
tab: "cpu",
|
||||
min: 1,
|
||||
max: 3600,
|
||||
step: 1,
|
||||
default: 900,
|
||||
ryzenadj_arg: "--stapm-time=",
|
||||
ryzenadj_value_convert: "toThousand",
|
||||
},
|
||||
{
|
||||
description: getTranslation(
|
||||
"ryzenAdj.fastLimit.desc",
|
||||
"The amount of power the CPU can draw while boost levels on."
|
||||
),
|
||||
label: getTranslation("ryzenAdj.fastLimit.label", "CPU Boost TDP (W)"),
|
||||
tab: "cpu",
|
||||
min: 5,
|
||||
max: 60,
|
||||
step: 1,
|
||||
default: 25,
|
||||
ryzenadj_arg: "--fast-limit=",
|
||||
ryzenadj_value_convert: "toThousand",
|
||||
},
|
||||
{
|
||||
description: getTranslation(
|
||||
"ryzenAdj.slowLimit.desc",
|
||||
"The amount of power the CPU can draw while boost levels off."
|
||||
),
|
||||
label: getTranslation("ryzenAdj.slowLimit.label", "CPU Min TDP (W)"),
|
||||
tab: "cpu",
|
||||
min: 5,
|
||||
max: 60,
|
||||
step: 1,
|
||||
default: 10,
|
||||
ryzenadj_arg: "--slow-limit=",
|
||||
ryzenadj_value_convert: "toThousand",
|
||||
},
|
||||
];
|
||||
|
||||
const valueConverter = function(converterName: RyzenAdjConverter, value: number): string {
|
||||
switch (converterName) {
|
||||
case "toHex":
|
||||
if (value < 0) {
|
||||
value = 0xffffffff + value * 1000 + 1;
|
||||
}
|
||||
return "0x" + value.toString(16).toUpperCase();
|
||||
|
||||
case "roundTen":
|
||||
return `${Math.round((value / 10) * 10)}`;
|
||||
|
||||
case "toThousand":
|
||||
return `${value * 1000}`;
|
||||
|
||||
case null:
|
||||
return `${value}`;
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
const getOptionDefinition = function(name: RyzenAdjArguments): RyzenAdjOptionDefinition {
|
||||
return RyzenAdjOptionDefinitions.filter(definition => {
|
||||
return definition.ryzenadj_arg === name;
|
||||
})[0];
|
||||
};
|
||||
|
||||
const createRyzenAdjCommandLine = function(preset: RyzenAdjOptionListType): Array<string> {
|
||||
let commandLine: Array<string> = [];
|
||||
for (const key in preset) {
|
||||
if (!preset.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
// @ts-ignore
|
||||
const arg: RyzenAdjArguments = key;
|
||||
const optionValue = preset[arg];
|
||||
if (!optionValue.enabled) {
|
||||
continue;
|
||||
}
|
||||
commandLine.push(`${arg}${valueConverter(getOptionDefinition(arg).ryzenadj_value_convert, optionValue.value)}`);
|
||||
}
|
||||
return commandLine;
|
||||
};
|
||||
|
||||
const ryzenAdjProcess = function(parameters: Array<string>): Promise<string> {
|
||||
return new Promise((res, rej) => {
|
||||
const child = window.require("child_process").execFile;
|
||||
const executablePath = getRyzenAdjExecutablePath();
|
||||
|
||||
if (parameters.length === 0) {
|
||||
NotificationContext.warning(
|
||||
getTranslation("ryzenAdj.pleaseAddSomeOptions", "Please add some options before applying ryzenAdj.")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`${executablePath} ${parameters.join(" ")}`);
|
||||
|
||||
child(executablePath, parameters, function(err: string, data: Buffer) {
|
||||
var output = data?.toString();
|
||||
if (err) {
|
||||
rej(err);
|
||||
} else if (output) {
|
||||
res(output);
|
||||
}
|
||||
res("no output");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const executeRyzenAdj = function(parameters: Array<string>, notification: boolean = true, retry = 3) {
|
||||
if (retry === 0) {
|
||||
NotificationContext.error(getTranslation("ryzenAdj.unableToApply", "Unable to apply ryzenadj"), "ryzenadj_applied");
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameters.length === 0) {
|
||||
NotificationContext.warning(
|
||||
getTranslation("ryzenAdj.pleaseAddSomeOptions", "Please add some options before applying ryzenAdj.")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
ryzenAdjProcess(parameters)
|
||||
.then((output: string) => {
|
||||
if (notification) {
|
||||
NotificationContext.success(
|
||||
getTranslation("ryzenAdj.applySuccess", "RyzenAdj has been executed successfully."),
|
||||
"ryzenadj_applied"
|
||||
);
|
||||
console.log(output);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
executeRyzenAdj(parameters, notification, retry - 1);
|
||||
console.error(err);
|
||||
});
|
||||
};
|
||||
|
||||
export { RyzenAdjOptionDefinitions, getOptionDefinition, executeRyzenAdj, createRyzenAdjCommandLine };
|
414
src/contexts/RyzenControllerAppContext.tsx
Normal file
@ -0,0 +1,414 @@
|
||||
import { createContext } from "react";
|
||||
import { getOptionDefinition, executeRyzenAdj, createRyzenAdjCommandLine } from "./RyzenAdjContext";
|
||||
import { isNumber } from "util";
|
||||
import compareVersions from "compare-versions";
|
||||
import NotificationContext from "./NotificationContext";
|
||||
import { getTranslation } from "./LocaleContext";
|
||||
const isDev = window.require("electron-is-dev");
|
||||
const electronSettings = window.require("electron-settings");
|
||||
const fileSystem = window.require("fs");
|
||||
const app_version_as_string = process.env?.REACT_APP_VERSION?.replace(/\./g, "_") || "dev";
|
||||
|
||||
const defaultPreset = {
|
||||
"--slow-time=": { enabled: false, value: getOptionDefinition("--slow-time=").default },
|
||||
"--psi0-current=": { enabled: false, value: getOptionDefinition("--psi0-current=").default },
|
||||
"--vrmmax-current=": { enabled: false, value: getOptionDefinition("--vrmmax-current=").default },
|
||||
"--min-gfxclk=": { enabled: false, value: getOptionDefinition("--min-gfxclk=").default },
|
||||
"--max-gfxclk=": { enabled: false, value: getOptionDefinition("--max-gfxclk=").default },
|
||||
"--min-fclk-frequency=": { enabled: false, value: getOptionDefinition("--min-fclk-frequency=").default },
|
||||
"--max-fclk-frequency=": { enabled: false, value: getOptionDefinition("--max-fclk-frequency=").default },
|
||||
"--tctl-temp=": { enabled: false, value: getOptionDefinition("--tctl-temp=").default },
|
||||
"--stapm-limit=": { enabled: false, value: getOptionDefinition("--stapm-limit=").default },
|
||||
"--stapm-time=": { enabled: false, value: getOptionDefinition("--stapm-time=").default },
|
||||
"--fast-limit=": { enabled: false, value: getOptionDefinition("--fast-limit=").default },
|
||||
"--slow-limit=": { enabled: false, value: getOptionDefinition("--slow-limit=").default },
|
||||
};
|
||||
|
||||
const getRyzenAdjExecutablePath = function(): string {
|
||||
const cwd = window.require("electron").remote.app.getAppPath();
|
||||
let path: string | undefined = electronSettings.get(app_version_as_string)?.settings?.ryzenAdjPath;
|
||||
|
||||
if (path) {
|
||||
return path;
|
||||
}
|
||||
path = `${cwd}\\${isDev ? "public\\" : "build\\"}bin\\ryzenadj.exe`;
|
||||
return path;
|
||||
};
|
||||
|
||||
const RyzenControllerSettingsDefinitions: RyzenControllerSettingDefinitionList = {
|
||||
autoStartOnBoot: {
|
||||
displayTitle: false,
|
||||
name: getTranslation("appContext.autoStartOnBoot.name", "Auto start on boot"),
|
||||
type: "boolean",
|
||||
default: false,
|
||||
short_description: getTranslation(
|
||||
"appContext.autoStartOnBoot.shortDesc",
|
||||
"Launch Ryzen Controller on computer start."
|
||||
),
|
||||
compatibility: {
|
||||
linux: false,
|
||||
win32: true,
|
||||
},
|
||||
apply(toBeEnabled) {
|
||||
const platform: "win32" | "linux" = window.require("os").platform();
|
||||
const AutoLaunch = window.require("auto-launch");
|
||||
let autoLaunch = new AutoLaunch({
|
||||
name: "Ryzen Controller",
|
||||
});
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
if (platform === "linux") {
|
||||
return autoLaunch.isEnabled().then((isEnabled: boolean) => {
|
||||
try {
|
||||
if (isEnabled) {
|
||||
autoLaunch.disable();
|
||||
}
|
||||
if (toBeEnabled) {
|
||||
autoLaunch.enable();
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
});
|
||||
} else if (platform === "win32") {
|
||||
// Ensure the old autolaunch has been deleted.
|
||||
autoLaunch.isEnabled().then((isEnabled: boolean) => {
|
||||
try {
|
||||
if (isEnabled) {
|
||||
autoLaunch.disable();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle new auto launch system.
|
||||
const path = `${window
|
||||
.require("electron")
|
||||
.remote.app.getAppPath()}.unpacked\\build\\bin\\auto-start-ryzen-controller.bat`;
|
||||
try {
|
||||
window.require("electron").remote.app.setLoginItemSettings({
|
||||
openAtLogin: toBeEnabled,
|
||||
path: path,
|
||||
});
|
||||
} catch (error) {
|
||||
rej(error);
|
||||
}
|
||||
res(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
minimizeOnLaunch: {
|
||||
displayTitle: false,
|
||||
name: getTranslation("appContext.minimizeOnLaunch.name", "Minimize on launch"),
|
||||
type: "boolean",
|
||||
default: false,
|
||||
short_description: getTranslation("appContext.minimizeOnLaunch.shortDesc", "Launch Ryzen Controller minimized."),
|
||||
compatibility: {
|
||||
linux: true,
|
||||
win32: true,
|
||||
},
|
||||
apply() {
|
||||
return new Promise(resolve => {
|
||||
resolve(true);
|
||||
});
|
||||
},
|
||||
},
|
||||
minimizeToTray: {
|
||||
displayTitle: false,
|
||||
name: getTranslation("appContext.minimizeToTray.name", "Minimize to tray"),
|
||||
type: "boolean",
|
||||
default: false,
|
||||
short_description: getTranslation(
|
||||
"appContext.minimizeToTray.shortDesc",
|
||||
"Minimize Ryzen Controller to tray instead of taskbar."
|
||||
),
|
||||
compatibility: {
|
||||
linux: true,
|
||||
win32: true,
|
||||
},
|
||||
apply() {
|
||||
return new Promise(resolve => {
|
||||
resolve(true);
|
||||
});
|
||||
},
|
||||
},
|
||||
reApplyPeriodically: {
|
||||
displayTitle: true,
|
||||
name: getTranslation("appContext.reApplyPeriodically.name", "Re-apply periodically"),
|
||||
type: "range",
|
||||
default: 0,
|
||||
short_description: getTranslation(
|
||||
"appContext.reApplyPeriodically.shortDesc",
|
||||
"Allow you to re-apply RyzenAdj settings periodically (every X seconds)."
|
||||
),
|
||||
description: getTranslation(
|
||||
"appContext.reApplyPeriodically.desc",
|
||||
"On some laptops, the bios sometimes reset what RyzenAdj is trying to do. You can use this to apply the settings periodically."
|
||||
),
|
||||
compatibility: {
|
||||
linux: true,
|
||||
win32: true,
|
||||
},
|
||||
apply(seconds) {
|
||||
// @ts-ignore
|
||||
let parsedSeconds: number = parseInt(seconds) >= 0 ? parseInt(seconds) * 1000 : 0;
|
||||
if (!isNumber(parsedSeconds)) {
|
||||
return new Promise((resolve, reject) => {
|
||||
reject("ERROR: Value must be of number type.");
|
||||
});
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
let intervalId = electronSettings.get("reApplyPeriodically");
|
||||
clearInterval(intervalId);
|
||||
intervalId = false;
|
||||
electronSettings.set("reApplyPeriodically", false);
|
||||
|
||||
if (parsedSeconds <= 0) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
electronSettings.set(
|
||||
"reApplyPeriodically",
|
||||
setInterval(() => {
|
||||
let preset = electronSettings.get(app_version_as_string).currentSettings;
|
||||
if (!isPresetValid(preset)) {
|
||||
return;
|
||||
}
|
||||
executeRyzenAdj(createRyzenAdjCommandLine(preset), false);
|
||||
}, parsedSeconds)
|
||||
);
|
||||
resolve(true);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
ryzenAdjPath: {
|
||||
displayTitle: true,
|
||||
name: getTranslation("appContext.ryzenAdjPath.name", "RyzenAdj path"),
|
||||
type: "path",
|
||||
default: "\\bin\\ryzenadj.exe",
|
||||
short_description: getTranslation("appContext.ryzenAdjPath.shortDesc", "The full path to ryzenadj binary."),
|
||||
compatibility: {
|
||||
linux: true,
|
||||
win32: true,
|
||||
},
|
||||
apply(path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!path) {
|
||||
path = getRyzenAdjExecutablePath();
|
||||
}
|
||||
if (!fileSystem.existsSync(path)) {
|
||||
reject(
|
||||
getTranslation(
|
||||
"appContext.ryzenAdjPath.wrongPath",
|
||||
"Path to ryzenadj.exe is wrong, please fix it in settings tab."
|
||||
)
|
||||
);
|
||||
}
|
||||
resolve(true);
|
||||
});
|
||||
},
|
||||
},
|
||||
onLaptopPluggedIn: {
|
||||
displayTitle: false,
|
||||
name: "",
|
||||
type: "path",
|
||||
default: false,
|
||||
short_description: "",
|
||||
compatibility: {
|
||||
linux: false,
|
||||
win32: false,
|
||||
},
|
||||
apply() {
|
||||
return new Promise(resolve => {
|
||||
resolve(true);
|
||||
});
|
||||
},
|
||||
},
|
||||
onLaptopPluggedOut: {
|
||||
displayTitle: false,
|
||||
name: "",
|
||||
type: "path",
|
||||
default: false,
|
||||
short_description: "",
|
||||
compatibility: {
|
||||
linux: false,
|
||||
win32: false,
|
||||
},
|
||||
apply() {
|
||||
return new Promise(resolve => {
|
||||
resolve(true);
|
||||
});
|
||||
},
|
||||
},
|
||||
onRCStart: {
|
||||
displayTitle: false,
|
||||
name: "",
|
||||
type: "path",
|
||||
default: false,
|
||||
short_description: "",
|
||||
compatibility: {
|
||||
linux: false,
|
||||
win32: false,
|
||||
},
|
||||
apply() {
|
||||
return new Promise(resolve => {
|
||||
resolve(true);
|
||||
});
|
||||
},
|
||||
},
|
||||
onSessionResume: {
|
||||
displayTitle: false,
|
||||
name: "",
|
||||
type: "path",
|
||||
default: false,
|
||||
short_description: "",
|
||||
compatibility: {
|
||||
linux: false,
|
||||
win32: false,
|
||||
},
|
||||
apply() {
|
||||
return new Promise(resolve => {
|
||||
resolve(true);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const getSettingDefinition = function(
|
||||
name: keyof RyzenControllerSettingDefinitionList
|
||||
): RyzenControllerSettingDefinition | false {
|
||||
if (RyzenControllerSettingsDefinitions.hasOwnProperty(name)) {
|
||||
return RyzenControllerSettingsDefinitions[name];
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const defaultRyzenControllerAppContext: RyzenControllerAppContextType = {
|
||||
latestSettings: defaultPreset,
|
||||
currentSettings: defaultPreset,
|
||||
presets: {},
|
||||
settings: {
|
||||
autoStartOnBoot: false,
|
||||
minimizeOnLaunch: false,
|
||||
minimizeToTray: false,
|
||||
reApplyPeriodically: false,
|
||||
ryzenAdjPath: "",
|
||||
onLaptopPluggedIn: false,
|
||||
onLaptopPluggedOut: false,
|
||||
onRCStart: false,
|
||||
onSessionResume: false,
|
||||
},
|
||||
updateLatestSettings() {},
|
||||
updateCurrentSettings(list) {},
|
||||
addPreset(name, preset) {},
|
||||
removePreset(name) {},
|
||||
updateSettings(settings) {},
|
||||
};
|
||||
|
||||
const isPresetValid = function(preset: PartialRyzenAdjOptionListType): boolean {
|
||||
try {
|
||||
for (const key in preset) {
|
||||
// @ts-ignore
|
||||
const arg: RyzenAdjArguments = key;
|
||||
if (!defaultPreset.hasOwnProperty(arg)) {
|
||||
throw new Error(`ERROR: key "${arg}" in preset does not exist in defaultPreset.`);
|
||||
} else {
|
||||
const value = preset[arg]?.value || -1;
|
||||
const min = getOptionDefinition(arg).min;
|
||||
const max = getOptionDefinition(arg).max;
|
||||
if (min > value || value > max) {
|
||||
throw new Error(`ERROR: "${arg}" with value ${value} is out of bound (${min} - ${max}) in preset.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
let loadedContext = window.require("electron-settings").get(app_version_as_string);
|
||||
let context = defaultRyzenControllerAppContext;
|
||||
if (loadedContext) {
|
||||
context = {
|
||||
...defaultRyzenControllerAppContext,
|
||||
...loadedContext,
|
||||
};
|
||||
}
|
||||
|
||||
const RyzenControllerAppContext = createContext<RyzenControllerAppContextType>(context);
|
||||
RyzenControllerAppContext.displayName = "RyzenControllerAppContext";
|
||||
|
||||
const persistentSave = function(context: RyzenControllerAppContextType) {
|
||||
let savedContext: RyzenControllerAppContextType = {
|
||||
...context,
|
||||
currentSettings: context.latestSettings,
|
||||
};
|
||||
window.require("electron-settings").set(app_version_as_string, savedContext);
|
||||
};
|
||||
|
||||
const executeRyzenAdjUsingPreset = function(presetName: string): boolean {
|
||||
const presets = electronSettings.get(app_version_as_string)?.presets;
|
||||
if (!presets.hasOwnProperty(presetName)) {
|
||||
return false;
|
||||
}
|
||||
if (!isPresetValid(presets[presetName])) {
|
||||
NotificationContext.warning(getTranslation("appContext.invalidPreset", "Unable to apply invalid preset"));
|
||||
return false;
|
||||
}
|
||||
executeRyzenAdj(createRyzenAdjCommandLine(presets[presetName]));
|
||||
return true;
|
||||
};
|
||||
|
||||
const checkIfNewerReleaseExist = function(): void {
|
||||
const currentVersion = process.env?.REACT_APP_VERSION;
|
||||
if (!currentVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch("https://gitlab.com/api/v4/projects/11046417/releases")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const last_released_version = data[0]?.tag_name;
|
||||
if (!last_released_version) {
|
||||
throw new Error("Unable to check for new release");
|
||||
}
|
||||
if (compareVersions.compare(currentVersion, last_released_version, "<")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.then((isNewReleaseExist: boolean) => {
|
||||
if (isNewReleaseExist) {
|
||||
NotificationContext.talk(
|
||||
getTranslation("appContext.newReleaseAvailable", "A new release is available, please check the release tab.")
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.warn(err);
|
||||
});
|
||||
};
|
||||
|
||||
export default RyzenControllerAppContext;
|
||||
export {
|
||||
context as defaultRyzenControllerAppContext,
|
||||
defaultPreset,
|
||||
persistentSave,
|
||||
RyzenControllerSettingsDefinitions,
|
||||
getSettingDefinition,
|
||||
getRyzenAdjExecutablePath,
|
||||
app_version_as_string,
|
||||
executeRyzenAdjUsingPreset,
|
||||
checkIfNewerReleaseExist as checkNewVersion,
|
||||
isPresetValid,
|
||||
};
|
53
src/contexts/SysInfoContext.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import { createContext } from "react";
|
||||
import { Systeminformation } from "systeminformation";
|
||||
|
||||
const hasher = require("object-hash");
|
||||
|
||||
export type SysInfoState = {
|
||||
cpu: Systeminformation.CpuData | false;
|
||||
graphics: Systeminformation.GraphicsData | false;
|
||||
mem: Systeminformation.MemData | false;
|
||||
memLayout: Array<Systeminformation.MemLayoutData> | false;
|
||||
system: Systeminformation.SystemData | false;
|
||||
bios: Systeminformation.BiosData | false;
|
||||
signature: string | false;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
const createMachineSignature = function(data: SysInfoState): string | false {
|
||||
if (data.error) {
|
||||
return false;
|
||||
}
|
||||
if (data.system === false || data.mem === false || data.cpu === false || data.graphics === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasher({
|
||||
"system.manufacturer": data.system.manufacturer,
|
||||
"system.model": data.system.model,
|
||||
"system.version": data.system.version,
|
||||
"mem.total": data.mem.total,
|
||||
"cpu.manufacturer": data.cpu.manufacturer,
|
||||
"cpu.brand": data.cpu.brand,
|
||||
"cpu.speedmax": data.cpu.speedmax,
|
||||
"cpu.physicalCores": data.cpu.physicalCores,
|
||||
"cpu.cores": data.cpu.cores,
|
||||
"gpu.controllers": data.graphics.controllers,
|
||||
});
|
||||
};
|
||||
|
||||
let context: SysInfoState = {
|
||||
cpu: false,
|
||||
graphics: false,
|
||||
mem: false,
|
||||
memLayout: false,
|
||||
system: false,
|
||||
bios: false,
|
||||
signature: false,
|
||||
};
|
||||
|
||||
const SysInfoContext = createContext(context);
|
||||
SysInfoContext.displayName = "SysInfoContext";
|
||||
|
||||
export default SysInfoContext;
|
||||
export { createMachineSignature };
|
29
src/index.scss
Normal file
@ -0,0 +1,29 @@
|
||||
@import "~uikit/dist/css/uikit";
|
||||
|
||||
html {
|
||||
#root {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
.uk-pointer,
|
||||
.uk-range {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.uk-modal {
|
||||
background-color: rgba(50, 50, 50, 0.6);
|
||||
|
||||
.uk-modal-dialog {
|
||||
background-color: #1e87f0;
|
||||
|
||||
.uk-form-stacked {
|
||||
.uk-modal-footer {
|
||||
background-color: #1e87f0;
|
||||
}
|
||||
}
|
||||
.uk-modal-footer {
|
||||
background-color: #1e87f0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
src/index.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import "./index.scss";
|
||||
import App from "./App";
|
||||
|
||||
const UIkit = require("uikit");
|
||||
const Icons = require("uikit/dist/js/uikit-icons");
|
||||
UIkit.use(Icons);
|
||||
|
||||
const rootEl = document.getElementById("root");
|
||||
ReactDOM.render(<App />, rootEl);
|
||||
|
||||
// For hot reload
|
||||
// See https://github.com/vitaliy-bobrov/angular-hot-loader/issues/5#issuecomment-377785900
|
||||
if ((module as any).hot) {
|
||||
(module as any).hot.accept("./App", () => {
|
||||
const NextApp = require("./App").default;
|
||||
ReactDOM.render(<NextApp />, rootEl);
|
||||
});
|
||||
}
|
8
src/locales/LocaleTranslations.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
/* Add language here with the corresponding json file, use en.json as reference */
|
||||
export default {
|
||||
en: require("./en.json") as Record<string, string>,
|
||||
fr: require("./fr.json") as Record<string, string>,
|
||||
ch: require("./ch.json") as Record<string, string>,
|
||||
de: require("./de.json") as Record<string, string>,
|
||||
tr: require("./tr.json") as Record<string, string>,
|
||||
};
|
102
src/locales/ch.json
Normal file
@ -0,0 +1,102 @@
|
||||
{
|
||||
"topbar.discord": "加入我们的Discord群组",
|
||||
"topbar.beer": "捐助我们 ❤️",
|
||||
"presetButtons.applyPresetTooltip": "将性能预设填充到所有调整项, 且立即应用调整指令",
|
||||
"presetButtons.loadPresetTooltip": "将性能预设填充到所有调整项, 但暂不应用调整指令",
|
||||
"presetAutoApply.whenSessionResume": "重新打开窗口时",
|
||||
"presetAutoApply.whenRCStart": "当Ryzen Controller启动时",
|
||||
"presetButtons.confirmDeletion": "确认删除预设 \"{preset}\"?",
|
||||
"presetButtons.loadedPreset": "已删除预设 {preset} .",
|
||||
"presetButtons.uploadPresetConfirmation": "确认上传预设 {preset}?",
|
||||
"presetListEmpty.youDontHaveAny": "当前没有性能预设",
|
||||
"presetListEmpty.sentencePart1": "请点击",
|
||||
"presetListEmpty.createPresetBtn": "新增预设",
|
||||
"presetListEmpty.sentencePart2": "按钮来创建一个性能预设. 该按钮可在Ryzen Adj设置 (CPU, GPU, ...) 选项卡中找到. ",
|
||||
"PresetOnline.listNotLoadedYet": "列表尚未加载或暂无在线列表",
|
||||
"PresetOnline.loadPresetListBtn": "加载预设列表",
|
||||
"PresetOnline.sentencePart1": "请点击预设列表中的",
|
||||
"PresetOnline.uploadBtn": "上传",
|
||||
"PresetOnline.sentencePart2": "按钮来在线分享你的预设. ",
|
||||
"sceneSelector.cpuTitle": "CPU",
|
||||
"sceneSelector.gpuTitle": "GPU",
|
||||
"ryzenAdj.maxGfxclk.label": "最大 Vega iGPU 时钟频率 (Mhz)",
|
||||
"ryzenAdj.stapmTime.desc": "表面温度感应功耗管理 (STAMP) . 该选项将定义GPU睿频时间. ",
|
||||
"ryzenAdj.slowLimit.desc": "睿频稳定时, CPU可达到的最大功耗",
|
||||
"appContext.minimizeOnLaunch.name": "软件启动时最小化",
|
||||
"appContext.ryzenAdjPath.name": "RyzenAdj所在的文件夹路径",
|
||||
"sceneSelector.settingsTitle": "设置",
|
||||
"sceneSelector.powerTitle": "电源管理",
|
||||
"ryzenAdj.slowTime.desc": "在睿频周期以外",
|
||||
"ryzenAdj.slowTime.label": "封装电源追踪 (PPT) - 减速期 (Slow Period)",
|
||||
"ryzenAdj.minFclkFrequency.desc": "Infinity Fabric (IF总线) 是AMD新架构中连接核心 (CPU/GPU) 的Bus总线. 这一选项将定义总线的最低时钟频率. ",
|
||||
"ryzenAdj.maxFclkFrequency.desc": "Infinity Fabric (IF总线) 是AMD新架构中连接核心 (CPU/GPU) 的Bus总线. 这一选项将定义总线的最高时钟频率. ",
|
||||
"ryzenAdj.stapmLimit.desc": "表面温度感应功耗管理 (STAMP) . 该选项将定义插槽电源包 (Socket Power Package) 的限值, 该限值用于管理设备的睿频时长. ",
|
||||
"ryzenAdj.stapmTime.label": "CPU 睿频时间",
|
||||
"ryzenAdj.vrmmaxCurrent.desc": "主板向CPU传输电流的限值.",
|
||||
"ryzenAdj.vrmmaxCurrent.label": "VRM 电流 (A)",
|
||||
"appContext.ryzenAdjPath.shortDesc": "ryzenadj 目录的完整路径.",
|
||||
"ryzenAdj.maxFclkFrequency.label": "最大 Infinity Fabric (IF总线) 频率 (Mhz)",
|
||||
"ryzenAdj.stapmLimit.label": "CPU 功耗 TDP (W)",
|
||||
"appContext.autoStartOnBoot.shortDesc": "设置 Ryzen Controller 开机自启.",
|
||||
"sceneSelector.presetsTitle": "预设",
|
||||
"ryzenAdj.psi0Current.label": "PSI0 电流限制 (mA)",
|
||||
"ryzenAdj.maxGfxclk.desc": "Vega核心显卡的最高运行频率.",
|
||||
"ryzenAdj.minFclkFrequency.label": " Infinity Fabric (IF总线) 的最低频率 (Mhz)",
|
||||
"ryzenAdj.slowLimit.label": "CPU 最小 TDP (W)",
|
||||
"appContext.minimizeOnLaunch.shortDesc": "最小化启动 Ryzen Controller .",
|
||||
"ryzenAdj.minGfxclk.label": "最低 Vega iGPU 时钟频率 (Mhz)",
|
||||
"ryzenAdj.fastLimit.desc": "睿频加速时, CPU可以达到的最大功耗.",
|
||||
"appContext.minimizeToTray.name": "最小化到托盘",
|
||||
"ryzenAdj.psi0Current.desc": "主板向PSI0传输电流的限值.",
|
||||
"ryzenAdj.minGfxclk.desc": "Vega核心显卡的最低运行频率.",
|
||||
"ryzenAdj.fastLimit.label": "CPU 睿频功耗 TDP (W)",
|
||||
"appContext.autoStartOnBoot.name": "开机时自启",
|
||||
"appContext.minimizeToTray.shortDesc": "将 Ryzen Controller 最小化到托盘而不是任务栏. ",
|
||||
"appContext.reApplyPeriodically.desc": "在某些笔记本上, Bios会自动覆盖Ryzen Adj发送的的性能调整指令. 启用这一选项, RyzenAdj将循环发送性能调整指令. ",
|
||||
"sceneSelector.releasesTitle": "版本更新",
|
||||
"ryzenAdj.tctlTemp.desc": "在降频之前笔记本可以达到的温度 (温度墙) .",
|
||||
"appContext.reApplyPeriodically.name": "循环应用",
|
||||
"ryzenAdj.tctlTemp.label": "温度墙 (°C)",
|
||||
"appContext.reApplyPeriodically.shortDesc": "循环执行 RyzenAdj 指令(每 X 秒一次).",
|
||||
"PresetsScene.onlinePresetTitle": "云端性能预设",
|
||||
"PresetsScene.localPresetTitle": "本地性能预设",
|
||||
"PresetsScene.autoApplyTitle": "自动应用预设",
|
||||
"SettingsScene.settingsTitle": "软件设置",
|
||||
"settingForm.currentPath": "当前路径:",
|
||||
"sysInfoCards.biosVersion": "Bios 版本号:",
|
||||
"sysInfoCards.gpuPerfDesc": "{ram}Mb (动态 vram: {dyn}).",
|
||||
"SettingsScene.sysInfoTitle": "系统信息",
|
||||
"sysInfoCards.basicInfoTitle": "基本信息",
|
||||
"settingForm.browseBtn": "浏览",
|
||||
"SettingsScene.systemHashDesc": "该选项用于保证下载预设时的的稳定性.",
|
||||
"sysInfoCards.CPUInfoTitle": "CPU 信息",
|
||||
"sysInfoCards.cpuPerfDesc": "最高频率{speedmax}Ghz, {physicalCores} 核心, {cores} 线程.",
|
||||
"sysInfoCards.GPUInfoTitle": "GPU #{index} 信息",
|
||||
"SettingsScene.loadingSysHash": "正在加载...",
|
||||
"ryzenAdjBottomBar.prompt": "新预设的名称",
|
||||
"ryzenAdjBottomBar.mustProvideName": "请填写预设名称",
|
||||
"ryzenAdjBottomBar.presetCreated": "预设 \"{newPresetName}\" 已经创建",
|
||||
"ryzenAdjBottomBar.presetWithSameNameExist": "名为 \"{newPresetName}\" 的预设已经存在",
|
||||
"presetButtons.apply": "应用",
|
||||
"presetButtons.delete": "删除",
|
||||
"presetButtons.load": "加载",
|
||||
"presetAutoApply.nonePreset": "无",
|
||||
"presetButtons.upload": "上传",
|
||||
"ryzenAdj.applySuccess": "RyzenAdj 已经成功执行性能调整指令.",
|
||||
"presetButtons.uploadSucceed": "预设 {preset} 已上传",
|
||||
"presetOnlineBtn.downloadTooltip": "预设将被存储到本地. ",
|
||||
"presetOnlineBtn.loadTooltip": "不保存性能预设, 也不应用预设, 仅将预设数据填充在RyzenAdj的性能调整项中. ",
|
||||
"presetOnlineBtn.download": "下载",
|
||||
"presetOnlineBtn.load": "加载",
|
||||
"presetButtons.presetWithSameNameAlreadyExistOnline": "在线服务器中已存在同名预设",
|
||||
"presetOnlineBtn.presetDownloaded": "预设 \"{presetName}\" 已加载",
|
||||
"presetOnlineBtn.presetSameNameExist": "同名预设已存在",
|
||||
"PresetsScene.cantVoteTwiceSamePreset": "不能为相同的预设多次投票",
|
||||
"PresetsScene.confirmVote": "确定要 {vote} 这一预设吗? ",
|
||||
"PresetsScene.updatingVotes": "正在上传投票内容... ",
|
||||
"notification.settingsSaveSuccess": "设置已经成功保存",
|
||||
"ryzenAdjBottomBar.apply": "应用",
|
||||
"ryzenAdjBottomBar.createPreset": "新增预设",
|
||||
"ryzenAdjBottomBar.reset": "重置",
|
||||
"app.localeSelectorModalTitle": "更改语言"
|
||||
}
|
102
src/locales/de.json
Normal file
@ -0,0 +1,102 @@
|
||||
{
|
||||
"topbar.discord": "Schließ dich uns in Discord an",
|
||||
"topbar.beer": "Spendier uns ein Bierchen ❤️",
|
||||
"presetButtons.applyPresetTooltip": "Die preset wird im RyzenAdj's Tab geladen und angewandt.",
|
||||
"presetButtons.loadPresetTooltip": "Die preset wird im RyzenAdj's Tab geladen jedoch nicht angewandt.",
|
||||
"presetAutoApply.whenSessionResume": "Wenn die session besteht",
|
||||
"presetAutoApply.whenRCStart": "Wenn Ryzen Controller startet",
|
||||
"presetButtons.confirmDeletion": "Bist du sicher, die \"{preset}\" zu entfernen?",
|
||||
"presetButtons.loadedPreset": "Preset {preset} wurde geladen.",
|
||||
"presetButtons.uploadPresetConfirmation": "Bist du sicher, die preset {preset} hochzuladen?",
|
||||
"presetListEmpty.youDontHaveAny": "Du hast noch keinen preset",
|
||||
"presetListEmpty.sentencePart1": "Du kannst einen erstellen, indem du den Button",
|
||||
"presetListEmpty.createPresetBtn": "Preset erstellen",
|
||||
"presetListEmpty.sentencePart2": "in den RyzenAdj Einstellungen Tab (CPU, GPU, ...) anklickst.",
|
||||
"PresetOnline.listNotLoadedYet": "Die Liste wurde noch nicht geladen oder es gibt noch keine Online Presets.",
|
||||
"PresetOnline.loadPresetListBtn": "Preset Liste laden",
|
||||
"PresetOnline.sentencePart1": "Du kannst deine selbsterstellten presets teilen, in dem du den Button",
|
||||
"PresetOnline.uploadBtn": "Hochladen",
|
||||
"PresetOnline.sentencePart2": "in den Presets Tab anklickst.",
|
||||
"sceneSelector.cpuTitle": "CPU",
|
||||
"sceneSelector.gpuTitle": "GPU",
|
||||
"ryzenAdj.maxGfxclk.label": "Maximale Vega iGPU Takt Frequenz (Mhz)",
|
||||
"ryzenAdj.stapmTime.desc": "Skin Temperature Aware Power Management (STAPM). Dies definiert die Boost Periode, die genutzt werden soll.",
|
||||
"ryzenAdj.slowLimit.desc": "Die Menge der Leistung, die die CPU ohne Boost aufbrauchen soll.",
|
||||
"appContext.minimizeOnLaunch.name": "Minimieren",
|
||||
"appContext.ryzenAdjPath.name": "RyzenAdj Pfad",
|
||||
"sceneSelector.settingsTitle": "Einstellungen",
|
||||
"sceneSelector.powerTitle": "Leistung",
|
||||
"ryzenAdj.slowTime.desc": "Dies definiert die Periode der konstanten Energiezufuhr zum Sockel während der Boostfreien Phase.",
|
||||
"ryzenAdj.slowTime.label": "Package Power Tracking (PPT) - Slow period (Langsame Phase)",
|
||||
"ryzenAdj.minFclkFrequency.desc": "Infinity Fabric ist AMD's Marketingbegriff für die Busverbindung, welche die Prozessor dies (CPU/GPU) verbindet. Dies definiert die mindest Takt Frequenz des Bus.",
|
||||
"ryzenAdj.maxFclkFrequency.desc": "Infinity Fabric ist AMD's Marketingbegriff für die Busverbindung, welche die Prozessor dies (CPU/GPU) verbindet. Dies definiert die maximale Takt Frequenz des Bus.",
|
||||
"ryzenAdj.stapmLimit.desc": "Skin Temperature Aware Power Management (STAPM). Dies definiert das Sockel power package Limit, welches für die Verwaltung der Boost Periode genutzt wird.",
|
||||
"ryzenAdj.stapmTime.label": "CPU Boost Periode",
|
||||
"ryzenAdj.vrmmaxCurrent.desc": "Das aktuelle Limit der Zufuhr durch die Motherboard an die CPU.",
|
||||
"ryzenAdj.vrmmaxCurrent.label": "VRM Current (A)",
|
||||
"appContext.ryzenAdjPath.shortDesc": "Der volle Pfad zur RyzenAdj Binary.",
|
||||
"ryzenAdj.maxFclkFrequency.label": "Maximale Infinity Fabric Frequenz (Mhz)",
|
||||
"ryzenAdj.stapmLimit.label": "CPU TDP (W)",
|
||||
"appContext.autoStartOnBoot.shortDesc": "Starte Ryzen Controller bei Systemstart.",
|
||||
"sceneSelector.presetsTitle": "Presets",
|
||||
"ryzenAdj.psi0Current.label": "PSI0 Current Limit (mA)",
|
||||
"ryzenAdj.maxGfxclk.desc": "Die maximale Takt Frequenz auf der die die integrierte GPU (Vega) laufen darf.",
|
||||
"ryzenAdj.minFclkFrequency.label": "Minimale Infinity Fabric Frequenz (Mhz)",
|
||||
"ryzenAdj.slowLimit.label": "CPU Min TDP (W)",
|
||||
"appContext.minimizeOnLaunch.shortDesc": "Starte Ryzen Controller minimiert.",
|
||||
"ryzenAdj.minGfxclk.label": "Minimale Vega iGPU Takt Frequenz (Mhz)",
|
||||
"ryzenAdj.fastLimit.desc": "Die Menge der Leistung, die die CPU während des Boosts aufbrauchen soll.",
|
||||
"appContext.minimizeToTray.name": "Minimiere in den Infobereich",
|
||||
"ryzenAdj.psi0Current.desc": "Das Limit, das die Motherboard an den PSI0 zuführen darf.",
|
||||
"ryzenAdj.minGfxclk.desc": "Die minimale Takt Frequenz auf der die die integrierte GPU (Vega) laufen darf.",
|
||||
"ryzenAdj.fastLimit.label": "CPU Boost TDP (W)",
|
||||
"appContext.autoStartOnBoot.name": "Automatischer Start beim booten",
|
||||
"appContext.minimizeToTray.shortDesc": "Minimiere Ryzen Controller in den Infobereich anstatt der Taskleiste.",
|
||||
"appContext.reApplyPeriodically.desc": "Auf einigen Laptops setzt das BIOS Einstellungen von RyztenAdj zurück. Damit kannst du die Einstellungen automatisiert in Perioden automatisch anwenden lassen.",
|
||||
"sceneSelector.releasesTitle": "Versionen",
|
||||
"ryzenAdj.tctlTemp.desc": "Die Temperatur, die die CPU erreichen kann, bevor Boost ausgeschaltet wird.",
|
||||
"appContext.reApplyPeriodically.name": "Periodisch erneut anwenden",
|
||||
"ryzenAdj.tctlTemp.label": "Temperatur Limit (°C)",
|
||||
"appContext.reApplyPeriodically.shortDesc": "Erlaubt dir die RyzenAdj Einstellungen periodisch erneut anzuwenden (\"alle X Sekunden\").",
|
||||
"PresetsScene.onlinePresetTitle": "Online Presets",
|
||||
"PresetsScene.localPresetTitle": "Lokale Presets",
|
||||
"PresetsScene.autoApplyTitle": "Presets unter bestimmten Bedingungen automatisch anwenden",
|
||||
"SettingsScene.settingsTitle": "Einstellungen",
|
||||
"settingForm.currentPath": "Aktueller Pfad:",
|
||||
"sysInfoCards.biosVersion": "BIOS Version:",
|
||||
"sysInfoCards.gpuPerfDesc": "{ram}Mb (Dynamic vram: {dyn}).",
|
||||
"SettingsScene.sysInfoTitle": "System Info",
|
||||
"sysInfoCards.basicInfoTitle": "Basic Information",
|
||||
"settingForm.browseBtn": "Auswählen",
|
||||
"SettingsScene.systemHashDesc": "Dies wird genutzt, um bei heruntergeladenen presets die Kompatibilität zu gewährleisten.",
|
||||
"sysInfoCards.CPUInfoTitle": "CPU Information",
|
||||
"sysInfoCards.cpuPerfDesc": "{speedmax}Ghz auf {physicalCores} Kernen, {cores} threads.",
|
||||
"sysInfoCards.GPUInfoTitle": "GPU #{index} Information",
|
||||
"SettingsScene.loadingSysHash": "Lädt...",
|
||||
"ryzenAdjBottomBar.prompt": "Neuer preset Name",
|
||||
"ryzenAdjBottomBar.mustProvideName": "Du musst einen Namen eingeben",
|
||||
"ryzenAdjBottomBar.presetCreated": "Preset \"{newPresetName}\" erstellt",
|
||||
"ryzenAdjBottomBar.presetWithSameNameExist": "Ein neuer Preset mit dem Namen \"{newPresetName}\" existiert bereits",
|
||||
"presetButtons.apply": "Anwenden",
|
||||
"presetButtons.delete": "Entfernen",
|
||||
"presetButtons.load": "Laden",
|
||||
"presetAutoApply.nonePreset": "Keine",
|
||||
"presetButtons.upload": "Hochladen",
|
||||
"ryzenAdj.applySuccess": "RyzenAdj wurde erfolgreich ausgeführt.",
|
||||
"presetButtons.uploadSucceed": "Preset {preset} wurde hochgeladen",
|
||||
"presetOnlineBtn.downloadTooltip": "Speichert den preset zur lokalen Preset-Sammlung.",
|
||||
"presetOnlineBtn.loadTooltip": "Ohne Speichern des Presets, wird dieser in RyzenAdj's Tabs geladen, aber nicht angewendet.",
|
||||
"presetOnlineBtn.download": "Herunterladen",
|
||||
"presetOnlineBtn.load": "Laden",
|
||||
"presetButtons.presetWithSameNameAlreadyExistOnline": "Ein Preset mit dem gleichen Namen existiert Online bereits",
|
||||
"presetOnlineBtn.presetDownloaded": "Preset \"{presetName}\" wurde geladen",
|
||||
"presetOnlineBtn.presetSameNameExist": "Du hast bereits einen Preset mit den gleichen Namen",
|
||||
"PresetsScene.cantVoteTwiceSamePreset": "Du kannst nicht mehrfach für den gleichen Preset abstimmen",
|
||||
"PresetsScene.confirmVote": "Bist du sicher, für dieses Preset {vote} abzustimmen?",
|
||||
"PresetsScene.updatingVotes": "Aktualisiere Abstimmungen...",
|
||||
"notification.settingsSaveSuccess": "Einstellungen wurden erfolgreich gespeichert",
|
||||
"ryzenAdjBottomBar.apply": "Anwenden",
|
||||
"ryzenAdjBottomBar.createPreset": "Preset erstellen",
|
||||
"ryzenAdjBottomBar.reset": "Zurücksetzen",
|
||||
"app.localeSelectorModalTitle": "Sprache ändern"
|
||||
}
|
102
src/locales/en.json
Normal file
@ -0,0 +1,102 @@
|
||||
{
|
||||
"topbar.discord": "Join us on Discord",
|
||||
"topbar.beer": "Buy us some beers ❤️",
|
||||
"presetButtons.applyPresetTooltip": "The preset will be loaded in RyzenAdj's tabs and applied.",
|
||||
"presetButtons.loadPresetTooltip": "The preset will be loaded in RyzenAdj's tabs but not applied.",
|
||||
"presetAutoApply.whenSessionResume": "When session resume",
|
||||
"presetAutoApply.whenRCStart": "When Ryzen Controller starts",
|
||||
"presetButtons.confirmDeletion": "Are you sure to delete \"{preset}\"?",
|
||||
"presetButtons.loadedPreset": "Preset {preset} has been loaded.",
|
||||
"presetButtons.uploadPresetConfirmation": "Are you sure to upload the preset {preset}?",
|
||||
"presetListEmpty.youDontHaveAny": "You don't have any preset yet",
|
||||
"presetListEmpty.sentencePart1": "You can create one by using the",
|
||||
"presetListEmpty.createPresetBtn": "Create preset",
|
||||
"presetListEmpty.sentencePart2": "button available on RyzenAdj settings tabs (CPU, GPU, ...).",
|
||||
"PresetOnline.listNotLoadedYet": "List hasn't been loaded or there is no online preset yet.",
|
||||
"PresetOnline.loadPresetListBtn": "Load preset list",
|
||||
"PresetOnline.sentencePart1": "You can share your own preset by clicking on the",
|
||||
"PresetOnline.uploadBtn": "Upload",
|
||||
"PresetOnline.sentencePart2": "button available on your presets.",
|
||||
"sceneSelector.cpuTitle": "CPU",
|
||||
"sceneSelector.gpuTitle": "GPU",
|
||||
"ryzenAdj.maxGfxclk.label": "Maximum Vega iGPU Clock Frequency (Mhz)",
|
||||
"ryzenAdj.stapmTime.desc": "Skin Temperature Aware Power Management (STAPM). This defines the boost period to be used.",
|
||||
"ryzenAdj.slowLimit.desc": "The amount of power the CPU can draw while boost levels off.",
|
||||
"appContext.minimizeOnLaunch.name": "Minimize on launch",
|
||||
"appContext.ryzenAdjPath.name": "RyzenAdj path",
|
||||
"sceneSelector.settingsTitle": "Settings",
|
||||
"sceneSelector.powerTitle": "Power",
|
||||
"ryzenAdj.slowTime.desc": "This defines the period to be used out of boost period to deliver a constant power to be delivered to the socket.",
|
||||
"ryzenAdj.slowTime.label": "Package Power Tracking (PPT) - Slow period",
|
||||
"ryzenAdj.minFclkFrequency.desc": "Infinity Fabric is AMD's marketing term for the bus connection that connects processor dies (GPU/CPU). This defines the bus's min. clock limit.",
|
||||
"ryzenAdj.maxFclkFrequency.desc": "Infinity Fabric is AMD's marketing term for the bus connection that connects processor dies (GPU/CPU). This defines the bus's max. clock limit.",
|
||||
"ryzenAdj.stapmLimit.desc": "Skin Temperature Aware Power Management (STAPM). This defines the socket power package limit which is used to manage the device boost period.",
|
||||
"ryzenAdj.stapmTime.label": "CPU Boost Period",
|
||||
"ryzenAdj.vrmmaxCurrent.desc": "The limit of current we let the motherboard deliver to the CPU.",
|
||||
"ryzenAdj.vrmmaxCurrent.label": "VRM Current (A)",
|
||||
"appContext.ryzenAdjPath.shortDesc": "The full path to RyzenAdj binary.",
|
||||
"ryzenAdj.maxFclkFrequency.label": "Maximum Infinity Fabric frequency (Mhz)",
|
||||
"ryzenAdj.stapmLimit.label": "CPU TDP (W)",
|
||||
"appContext.autoStartOnBoot.shortDesc": "Launch Ryzen Controller on computer start.",
|
||||
"sceneSelector.presetsTitle": "Presets",
|
||||
"ryzenAdj.psi0Current.label": "PSI0 Current Limit (mA)",
|
||||
"ryzenAdj.maxGfxclk.desc": "The maximum clock speed the integrated (Vega) GPU is allowed to run at.",
|
||||
"ryzenAdj.minFclkFrequency.label": "Minimum Infinity Fabric frequency (Mhz)",
|
||||
"ryzenAdj.slowLimit.label": "CPU Min TDP (W)",
|
||||
"appContext.minimizeOnLaunch.shortDesc": "Launch Ryzen Controller minimized.",
|
||||
"ryzenAdj.minGfxclk.label": "Minimum Vega iGPU Clock Frequency (Mhz)",
|
||||
"ryzenAdj.fastLimit.desc": "The amount of power the CPU can draw while boost levels on.",
|
||||
"appContext.minimizeToTray.name": "Minimize to tray",
|
||||
"ryzenAdj.psi0Current.desc": "The limit of current we let the motherboard deliver to the PSI0.",
|
||||
"ryzenAdj.minGfxclk.desc": "The minimum clock speed the integrated (Vega) GPU is allowed to run at.",
|
||||
"ryzenAdj.fastLimit.label": "CPU Boost TDP (W)",
|
||||
"appContext.autoStartOnBoot.name": "Auto start on boot",
|
||||
"appContext.minimizeToTray.shortDesc": "Minimize Ryzen Controller to tray instead of taskbar.",
|
||||
"appContext.reApplyPeriodically.desc": "On some laptops, the BIOS sometimes reset what RyzenAdj is trying to do. You can use this to apply the settings periodically.",
|
||||
"sceneSelector.releasesTitle": "Releases",
|
||||
"ryzenAdj.tctlTemp.desc": "The temperature the CPU can reach before boost levels off.",
|
||||
"appContext.reApplyPeriodically.name": "Re-apply periodically",
|
||||
"ryzenAdj.tctlTemp.label": "Temperature Limit (°C)",
|
||||
"appContext.reApplyPeriodically.shortDesc": "Allow you to re-apply RyzenAdj settings periodically (every X seconds).",
|
||||
"PresetsScene.onlinePresetTitle": "Online Presets",
|
||||
"PresetsScene.localPresetTitle": "Local Presets",
|
||||
"PresetsScene.autoApplyTitle": "Auto apply preset",
|
||||
"SettingsScene.settingsTitle": "Settings",
|
||||
"settingForm.currentPath": "Current path:",
|
||||
"sysInfoCards.biosVersion": "BIOS version:",
|
||||
"sysInfoCards.gpuPerfDesc": "{ram}Mb (Dynamic vram: {dyn}).",
|
||||
"SettingsScene.sysInfoTitle": "System Info",
|
||||
"sysInfoCards.basicInfoTitle": "Basic Information",
|
||||
"settingForm.browseBtn": "Browse",
|
||||
"SettingsScene.systemHashDesc": "This will be used to ensure downloaded presets compatibility.",
|
||||
"sysInfoCards.CPUInfoTitle": "CPU Information",
|
||||
"sysInfoCards.cpuPerfDesc": "{speedmax}Ghz on {physicalCores} cores, {cores} threads.",
|
||||
"sysInfoCards.GPUInfoTitle": "GPU #{index} Information",
|
||||
"SettingsScene.loadingSysHash": "Loading...",
|
||||
"ryzenAdjBottomBar.prompt": "New preset name",
|
||||
"ryzenAdjBottomBar.mustProvideName": "You must provide a name",
|
||||
"ryzenAdjBottomBar.presetCreated": "Preset \"{newPresetName}\" created",
|
||||
"ryzenAdjBottomBar.presetWithSameNameExist": "A preset with the name \"{newPresetName}\" already exist",
|
||||
"presetButtons.apply": "Apply",
|
||||
"presetButtons.delete": "Delete",
|
||||
"presetButtons.load": "Load",
|
||||
"presetAutoApply.nonePreset": "None",
|
||||
"presetButtons.upload": "Upload",
|
||||
"ryzenAdj.applySuccess": "RyzenAdj has been executed successfully.",
|
||||
"presetButtons.uploadSucceed": "Preset {preset} has been uploaded",
|
||||
"presetOnlineBtn.downloadTooltip": "Will save the preset to your local preset.",
|
||||
"presetOnlineBtn.loadTooltip": "Without saving the preset, it will be loaded in RyzenAdj's tabs but not applied.",
|
||||
"presetOnlineBtn.download": "Download",
|
||||
"presetOnlineBtn.load": "Load",
|
||||
"presetButtons.presetWithSameNameAlreadyExistOnline": "A preset with the same name already exist online",
|
||||
"presetOnlineBtn.presetDownloaded": "Preset \"{presetName}\" has been loaded",
|
||||
"presetOnlineBtn.presetSameNameExist": "You already have a preset with the same name",
|
||||
"PresetsScene.cantVoteTwiceSamePreset": "You can't vote twice for the same preset",
|
||||
"PresetsScene.confirmVote": "Are you sure to {vote} this preset?",
|
||||
"PresetsScene.updatingVotes": "Updating votes...",
|
||||
"notification.settingsSaveSuccess": "Settings has been saved successfully",
|
||||
"ryzenAdjBottomBar.apply": "Apply",
|
||||
"ryzenAdjBottomBar.createPreset": "Create preset",
|
||||
"ryzenAdjBottomBar.reset": "Reset",
|
||||
"app.localeSelectorModalTitle": "Change language"
|
||||
}
|
102
src/locales/fr.json
Normal file
@ -0,0 +1,102 @@
|
||||
{
|
||||
"topbar.discord": "Rejoignez-nous sur discord",
|
||||
"topbar.beer": "Une bière pour les devs ❤️",
|
||||
"presetButtons.applyPresetTooltip": "Le preset sera chargé dans les onglets de RyzenAdj et appliqué.",
|
||||
"presetButtons.loadPresetTooltip": "Le preset sera chargé dans les onglets de RyzenAdj mais ne sera pas appliqué.",
|
||||
"presetAutoApply.whenSessionResume": "Lorsque la session reprend",
|
||||
"presetAutoApply.whenRCStart": "Quand Ryzen Controller est lancé",
|
||||
"presetButtons.confirmDeletion": "Êtes-vous sûr de supprimer \"{preset}\" ?",
|
||||
"presetButtons.loadedPreset": "Le preset {preset} a été chargée.",
|
||||
"presetButtons.uploadPresetConfirmation": "Êtes-vous sûr de charger le preset {preset} ?",
|
||||
"presetListEmpty.youDontHaveAny": "Vous n'avez pas encore de preset",
|
||||
"presetListEmpty.sentencePart1": "Vous pouvez en créer un en utilisant le button",
|
||||
"presetListEmpty.createPresetBtn": "Créer preset",
|
||||
"presetListEmpty.sentencePart2": "disponible dans les onglets des paramètres RyzenAdj (CPU, GPU, ...).",
|
||||
"PresetOnline.listNotLoadedYet": "La liste n'a pas été chargée ou il n'y a pas encore de preset en ligne.",
|
||||
"PresetOnline.loadPresetListBtn": "Actualiser la liste",
|
||||
"PresetOnline.sentencePart1": "Vous pouvez partager votre propre preset en cliquant sur le button",
|
||||
"PresetOnline.uploadBtn": "Upload",
|
||||
"PresetOnline.sentencePart2": "disponible sur vos presets.",
|
||||
"sceneSelector.cpuTitle": "CPU",
|
||||
"sceneSelector.gpuTitle": "GPU",
|
||||
"ryzenAdj.maxGfxclk.label": "Fréquence maximale de l'horloge Vega iGPU (Mhz)",
|
||||
"ryzenAdj.stapmTime.desc": "Skin Temperature Aware Power Management. Ceci définira la période de boost à utiliser.",
|
||||
"ryzenAdj.slowLimit.desc": "La limite de puissance du CPU pendant la période de boost est désactivé.",
|
||||
"appContext.minimizeOnLaunch.name": "Minimiser au lancement",
|
||||
"appContext.ryzenAdjPath.name": "Le chemin vers RyzenAdj",
|
||||
"sceneSelector.settingsTitle": "Paramètres",
|
||||
"sceneSelector.powerTitle": "Power",
|
||||
"ryzenAdj.slowTime.desc": "Il définit la période à utiliser en dehors de la période de boost pour fournir une puissance constante.",
|
||||
"ryzenAdj.slowTime.label": "Package Power Tracking (PPT) - Période de ralentissement",
|
||||
"ryzenAdj.minFclkFrequency.desc": "Infinity Fabric est le terme marketing d'AMD pour la connexion de bus qui relie les matrices de processeur (GPU/CPU). Ce terme définit la limite d'horloge minimale du bus.",
|
||||
"ryzenAdj.maxFclkFrequency.desc": "Infinity Fabric est le terme marketing d'AMD pour la connexion de bus qui relie les puces de processeur (GPU/CPU). Ce terme définit la limite d'horloge maximale du bus.",
|
||||
"ryzenAdj.stapmLimit.desc": "Skin Temperature Aware Power Management (gestion de l'énergie en fonction de la température). Ceci définit la limite de la puissance de la prise qui est utilisée pour gérer la période de boost du dispositif.",
|
||||
"ryzenAdj.stapmTime.label": "Durée du Boost CPU",
|
||||
"ryzenAdj.vrmmaxCurrent.desc": "La limite de courant que la carte mère est autorisé à livrer au CPU.",
|
||||
"ryzenAdj.vrmmaxCurrent.label": "Courant du VRM (A)",
|
||||
"appContext.ryzenAdjPath.shortDesc": "Le chemin complet vers le binaire ryzenadj.",
|
||||
"ryzenAdj.maxFclkFrequency.label": "Fréquence maximale de l'Infinity Fabric (Mhz)",
|
||||
"ryzenAdj.stapmLimit.label": "CPU TDP (W)",
|
||||
"appContext.autoStartOnBoot.shortDesc": "Lancer Ryzen Controller au démarrage de l'ordinateur.",
|
||||
"sceneSelector.presetsTitle": "presets",
|
||||
"ryzenAdj.psi0Current.label": "Limite de courant PSI0 (mA)",
|
||||
"ryzenAdj.maxGfxclk.desc": "La vitesse d'horloge maximale à laquelle le GPU intégré (Vega) est autorisé à fonctionner.",
|
||||
"ryzenAdj.minFclkFrequency.label": "Fréquence minimale du tissu à l'infini (Mhz)",
|
||||
"ryzenAdj.slowLimit.label": "CPU Min TDP (W)",
|
||||
"appContext.minimizeOnLaunch.shortDesc": "Lancer Ryzen Controller minimisé.",
|
||||
"ryzenAdj.minGfxclk.label": "Fréquence d'horloge minimale du Vega iGPU (Mhz)",
|
||||
"ryzenAdj.fastLimit.desc": "La limite de puissance du CPU pendant la période de boost.",
|
||||
"appContext.minimizeToTray.name": "Réduire dans la barre d'état'.",
|
||||
"ryzenAdj.psi0Current.desc": "La limite de courant que la carte mère est autorisé à livrer au PSI0.",
|
||||
"ryzenAdj.minGfxclk.desc": "La vitesse d'horloge minimale à laquelle le GPU intégré (Vega) est autorisé à fonctionner.",
|
||||
"ryzenAdj.fastLimit.label": "TDP de Boost CPU (W)",
|
||||
"appContext.autoStartOnBoot.name": "Démarrage automatique",
|
||||
"appContext.minimizeToTray.shortDesc": "Réduire Ryzen Controller à la barre d'état au lieu de la barre des tâches.",
|
||||
"appContext.reApplyPeriodically.desc": "Sur certains ordinateurs portables, le bios réinitialise parfois ce que RyzenAdj essaie de faire. Vous pouvez l'utiliser pour appliquer les réglages périodiquement.",
|
||||
"sceneSelector.releasesTitle": "Releases",
|
||||
"ryzenAdj.tctlTemp.desc": "La température que le CPU peut atteindre avant la période de boost se désactive.",
|
||||
"appContext.reApplyPeriodically.name": "Ré-appliquer RyzenAdj périodiquement",
|
||||
"ryzenAdj.tctlTemp.label": "Limite de température (°C)",
|
||||
"appContext.reApplyPeriodically.shortDesc": "Permet de réappliquer les paramètres de RyzenAdj périodiquement (toutes les X secondes).",
|
||||
"PresetsScene.onlinePresetTitle": "presets en ligne",
|
||||
"PresetsScene.localPresetTitle": "presets en local",
|
||||
"PresetsScene.autoApplyTitle": "Application automatique de preset",
|
||||
"SettingsScene.settingsTitle": "Paramètres",
|
||||
"settingForm.currentPath": "Chemin actuel :",
|
||||
"sysInfoCards.biosVersion": "Version du bios :",
|
||||
"sysInfoCards.gpuPerfDesc": "{ram}Mb (RAM dynamique : {dyn}).",
|
||||
"SettingsScene.sysInfoTitle": "Informations sur le système",
|
||||
"sysInfoCards.basicInfoTitle": "Informations de base",
|
||||
"settingForm.browseBtn": "Changer",
|
||||
"SettingsScene.systemHashDesc": "Cela servira à assurer la compatibilité des presets téléchargés.",
|
||||
"sysInfoCards.CPUInfoTitle": "Informations sur le CPU",
|
||||
"sysInfoCards.cpuPerfDesc": "{speedmax}Ghz avec {physicalCores} cores et {cores} threads.",
|
||||
"sysInfoCards.GPUInfoTitle": "Information sur le GPU #{index}",
|
||||
"SettingsScene.loadingSysHash": "Chargement...",
|
||||
"ryzenAdjBottomBar.prompt": "Nom du nouveau preset",
|
||||
"ryzenAdjBottomBar.mustProvideName": "Vous devez renseigner un nom",
|
||||
"ryzenAdjBottomBar.presetCreated": "preset \"{newPresetName}\" créé",
|
||||
"ryzenAdjBottomBar.presetWithSameNameExist": "Un preset \"{newPresetName}\" existe déjà",
|
||||
"presetButtons.apply": "Appliquer",
|
||||
"presetButtons.delete": "Supprimer",
|
||||
"presetButtons.load": "Charger",
|
||||
"presetAutoApply.nonePreset": "Aucun",
|
||||
"presetButtons.upload": "Upload",
|
||||
"ryzenAdj.applySuccess": "RyzenAdj a été exécuté avec succès.",
|
||||
"presetButtons.uploadSucceed": "Le preset {preset} a été uploadé",
|
||||
"presetOnlineBtn.downloadTooltip": "Sauvegardera le preset dans vos preset en local.",
|
||||
"presetOnlineBtn.loadTooltip": "Sans sauvegarder le preset, il sera chargé dans les onglets de RyzenAdj mais ne sera pas appliqué.",
|
||||
"presetOnlineBtn.download": "Télécharger",
|
||||
"presetOnlineBtn.load": "Charger",
|
||||
"presetButtons.presetWithSameNameAlreadyExistOnline": "Un preset du même nom existe déjà en ligne",
|
||||
"presetOnlineBtn.presetDownloaded": "Le preset \"{presetName}\" a été chargée",
|
||||
"presetOnlineBtn.presetSameNameExist": "Vous avez déjà un preset du même nom",
|
||||
"PresetsScene.cantVoteTwiceSamePreset": "Vous ne pouvez pas voter deux fois pour le même preset",
|
||||
"PresetsScene.confirmVote": "Vous allez {vote} ce preset, êtes-vous sûr ?",
|
||||
"PresetsScene.updatingVotes": "Mise à jour des votes...",
|
||||
"notification.settingsSaveSuccess": "Les réglages ont été sauvegardés avec succès",
|
||||
"ryzenAdjBottomBar.apply": "Appliquer",
|
||||
"ryzenAdjBottomBar.reset": "Reset",
|
||||
"ryzenAdjBottomBar.createPreset": "Créer preset",
|
||||
"app.localeSelectorModalTitle": "Changer de langage"
|
||||
}
|
102
src/locales/tr.json
Normal file
@ -0,0 +1,102 @@
|
||||
{
|
||||
"topbar.discord": "Discord'a katıl",
|
||||
"topbar.beer": "Bağış yap ❤️",
|
||||
"presetButtons.applyPresetTooltip": "Preset RyzenAdj sekmesinde yüklendi ve uygulandı",
|
||||
"presetButtons.loadPresetTooltip": "Preset RyzenAdj sekmesinde yüklendi ama uygulanmadı",
|
||||
"presetAutoApply.whenSessionResume": "Oturum mevcut ise",
|
||||
"presetAutoApply.whenRCStart": "Ryzen Controller başlar ise",
|
||||
"presetButtons.confirmDeletion": "\"{preset}\" kaldırılmasına eminmisin?",
|
||||
"presetButtons.loadedPreset": "Preset {preset} yüklendi.",
|
||||
"presetButtons.uploadPresetConfirmation": "Preset {preset} online yüklenmesine eminmisin?",
|
||||
"presetListEmpty.youDontHaveAny": "Henüz Preset yok",
|
||||
"presetListEmpty.sentencePart1": "Birini oluşturmak için, RyzenAdj Ayarlar sekmesindeki (CPU, GPU, ...)",
|
||||
"presetListEmpty.createPresetBtn": "Preset oluştur",
|
||||
"presetListEmpty.sentencePart2": "buttonuna tıkla.",
|
||||
"PresetOnline.listNotLoadedYet": "Liste henüz yüklenilmedi veya hiçbir online Preset mevcut değil.",
|
||||
"PresetOnline.loadPresetListBtn": "Preset Listeyi yükle",
|
||||
"PresetOnline.sentencePart1": "Oluşturduğunuz Preset'leri Preset sekmesindeki",
|
||||
"PresetOnline.uploadBtn": "Online yükle",
|
||||
"PresetOnline.sentencePart2": "buttonu ile paylaşabilirsin.",
|
||||
"sceneSelector.cpuTitle": "CPU",
|
||||
"sceneSelector.gpuTitle": "GPU",
|
||||
"ryzenAdj.maxGfxclk.label": "Maksimum Vega iGPU Saat Frekans (Mhz)",
|
||||
"ryzenAdj.stapmTime.desc": "Skin Temperature Aware Power Management (STAPM). Bu kullanılacak Boost dönemini ayarlıyor.",
|
||||
"ryzenAdj.slowLimit.desc": "CPU'nun Boost kullanılmadığı dönemde, en az kullanacak performans miktarı.",
|
||||
"appContext.minimizeOnLaunch.name": "Minimieren",
|
||||
"appContext.ryzenAdjPath.name": "RyzenAdj dizin yolu",
|
||||
"sceneSelector.settingsTitle": "Ayarlar",
|
||||
"sceneSelector.powerTitle": "Performans",
|
||||
"ryzenAdj.slowTime.desc": "Bu Sokete (Boost kullanılmadığı dönemde) sürekli enerji sağlama dönemini ayarlıyor.",
|
||||
"ryzenAdj.slowTime.label": "Package Power Tracking (PPT) - Slow period (Yavaş Dönem)",
|
||||
"ryzenAdj.minFclkFrequency.desc": "Infinity Fabric AMD'nin Piyasadaki terimidir ve işlemci die'lerini bağlayan veri yolu bağlantısıdır. Bu ayar bağlantının (IF) minimum saat frekansını ayarlıyor.",
|
||||
"ryzenAdj.maxFclkFrequency.desc": "Infinity Fabric AMD'nin Piyasadaki terimidir ve işlemci die'lerini bağlayan veri yolu bağlantısıdır. Bu ayar bağlantının (IF) maksimum saat frekansını ayarlıyor.",
|
||||
"ryzenAdj.stapmLimit.desc": "Skin Temperature Aware Power Management (STAPM). Bu Soket power package limit'i ayarlıyor. Boost dönemi yönetimi ayarlıyor.",
|
||||
"ryzenAdj.stapmTime.label": "CPU Boost Dönemi",
|
||||
"ryzenAdj.vrmmaxCurrent.desc": "Anakarttan CPU'ya giden güncel limit",
|
||||
"ryzenAdj.vrmmaxCurrent.label": "VRM Current (A)",
|
||||
"appContext.ryzenAdjPath.shortDesc": "RyzenAdj Binary'nin dizin yolu.",
|
||||
"ryzenAdj.maxFclkFrequency.label": "Maksimum Infinity Fabric Frekans (Mhz)",
|
||||
"ryzenAdj.stapmLimit.label": "CPU TDP (W)",
|
||||
"appContext.autoStartOnBoot.shortDesc": "Ryzen Controller uygulamasını System ile başlat.",
|
||||
"sceneSelector.presetsTitle": "Preset'ler",
|
||||
"ryzenAdj.psi0Current.label": "PSI0 Current Limit (mA)",
|
||||
"ryzenAdj.maxGfxclk.desc": "Maksimum iGPU (Vega) Saat Frekans.",
|
||||
"ryzenAdj.minFclkFrequency.label": "Minimum Infinity Fabric Frekans (Mhz)",
|
||||
"ryzenAdj.slowLimit.label": "CPU Min TDP (W)",
|
||||
"appContext.minimizeOnLaunch.shortDesc": "Ryzen Controller uygulamasını küçülterek başlat.",
|
||||
"ryzenAdj.minGfxclk.label": "Minimum Vega iGPU Saat Frekans (Mhz)",
|
||||
"ryzenAdj.fastLimit.desc": "CPU'nun Boost döneminde kullanabilecek performans miktarı.",
|
||||
"appContext.minimizeToTray.name": "Bildirim alanına küçült",
|
||||
"ryzenAdj.psi0Current.desc": "Anakarttan PSI0'ya giden limit.",
|
||||
"ryzenAdj.minGfxclk.desc": "Minimum iGPU (Vega) Saat Frekansı.",
|
||||
"ryzenAdj.fastLimit.label": "CPU Boost TDP (W)",
|
||||
"appContext.autoStartOnBoot.name": "Açılışta otomatik başlatma",
|
||||
"appContext.minimizeToTray.shortDesc": "Ryzen Controller uygulamasını bildirim alanına küçült.",
|
||||
"appContext.reApplyPeriodically.desc": "Bazı Laptop BIOS ayarları RyzenAdj ayarlarını sıfırlıyor. Bu ayar ile ayarlanmış vakitlerde ayarlar otomatik şekilde tekrar uygulanabilir.",
|
||||
"sceneSelector.releasesTitle": "Versiyonlar",
|
||||
"ryzenAdj.tctlTemp.desc": "Boost kapanmadan CPU'nun ulaşabileceği sıcaklık.",
|
||||
"appContext.reApplyPeriodically.name": "Periyodik olarak yeniden uygula",
|
||||
"ryzenAdj.tctlTemp.label": "Sıcaklık Limit (°C)",
|
||||
"appContext.reApplyPeriodically.shortDesc": "RyzenAdj ayarlarını periyodik yeniden uygulamasını sağlıyor (\"her X saniye\").",
|
||||
"PresetsScene.onlinePresetTitle": "Online Preset'ler",
|
||||
"PresetsScene.localPresetTitle": "Yerel Preset'ler",
|
||||
"PresetsScene.autoApplyTitle": "Preset'leri bazı koşullarda uygula",
|
||||
"SettingsScene.settingsTitle": "Ayarlar",
|
||||
"settingForm.currentPath": "Güncel dizin yolu:",
|
||||
"sysInfoCards.biosVersion": "BIOS Versiyonu:",
|
||||
"sysInfoCards.gpuPerfDesc": "{ram}Mb (Dynamic vram: {dyn}).",
|
||||
"SettingsScene.sysInfoTitle": "System Bilgi",
|
||||
"sysInfoCards.basicInfoTitle": "Basic Bilgi",
|
||||
"settingForm.browseBtn": "Seç",
|
||||
"SettingsScene.systemHashDesc": "Bu bilgi, indirilmiş Preset'lerin bu system ve uygulama versiyon uygunluklarını sağlamak için kullanılıyor.",
|
||||
"sysInfoCards.CPUInfoTitle": "CPU Bilgi",
|
||||
"sysInfoCards.cpuPerfDesc": "{speedmax}Ghz, {physicalCores} çekirdek, {cores} threads.",
|
||||
"sysInfoCards.GPUInfoTitle": "GPU #{index} Bilgi",
|
||||
"SettingsScene.loadingSysHash": "Yükleniyor...",
|
||||
"ryzenAdjBottomBar.prompt": "Yeni Preset adı",
|
||||
"ryzenAdjBottomBar.mustProvideName": "Bir ad girilmesi lazım",
|
||||
"ryzenAdjBottomBar.presetCreated": "Preset \"{newPresetName}\" oluşturuldu",
|
||||
"ryzenAdjBottomBar.presetWithSameNameExist": "Yeni bir \"{newPresetName}\" adlı Preset zaten mevcut",
|
||||
"presetButtons.apply": "Uygula",
|
||||
"presetButtons.delete": "Kaldır",
|
||||
"presetButtons.load": "Yükle",
|
||||
"presetAutoApply.nonePreset": "Yok",
|
||||
"presetButtons.upload": "Online yükle",
|
||||
"ryzenAdj.applySuccess": "RyzenAdj başarıyla uygulandı.",
|
||||
"presetButtons.uploadSucceed": "Preset {preset} online yüklendi",
|
||||
"presetOnlineBtn.downloadTooltip": "Preset'i yerel (offline) Preset-Koleksiyonuna yüklüyor.",
|
||||
"presetOnlineBtn.loadTooltip": "Yüklenilmez ise, Preset RyzenAdj sekmesine yüklenilir, ama uygulanılmaz.",
|
||||
"presetOnlineBtn.download": "Indir",
|
||||
"presetOnlineBtn.load": "Yükle",
|
||||
"presetButtons.presetWithSameNameAlreadyExistOnline": "Aynı adlı bir Online Preset zaten mevcut",
|
||||
"presetOnlineBtn.presetDownloaded": "Preset \"{presetName}\" yüklendi",
|
||||
"presetOnlineBtn.presetSameNameExist": "Aynı adlı bir Preset zaten mevcut",
|
||||
"PresetsScene.cantVoteTwiceSamePreset": "Bir Preset için bir den fazla oy verilemiyor",
|
||||
"PresetsScene.confirmVote": "Bu Preset için {vote} oyu vermeye eminmisin?",
|
||||
"PresetsScene.updatingVotes": "Oylar güncelleniyor...",
|
||||
"notification.settingsSaveSuccess": "Ayarlar başarıyla kaydedildi",
|
||||
"ryzenAdjBottomBar.apply": "Uygula",
|
||||
"ryzenAdjBottomBar.createPreset": "Preset oluştur",
|
||||
"ryzenAdjBottomBar.reset": "Sıfırla",
|
||||
"app.localeSelectorModalTitle": "Dil değiştir"
|
||||
}
|
123
src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
/// <reference types="react-scripts" />
|
||||
|
||||
/* Add language here */
|
||||
type AvailableLanguages = "en" | "fr" | "ch" | "de" | "tr";
|
||||
|
||||
type RyzenControllerTabForRyzenAdj = "power" | "cpu" | "gpu";
|
||||
|
||||
type RyzenAdjArguments =
|
||||
| "--slow-time="
|
||||
| "--psi0-current="
|
||||
| "--vrmmax-current="
|
||||
| "--min-gfxclk="
|
||||
| "--max-gfxclk="
|
||||
| "--min-fclk-frequency="
|
||||
| "--max-fclk-frequency="
|
||||
| "--tctl-temp="
|
||||
| "--stapm-limit="
|
||||
| "--stapm-time="
|
||||
| "--fast-limit="
|
||||
| "--slow-limit=";
|
||||
|
||||
type RyzenAdjConverter = "toHex" | "roundTen" | "toThousand" | null;
|
||||
|
||||
type RyzenAdjOptionDefinition = {
|
||||
description: string;
|
||||
label: string;
|
||||
tab: RyzenControllerTabForRyzenAdj;
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
default: number;
|
||||
ryzenadj_arg: RyzenAdjArguments;
|
||||
ryzenadj_value_convert: RyzenAdjConverter;
|
||||
};
|
||||
|
||||
type RyzenAdjOptionValue = {
|
||||
enabled: boolean;
|
||||
value: number;
|
||||
};
|
||||
|
||||
type RyzenAdjOptionListType = { [args in RyzenAdjArguments]: RyzenAdjOptionValue };
|
||||
type RyzenAdjOptionListNamedType = { [name: string]: RyzenAdjOptionListType };
|
||||
|
||||
type PartialRyzenAdjOptionListType = { [args in RyzenAdjArguments]?: RyzenAdjOptionValue };
|
||||
type PartialRyzenAdjOptionListNamedType = {
|
||||
[name: string]: PartialRyzenAdjOptionListType;
|
||||
};
|
||||
|
||||
type RyzenAdjOptionContextType = {
|
||||
update(name: RyzenAdjArguments, value: Partial<RyzenAdjOptionValue>): void;
|
||||
list: PartialRyzenAdjOptionListType;
|
||||
};
|
||||
|
||||
type RyzenControllerSettingsNames =
|
||||
| "autoStartOnBoot"
|
||||
| "minimizeOnLaunch"
|
||||
| "minimizeToTray"
|
||||
| "reApplyPeriodically"
|
||||
| "ryzenAdjPath"
|
||||
| "onLaptopPluggedIn"
|
||||
| "onLaptopPluggedOut"
|
||||
| "onRCStart"
|
||||
| "onSessionResume";
|
||||
|
||||
type RyzenControllerSettings = {
|
||||
autoStartOnBoot: boolean;
|
||||
minimizeOnLaunch: boolean;
|
||||
minimizeToTray: boolean;
|
||||
reApplyPeriodically: number | false;
|
||||
ryzenAdjPath: string;
|
||||
onLaptopPluggedIn: string | false;
|
||||
onLaptopPluggedOut: string | false;
|
||||
onRCStart: string | false;
|
||||
onSessionResume: string | false;
|
||||
};
|
||||
|
||||
type RyzenControllerSettingDefinition = {
|
||||
displayTitle: boolean;
|
||||
name: string;
|
||||
short_description: string;
|
||||
description?: string;
|
||||
type: "boolean" | "range" | "path";
|
||||
default: boolean | number | string;
|
||||
compatibility: {
|
||||
win32: boolean;
|
||||
linux: boolean;
|
||||
};
|
||||
apply(value: boolean | number | string): Promise<string | boolean>;
|
||||
};
|
||||
|
||||
type RyzenControllerSettingDefinitionList = {
|
||||
[args in RyzenControllerSettingsNames]: RyzenControllerSettingDefinition;
|
||||
};
|
||||
|
||||
type RyzenControllerAppContextType = {
|
||||
latestSettings: RyzenAdjOptionListType;
|
||||
currentSettings: RyzenAdjOptionListType;
|
||||
presets: RyzenAdjOptionListNamedType;
|
||||
settings: RyzenControllerSettings;
|
||||
updateLatestSettings(): void;
|
||||
updateCurrentSettings(list: PartialRyzenAdjOptionListType): void;
|
||||
addPreset(name: string, preset: PartialRyzenAdjOptionListType): void;
|
||||
removePreset(name: keyof RyzenAdjOptionListNamedType): void;
|
||||
updateSettings(settings: Partial<RyzenControllerSettings>): void;
|
||||
};
|
||||
|
||||
type ApiPreset = {
|
||||
id: number;
|
||||
systemHash: string;
|
||||
upvote: number;
|
||||
downvote: number;
|
||||
name: string;
|
||||
ryzenAdjArguments: RyzenAdjOptionListType;
|
||||
};
|
||||
|
||||
type PresetsOnlineContextType = {
|
||||
loading: boolean;
|
||||
list: Array<ApiPreset>;
|
||||
update(): void;
|
||||
uploadPreset(preset: Partial<ApiPreset>): Promise<ApiPreset>;
|
||||
upvote(presetId: number): Promise<ApiPreset>;
|
||||
downvote(presetId: number): Promise<ApiPreset>;
|
||||
};
|
220
src/scenes/PresetsScene.tsx
Normal file
@ -0,0 +1,220 @@
|
||||
import * as React from "react";
|
||||
import RyzenControllerAppContext from "../contexts/RyzenControllerAppContext";
|
||||
import PresetListEmpty from "../components/PresetListEmpty";
|
||||
import PresetLine from "../components/PresetLine";
|
||||
import SceneTitle from "../components/SceneTitle";
|
||||
import PresetAutoApplyCards from "../components/PresetAutoApplyCards";
|
||||
import PresetOnline from "../components/PresetOnline";
|
||||
import NotificationContext from "../contexts/NotificationContext";
|
||||
import PresetsOnlineContext from "../contexts/PresetsOnline";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
const uikit = window.require("uikit");
|
||||
|
||||
class PresetsScene extends React.Component<{}, PresetsOnlineContextType> {
|
||||
_isMounted = false;
|
||||
state: PresetsOnlineContextType = {
|
||||
loading: false,
|
||||
list: [],
|
||||
update: this.updatePresetList.bind(this),
|
||||
uploadPreset: this.uploadPreset.bind(this),
|
||||
upvote: this.upvote.bind(this),
|
||||
downvote: this.downvote.bind(this),
|
||||
};
|
||||
|
||||
__constructor() {
|
||||
this.updatePresetList = this.updatePresetList.bind(this);
|
||||
this.uploadPreset = this.uploadPreset.bind(this);
|
||||
this.upvote = this.upvote.bind(this);
|
||||
this.downvote = this.downvote.bind(this);
|
||||
this.vote = this.vote.bind(this);
|
||||
this.retainVotedPreset = this.retainVotedPreset.bind(this);
|
||||
this.isUserAlreadyVotedForThisPreset = this.isUserAlreadyVotedForThisPreset.bind(this);
|
||||
}
|
||||
|
||||
uploadPreset(preset: ApiPreset) {
|
||||
const requestOption: RequestInit = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/ld+json",
|
||||
"Content-Type": "application/ld+json",
|
||||
},
|
||||
body: JSON.stringify(preset),
|
||||
};
|
||||
|
||||
return fetch(process.env.REACT_APP_SERVER_ENDPOINT + "/presets", requestOption)
|
||||
.then(response => response.json())
|
||||
.then((data: ApiPreset | any) => {
|
||||
if (data?.violations?.length) {
|
||||
data.violations.map((violation: any) => {
|
||||
NotificationContext.error(`${violation.pathProperty}: ${violation.message}`);
|
||||
return false;
|
||||
});
|
||||
throw new Error("Violation detected while POST request to API.");
|
||||
}
|
||||
if (data?.id) {
|
||||
// @ts-ignore
|
||||
let newSavedPreset: ApiPreset = data;
|
||||
return newSavedPreset;
|
||||
}
|
||||
throw new Error("Unable to upload the preset.");
|
||||
});
|
||||
}
|
||||
|
||||
updatePresetList() {
|
||||
this.setState({ loading: true });
|
||||
const requestOption: RequestInit = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
};
|
||||
fetch(process.env.REACT_APP_SERVER_ENDPOINT + "/presets", requestOption)
|
||||
.then(response => response.json())
|
||||
.then((data: Array<ApiPreset>) => {
|
||||
if (this._isMounted) this.setState({ list: data, loading: false });
|
||||
});
|
||||
}
|
||||
|
||||
downvote(presetId: number): Promise<ApiPreset> {
|
||||
if (this.isUserAlreadyVotedForThisPreset(presetId)) {
|
||||
let message = getTranslation("PresetsScene.cantVoteTwiceSamePreset", "You can't vote twice for the same preset");
|
||||
NotificationContext.warning(message);
|
||||
return new Promise((res, rej) => {
|
||||
rej(message);
|
||||
});
|
||||
}
|
||||
let confirmMessage = getTranslation("PresetsScene.confirmVote", "Are you sure to {vote} this preset?", {
|
||||
vote: "👎",
|
||||
});
|
||||
return uikit.modal.confirm(confirmMessage).then(() => {
|
||||
return this.vote(presetId, "down");
|
||||
});
|
||||
}
|
||||
|
||||
upvote(presetId: number): Promise<ApiPreset> {
|
||||
if (this.isUserAlreadyVotedForThisPreset(presetId)) {
|
||||
let message = getTranslation("PresetsScene.cantVoteTwiceSamePreset", "You can't vote twice for the same preset");
|
||||
NotificationContext.warning(message);
|
||||
return new Promise((res, rej) => {
|
||||
rej(message);
|
||||
});
|
||||
}
|
||||
let confirmMessage = getTranslation("PresetsScene.confirmVote", "Are you sure to {vote} this preset?", {
|
||||
vote: "👍",
|
||||
});
|
||||
return uikit.modal.confirm(confirmMessage).then(() => {
|
||||
return this.vote(presetId, "up");
|
||||
});
|
||||
}
|
||||
|
||||
vote(presetId: number, action: "up" | "down"): Promise<ApiPreset> {
|
||||
const url = `${process.env.REACT_APP_SERVER_ENDPOINT}/presets/${presetId}`;
|
||||
const requestOptionGet: RequestInit = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
};
|
||||
const requestOptionPatch: RequestInit = {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"Content-Type": "application/merge-patch+json",
|
||||
},
|
||||
};
|
||||
NotificationContext.talk(getTranslation("PresetsScene.updatingVotes", "Updating votes..."));
|
||||
return fetch(url, requestOptionGet)
|
||||
.then(response => response.json())
|
||||
.then((data: ApiPreset) => {
|
||||
const presetField = `${action}vote`;
|
||||
const value = action === "up" ? data.upvote + 1 : data.downvote - 1;
|
||||
const newPresetValue: Partial<ApiPreset> = {
|
||||
[presetField]: value,
|
||||
};
|
||||
return fetch(url, {
|
||||
...requestOptionPatch,
|
||||
body: JSON.stringify(newPresetValue),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((data: ApiPreset) => {
|
||||
this.retainVotedPreset(presetId);
|
||||
this.updatePresetList();
|
||||
return data;
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
NotificationContext.error(getTranslation("PresetsScene.errorWhileSendingVote", "Error while sending vote"));
|
||||
throw new Error(error);
|
||||
});
|
||||
}
|
||||
|
||||
isUserAlreadyVotedForThisPreset(presetId: number): boolean {
|
||||
const votedPresets: Array<number> = window.require("electron-settings").get("votedPresets");
|
||||
if (!votedPresets) {
|
||||
return false;
|
||||
}
|
||||
return votedPresets.indexOf(presetId) !== -1;
|
||||
}
|
||||
|
||||
retainVotedPreset(presetId: number): void {
|
||||
let votedPresets = window.require("electron-settings").get("votedPresets");
|
||||
if (!votedPresets) {
|
||||
votedPresets = [];
|
||||
}
|
||||
votedPresets.push(presetId);
|
||||
window.require("electron-settings").set("votedPresets", votedPresets);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
this.updatePresetList();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const onlinePresetTitle = getTranslation("PresetsScene.onlinePresetTitle", "Online Presets");
|
||||
const localPresetTitle = getTranslation("PresetsScene.localPresetTitle", "Local Presets");
|
||||
const autoApplyTitle = getTranslation("PresetsScene.autoApplyTitle", "Auto apply preset");
|
||||
return (
|
||||
<PresetsOnlineContext.Provider value={this.state}>
|
||||
<RyzenControllerAppContext.Consumer>
|
||||
{(ryzenControllerAppContext: RyzenControllerAppContextType) => {
|
||||
if (Object.entries(ryzenControllerAppContext.presets).length <= 0) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PresetListEmpty />
|
||||
<SceneTitle title={onlinePresetTitle} />
|
||||
<PresetOnline />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const presetNames = Object.keys(ryzenControllerAppContext.presets);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SceneTitle title={localPresetTitle} />
|
||||
<ul className="uk-margin uk-list uk-list-large uk-list-striped">
|
||||
{presetNames.map(presetName => {
|
||||
const preset = ryzenControllerAppContext.presets[presetName];
|
||||
return <PresetLine key={`0_${presetName}`} presetName={presetName} preset={preset} />;
|
||||
})}
|
||||
</ul>
|
||||
<SceneTitle title={onlinePresetTitle} />
|
||||
<PresetOnline />
|
||||
<SceneTitle title={autoApplyTitle} />
|
||||
<PresetAutoApplyCards />
|
||||
</React.Fragment>
|
||||
);
|
||||
}}
|
||||
</RyzenControllerAppContext.Consumer>
|
||||
</PresetsOnlineContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PresetsScene;
|
14
src/scenes/RyzenAdjScene.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import * as React from "react";
|
||||
import RyzenAdjOptionList from "../components/RyzenAdjOptionList";
|
||||
import RyzenAdjBottomBar from "../components/RyzenAdjBottomBar";
|
||||
|
||||
function RyzenAdjScene(props: { filter: RyzenControllerTabForRyzenAdj }) {
|
||||
return (
|
||||
<div className="uk-container uk-container-expend">
|
||||
<RyzenAdjOptionList filter={props.filter} />
|
||||
<RyzenAdjBottomBar />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default RyzenAdjScene;
|
193
src/scenes/Scene.tsx
Normal file
@ -0,0 +1,193 @@
|
||||
import * as React from "react";
|
||||
import { Route, Switch, Redirect } from "react-router-dom";
|
||||
import RyzenControllerAppContext, {
|
||||
defaultRyzenControllerAppContext,
|
||||
defaultPreset,
|
||||
persistentSave,
|
||||
getSettingDefinition,
|
||||
app_version_as_string,
|
||||
executeRyzenAdjUsingPreset,
|
||||
} from "../contexts/RyzenControllerAppContext";
|
||||
import RyzenAdjScene from "../scenes/RyzenAdjScene";
|
||||
import PresetsScene from "../scenes/PresetsScene";
|
||||
import SettingsScene from "../scenes/SettingsScene";
|
||||
import NotificationContext from "../contexts/NotificationContext";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
const electronSettings = window.require("electron-settings");
|
||||
const powerMonitor = window.require("electron").remote.powerMonitor;
|
||||
|
||||
class Scene extends React.Component<{}, RyzenControllerAppContextType> {
|
||||
state: RyzenControllerAppContextType = {
|
||||
...defaultRyzenControllerAppContext,
|
||||
updateLatestSettings: this.updateLatestSettings.bind(this),
|
||||
updateCurrentSettings: this.updateCurrentSettings.bind(this),
|
||||
addPreset: this.addPreset.bind(this),
|
||||
removePreset: this.removePreset.bind(this),
|
||||
updateSettings: this.updateSettings.bind(this),
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
let settings = electronSettings.get(app_version_as_string);
|
||||
if (settings) {
|
||||
settings = settings.settings;
|
||||
} else {
|
||||
settings = defaultRyzenControllerAppContext.settings;
|
||||
}
|
||||
let newSettingsPromises: Array<Promise<string | boolean>> = [];
|
||||
|
||||
for (const key in settings) {
|
||||
if (settings.hasOwnProperty(key)) {
|
||||
// @ts-ignore
|
||||
const arg: RyzenControllerSettingsNames = key;
|
||||
const settingValue = settings[arg];
|
||||
const settingDef = getSettingDefinition(arg);
|
||||
if (settingDef && typeof settingValue !== "undefined") {
|
||||
newSettingsPromises.push(settingDef.apply(settingValue));
|
||||
}
|
||||
|
||||
if (arg === "onRCStart" && settingValue !== false && settingValue !== "") {
|
||||
executeRyzenAdjUsingPreset(settingValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newSettingsPromises.length > 0) {
|
||||
Promise.all(newSettingsPromises).catch((error: string) => {
|
||||
NotificationContext.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
powerMonitor.on("unlock-screen", () => {
|
||||
const presetName = electronSettings.get(app_version_as_string)?.settings?.onSessionResume;
|
||||
if (presetName) {
|
||||
executeRyzenAdjUsingPreset(presetName);
|
||||
}
|
||||
});
|
||||
|
||||
if (window.require("os").platform() === "win32") {
|
||||
this.handleBatteryStatusChange();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
persistentSave(this.state);
|
||||
}
|
||||
|
||||
handleBatteryStatusChange() {
|
||||
powerMonitor.on("on-ac", () => {
|
||||
const presetName = electronSettings.get(app_version_as_string)?.settings?.onLaptopPluggedIn;
|
||||
if (presetName !== false && presetName !== "") {
|
||||
executeRyzenAdjUsingPreset(presetName);
|
||||
}
|
||||
});
|
||||
|
||||
powerMonitor.on("on-battery", () => {
|
||||
const presetName = electronSettings.get(app_version_as_string)?.settings?.onLaptopPluggedOut;
|
||||
if (presetName !== false && presetName !== "") {
|
||||
executeRyzenAdjUsingPreset(presetName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateLatestSettings() {
|
||||
const newLatestSettings: RyzenAdjOptionListType = {
|
||||
...defaultPreset,
|
||||
...this.state.currentSettings,
|
||||
};
|
||||
this.setState({
|
||||
latestSettings: newLatestSettings,
|
||||
});
|
||||
}
|
||||
|
||||
updateCurrentSettings(list: PartialRyzenAdjOptionListType) {
|
||||
const newCurrentSettings: RyzenAdjOptionListType = {
|
||||
...defaultPreset,
|
||||
...this.state.currentSettings,
|
||||
...list,
|
||||
};
|
||||
this.setState({
|
||||
currentSettings: newCurrentSettings,
|
||||
});
|
||||
}
|
||||
|
||||
addPreset(name: string, preset: PartialRyzenAdjOptionListType) {
|
||||
const newPreset = {
|
||||
[name]: {
|
||||
...defaultPreset,
|
||||
...preset,
|
||||
},
|
||||
};
|
||||
|
||||
if (this.state.presets.hasOwnProperty(name)) {
|
||||
// @TODO handle preset creation when name already existing.
|
||||
}
|
||||
|
||||
this.setState({
|
||||
presets: {
|
||||
...this.state.presets,
|
||||
...newPreset,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
removePreset(name: keyof RyzenAdjOptionListNamedType) {
|
||||
let newState = { ...this.state };
|
||||
delete newState.presets[name];
|
||||
this.setState(newState);
|
||||
}
|
||||
|
||||
updateSettings(settings: Partial<RyzenControllerSettings>) {
|
||||
let newSettings = {
|
||||
...this.state.settings,
|
||||
...settings,
|
||||
};
|
||||
let newSettingsPromises: Array<Promise<string | boolean>> = [];
|
||||
|
||||
for (const key in settings) {
|
||||
if (settings.hasOwnProperty(key)) {
|
||||
// @ts-ignore
|
||||
const arg: RyzenControllerSettingsNames = key;
|
||||
const newSettingValue = settings[arg];
|
||||
const settingDef = getSettingDefinition(arg);
|
||||
if (settingDef && typeof newSettingValue !== "undefined") {
|
||||
newSettingsPromises.push(settingDef.apply(newSettingValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newSettingsPromises.length > 0) {
|
||||
Promise.all(newSettingsPromises)
|
||||
.then(results => {
|
||||
NotificationContext.success(
|
||||
getTranslation("notification.settingsSaveSuccess", "Settings has been saved successfully"),
|
||||
"settings_applied"
|
||||
);
|
||||
this.setState({ settings: newSettings });
|
||||
})
|
||||
.catch((error: string) => {
|
||||
NotificationContext.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RyzenControllerAppContext.Provider value={this.state}>
|
||||
<Switch>
|
||||
<Redirect exact from="/" to="/cpu" />
|
||||
<Route exact path="/cpu" render={() => <RyzenAdjScene filter="cpu" />} />
|
||||
<Route exact path="/gpu" render={() => <RyzenAdjScene filter="gpu" />} />
|
||||
<Route exact path="/power" render={() => <RyzenAdjScene filter="power" />} />
|
||||
<Route exact path="/presets">
|
||||
<PresetsScene />
|
||||
</Route>
|
||||
<Route exact path="/settings">
|
||||
<SettingsScene />
|
||||
</Route>
|
||||
</Switch>
|
||||
</RyzenControllerAppContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Scene;
|
37
src/scenes/SettingsScene.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import * as React from "react";
|
||||
import SysInfoCards from "../components/SysInfoCards";
|
||||
import SceneTitle from "../components/SceneTitle";
|
||||
import SettingsList from "../components/SettingsList";
|
||||
import SysInfoContext from "../contexts/SysInfoContext";
|
||||
import { getTranslation } from "../contexts/LocaleContext";
|
||||
|
||||
class SettingsScene extends React.PureComponent<{}, {}> {
|
||||
render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SceneTitle title={getTranslation("SettingsScene.settingsTitle", "Settings")} />
|
||||
<SettingsList />
|
||||
<SceneTitle
|
||||
title={getTranslation("SettingsScene.sysInfoTitle", "System Info")}
|
||||
className="uk-margin-remove-bottom"
|
||||
/>
|
||||
<SysInfoCards />
|
||||
<SysInfoContext.Consumer>
|
||||
{sysInfoContext => (
|
||||
<p
|
||||
className="uk-text-small uk-text-italic uk-margin-left uk-margin-remove-bottom"
|
||||
uk-tooltip={`pos: top-left; title: ${getTranslation(
|
||||
"SettingsScene.systemHashDesc",
|
||||
"This will be used to ensure downloaded presets compatibility."
|
||||
)}`}
|
||||
>
|
||||
System hash: {sysInfoContext.signature || getTranslation("SettingsScene.loadingSysHash", "Loading...")}
|
||||
</p>
|
||||
)}
|
||||
</SysInfoContext.Consumer>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingsScene;
|
5
src/setupTests.js
Normal file
@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import "@testing-library/jest-dom/extend-expect";
|
25
tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|