Compare commits

..
Author SHA1 Message Date
martin e7ef05d683 merge: PR #1613 custom entries (Neintonine) — homebrew-eintraege
Upstream: https://github.com/elyukai/optolith-client/pull/1613
Basis develop-V1, mergeable. Fuegt eigene Sonderregeln/Eintraege hinzu.
Ref: Alex-Wunsch 'man kann nichts selbst ergaenzen'.
2026-07-16 17:57:33 +02:00
martin 3706a4f78e build(fork): publish explizit null — kein updater-fallback auf upstream-github 2026-07-16 14:53:27 +02:00
martin f4cf03966c build(fork): update-kanal entfernt — releases nur noch privat, kein auto-update 2026-07-16 14:47:59 +02:00
martin 630ec88c7e build(fork): electron-builder-konfigurationen für fork-releases 2026-07-16 13:52:09 +02:00
martin 8484265c2a fix(fork): updater nur aktivieren wenn app-update.yml existiert 2026-07-16 13:40:37 +02:00
martin dbdb75a888 feat(fork): v1.6.0 mit Fork-Kennzeichnung (Fenstertitel + NavBar-Badge) 2026-07-16 13:18:58 +02:00
Lukas Obermann 0dc6316b5b docs(github): use task list for new book data sources 2025-09-06 17:08:53 +02:00
Lukas Obermann c7d1735e4b docs(github): change new book data template to hide instructions as comments 2025-09-06 17:03:13 +02:00
Lukas Obermann aa93dab7fd build(release): 1.5.2 2025-01-02 18:43:46 +01:00
Lukas Obermann 5a9ae86cf9 build(git): merge master 2025-01-02 18:43:14 +01:00
Michel FeddeandLukas Obermann 7f5f94491e fix: throws during startup due to changed devtools identifiers
Refs: #1634
Co-authored-by: Lukas Obermann <lukas.obermann@icloud.com>
2025-01-02 18:26:57 +01:00
Lukas Obermann a2979650f2 build(deps): fix auto-fixable vulnerabilities in packages 2025-01-02 18:17:24 +01:00
Michel Fedde 3e2f55b9a2 feat(custom-rule): add translation interfaces 2023-08-20 13:58:17 +02:00
Michel Fedde 8d02399238 feat(custom-rule): add custom rule-page to PDF 2023-08-19 23:24:34 +02:00
Michel Fedde f0d46a9d73 feat(custom-rule): fixes the bug, where all elements where selected individually 2023-08-19 19:18:49 +02:00
Michel Fedde c3f7f4faa8 feat(custom-rules): moved the inline textarea to a dialog to enter the rule 2023-08-19 19:07:48 +02:00
Michel Fedde ed97c55c4e feat: add option to add and save a custom rule to a custom activatable 2023-08-19 15:09:00 +02:00
Lukas Obermann 8e8932cd83 build(release): bump version to 1.5.1 2022-03-20 16:05:05 +01:00
Lukas Obermann c15505062d build(release): 1.5.1
Refs: #1360
2022-03-20 15:26:11 +01:00
920 changed files with 39157 additions and 68778 deletions
+6
View File
@@ -0,0 +1,6 @@
src/App/Utilities/YAML/Schema/
*.js
*.gen.tsx
node_modules
/app
/deploy
+496
View File
@@ -0,0 +1,496 @@
root: true
parser: "@typescript-eslint/parser"
plugins:
- "@typescript-eslint"
- eslint-plugin-react
- eslint-plugin-react-hooks
- eslint-plugin-tsdoc
parserOptions:
ecmaVersion: latest
sourceType: module
project:
- ./tsconfig.json
ecmaFeatures:
jsx: true
env:
commonjs: true
node: true
es6: true
browser: true
rules:
# Possible errors
for-direction: 2
getter-return: 2
no-async-promise-executor: 1
no-compare-neg-zero: 1
no-cond-assign: 2
no-constant-condition: 2
no-control-regex: 2
no-debugger: 2
no-duplicate-case: 2
no-empty-character-class: 2
no-ex-assign: 2
no-inner-declarations: 1
no-invalid-regexp: 2
no-irregular-whitespace: 2
no-misleading-character-class: 2
no-prototype-builtins: 2
no-regex-spaces: 2
no-sparse-arrays: 2
no-template-curly-in-string: 2
no-unreachable: 2
no-unsafe-finally: 2
no-unsafe-negation: 2
require-atomic-updates: 2
use-isnan: 2
# Best Practices
accessor-pairs: 2
class-methods-use-this: 1
# complexity: 2
curly:
- 2
- all
default-case: 2
dot-location:
- 2
- property
# dot-notation:
# - 2
# - allowKeywords: false
eqeqeq: 2
guard-for-in: 2
no-alert: 2
no-caller: 2
no-case-declarations: 2
no-div-regex: 2
# no-else-return:
# - 2
# - allowElseIf: true
# no-empty-function: 2
no-empty-pattern: 2
no-eq-null: 2
no-eval: 2
no-extend-native:
- 2
- exceptions:
- "Array"
- "Map"
- "Set"
no-extra-bind: 2
no-extra-label: 2
no-fallthrough: 2
no-floating-decimal: 2
no-global-assign: 2
no-implicit-coercion:
- 2
- allow:
- "!!"
no-implied-eval: 2
# no-invalid-this: 2
no-iterator: 2
no-labels: 2
no-lone-blocks: 2
no-loop-func: 2
no-multi-spaces: 2
no-multi-str: 2
no-new: 2
no-new-func: 2
no-new-wrappers: 2
no-octal: 2
no-octal-escape: 2
no-param-reassign: 2
no-proto: 2
no-redeclare: 0
no-return-assign: 2
no-script-url: 2
no-self-assign: 2
no-self-compare: 2
no-throw-literal: 2
no-unmodified-loop-condition: 2
no-unused-labels: 2
no-useless-call: 2
no-useless-catch: 2
no-useless-concat: 2
no-useless-escape: 2
no-useless-return: 2
no-void: 2
no-warning-comments: 1
no-with: 2
prefer-named-capture-group: 1
prefer-promise-reject-errors: 2
radix: 2
require-unicode-regexp: 2
wrap-iife: 2
yoda: 2
# Variables
init-declarations: 2
no-delete-var: 2
no-label-var: 2
no-shadow: 0
no-shadow-restricted-names: 2
# no-undef: 2
# Stylistic Issues
array-bracket-spacing:
- 2
- always
array-element-newline:
- 2
- consistent
block-spacing: 2
# capitalized-comments: 2
comma-dangle:
- 2
- arrays: always-multiline
objects: always-multiline
imports: always-multiline
exports: always-multiline
functions: only-multiline
comma-style: 2
computed-property-spacing: 2
eol-last: 2
func-name-matching: 2
func-names: 2
func-style:
- 2
- declaration
- allowArrowFunctions: true
function-call-argument-newline:
- 2
- consistent
function-paren-newline:
- 2
- multiline-arguments
jsx-quotes: 2
key-spacing: 2
keyword-spacing: 2
line-comment-position: 2
lines-around-comment:
- 0
# requires an empty line before block comments
- beforeBlockComment: true
# requires an empty line after block comments
afterBlockComment: false
# requires an empty line before line comments
beforeLineComment: true
# requires an empty line after line comments
afterLineComment: false
# allows comments to appear at the start of block statements
allowBlockStart: true
# allows comments to appear at the end of block statements
allowBlockEnd: false
# allows comments to appear at the start of object literals
allowObjectStart: true
# allows comments to appear at the end of object literals
allowObjectEnd: false
# allows comments to appear at the start of array literals
allowArrayStart: true
# allows comments to appear at the end of array literals
allowArrayEnd: false
# allows comments to appear at the start of classes
allowClassStart: true
# allows comments to appear at the end of classes
allowClassEnd: false
ignorePattern: "^ *@"
lines-between-class-members: 2
max-len:
- 2
- code: 100
ignoreComments: true
ignoreTemplateLiterals: true
ignorePattern: "^import |^export \\{(.*?)\\}|^ \\* |^ *it ?\\(\"\\w+"
max-statements-per-line: [2, { max: 2 }]
multiline-ternary:
- 2
- always-multiline
new-parens: 2
newline-per-chained-call:
- 2
- ignoreChainWithDepth: 2
no-bitwise: 2
no-continue: 2
no-inline-comments: 2
no-lonely-if: 2
no-mixed-operators:
- 2
- groups:
- ["&", "|", "^", "~", "<<", ">>", ">>>"]
- ["==", "!=", "===", "!==", ">", ">=", "<", "<="]
- ["&&", "||"]
- ["in", "instanceof"]
allowSamePrecedence: true
no-mixed-spaces-and-tabs: 2
no-multi-assign: 2
no-multiple-empty-lines: 2
no-negated-condition: 2
no-new-object: 2
no-tabs: 2
no-trailing-spaces: 2
no-unneeded-ternary: 2
nonblock-statement-body-position: 2
# object-curly-newline:
# - 2
# - multiline: true
object-curly-spacing:
- 2
- always
operator-assignment: 2
operator-linebreak:
- 2
- before
- overrides:
"=": ignore
padded-blocks:
- 2
- blocks: never
classes: never
switches: never
padding-line-between-statements:
- 2
- blankLine: always
prev: "*"
next: return
prefer-object-spread: 2
quote-props:
- 2
- as-needed
semi-spacing: 2
semi-style: 2
space-before-blocks: 2
space-in-parens: 2
space-infix-ops:
- 2
- int32Hint: true
space-unary-ops: 2
spaced-comment: 2
switch-colon-spacing: 2
template-tag-spacing:
- 2
- always
# ECMAScript 6
arrow-body-style: 2
arrow-parens:
- 2
- as-needed
arrow-spacing: 2
constructor-super: 2
generator-star-spacing:
- 2
- after
no-class-assign: 2
no-new-symbol: 2
no-this-before-super: 2
no-useless-computed-key: 2
no-useless-rename: 2
no-var: 2
object-shorthand: 2
prefer-arrow-callback: 2
prefer-const: 2
prefer-destructuring:
- 2
- object: true
array: false
prefer-numeric-literals: 2
prefer-rest-params: 2
prefer-spread: 2
prefer-template: 2
require-yield: 2
rest-spread-spacing: 2
# sort-imports:
# - 2
# - ignoreCase: true
# ignoreMemberSort: true
symbol-description: 2
template-curly-spacing: 2
yield-star-spacing:
- 2
- after
# TypeScript
"@typescript-eslint/adjacent-overload-signatures": 2
"@typescript-eslint/array-type": 2
"@typescript-eslint/await-thenable": 2
# "@typescript-eslint/class-name-casing": 2
"@typescript-eslint/consistent-type-assertions": 2
"@typescript-eslint/member-delimiter-style":
- 2
- multiline:
delimiter: none
singleline:
delimiter: semi
requireLast: false
"@typescript-eslint/naming-convention":
- error
- selector: interface
format:
- PascalCase
custom:
regex: "^I[A-Z]"
match: false
"@typescript-eslint/no-floating-promises": 2
"@typescript-eslint/no-for-in-array": 2
"@typescript-eslint/no-inferrable-types": 2
"@typescript-eslint/no-misused-new": 2
"@typescript-eslint/no-misused-promises": 2
"@typescript-eslint/no-this-alias": 2
"@typescript-eslint/no-unnecessary-type-arguments": 2
"@typescript-eslint/no-unnecessary-type-assertion": 2
"@typescript-eslint/prefer-for-of": 2
"@typescript-eslint/prefer-includes": 2
"@typescript-eslint/prefer-readonly": 2
"@typescript-eslint/promise-function-async": 2
# "@typescript-eslint/quotes": 2
"@typescript-eslint/require-array-sort-compare": 2
"@typescript-eslint/require-await": 2
"@typescript-eslint/restrict-plus-operands": 2
# "@typescript-eslint/semi":
# - 2
# - never
"@typescript-eslint/strict-boolean-expressions": 2
"@typescript-eslint/triple-slash-reference": 2
"@typescript-eslint/type-annotation-spacing": 2
# "@typescript-eslint/unbound-method": 2
"@typescript-eslint/unified-signatures": 2
# TypeScript Extension
brace-style: 0
"@typescript-eslint/brace-style":
- 2
- stroustrup
comma-spacing: 0
"@typescript-eslint/comma-spacing": 2
func-call-spacing: 0
"@typescript-eslint/func-call-spacing":
- 2
- always
- allowNewlines: true
indent: 0
"@typescript-eslint/indent": 0
no-array-constructor: 0
"@typescript-eslint/no-array-constructor": 2
no-extra-parens: 0
"@typescript-eslint/no-extra-parens": 0
no-extra-semi: 0
"@typescript-eslint/no-extra-semi": 2
no-unused-expressions: 0
"@typescript-eslint/no-redeclare": 2
"@typescript-eslint/no-shadow": 2
"@typescript-eslint/no-unused-expressions": 2
"@typescript-eslint/no-unused-vars":
- 2
- argsIgnorePattern: "^_"
varsIgnorePattern: "^_"
"@typescript-eslint/no-use-before-define": 2
no-useless-constructor: 0
"@typescript-eslint/no-useless-constructor": 2
quotes: 0
"@typescript-eslint/quotes":
- 2
- double
- avoidEscape: true
allowTemplateLiterals: true
no-return-await: 0
"@typescript-eslint/return-await": 2
semi: 0
"@typescript-eslint/semi":
- 2
- never
- beforeStatementContinuationChars: never
space-before-function-paren: 0
"@typescript-eslint/space-before-function-paren": 2
# React
react/boolean-prop-naming: 2
react/destructuring-assignment: 2
react/no-access-state-in-setstate: 2
react/no-array-index-key: 2
react/no-children-prop: 2
react/no-danger: 2
react/no-danger-with-children: 2
react/no-deprecated: 2
react/no-did-mount-set-state: 2
react/no-did-update-set-state: 1
react/no-direct-mutation-state: 2
react/no-find-dom-node: 2
react/no-is-mounted: 2
# react/no-multi-comp: 2
react/no-redundant-should-component-update: 2
react/no-render-return-value: 2
react/no-typos: 2
react/no-string-refs: 2
react/no-this-in-sfc: 2
react/no-unescaped-entities: 2
react/no-unsafe: 2
react/no-unused-prop-types: 2
react/no-unused-state: 2
react/no-will-update-set-state: 2
react/prefer-es6-class: 2
react/prefer-stateless-function: 2
react/react-in-jsx-scope: 2
react/require-render-return: 2
react/self-closing-comp: 2
react/state-in-constructor:
- 2
- never
react/style-prop-object: 2
react/void-dom-elements-no-children: 2
react/jsx-boolean-value:
- 2
- never
react/jsx-child-element-spacing: 2
react/jsx-closing-bracket-location:
- 2
- props-aligned
react/jsx-closing-tag-location: 2
react/jsx-curly-newline: 2
react/jsx-curly-spacing: 2
react/jsx-equals-spacing: 2
react/jsx-first-prop-new-line: 2
react/jsx-handler-names: 2
# react/jsx-indent:
# - 2
# - 2
react/jsx-indent-props:
- 2
- 2
react/jsx-max-props-per-line:
- 2
- maximum: 3
react/jsx-no-bind: 2
react/jsx-no-comment-textnodes: 2
react/jsx-no-literals: 2
react/jsx-no-target-blank: 2
react/jsx-one-expression-per-line:
- 2
- allow: single-child
react/jsx-curly-brace-presence:
- 2
- props: never
children: ignore
react/jsx-fragments: 2
react/jsx-pascal-case: 2
react/jsx-props-no-multi-spaces: 2
react/jsx-props-no-spreading: 2
react/jsx-tag-spacing: 2
react/jsx-wrap-multilines:
- 2
- declaration: parens-new-line
assignment: parens-new-line
return: parens-new-line
arrow: parens-new-line
condition: parens-new-line
logical: parens-new-line
prop: ignore
# React Hooks
react-hooks/rules-of-hooks: 2
react-hooks/exhaustive-deps: 2
# TSDoc
tsdoc/syntax: warn
settings:
react:
version: detect
+20 -10
View File
@@ -9,19 +9,27 @@ assignees: ''
## Sources
> List here what is included in this part of the book to implement. E.g., for special abilities, you need to list every source of entries from the book. Example (in German):
>
> - S. 133150 (Berufsgeheimnisse)
<!-- List here what is included in this part of the book to implement. E.g., for special abilities, you need to list every source of entries from the book. Use page ranges if entries span multiple pages. Example:
- S. X (Section/Chapter Name)
- [ ] S. 133 (Entry A)
- [ ] S. 133134 (Entry B)
Alternatively, provide an overview of the different sections in the book where one or more relevant entries occur. Example (in German):
- [ ] S. 133150 (Berufsgeheimnisse)
-->
- [ ] S. X (Entry/Section/Chapter Name)
## Special Notes
> List here any special hints to implement the sources listed above.
>
> - …
<!-- List here any special hints to implement the sources listed above. Example:
- All entries are already present, only add source references
- Pay attention to the layout of entry A, there is a gap that should not be in Optolith
Replace the dash below this comment if there are any notes. -->
@@ -29,8 +37,10 @@ assignees: ''
## Special Implementations
> List here what cannot be solved only using the database. Example (in German):
>
> - [ ] Voraussetzungen Vorliebe I-II
<!-- List issues that track what cannot be solved only using the database. Example:
- #123
Replace the dash below this comment if there are any notes. -->
+8 -9
View File
@@ -27,20 +27,19 @@ jobs:
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: Use Node.js 20
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 20
node-version: 18.x
- name: Install Node.js dependencies
run: npm ci
- name: Compile JavaScript files
run: npm run js:build
- name: Build database cache
run: npm run buildCache
working-directory: ./src/database/contents
- name: Compile Sass files
run: npm run css:build
- name: Build macOS application
if: runner.os == 'macOS'
run: node ./deploy/build.js --prerelease
run: node ./deploy/build.mjs prerelease
env:
APPLEID: ${{ secrets.APPLEID }}
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
@@ -50,16 +49,16 @@ jobs:
UPDATE_URL: ${{ secrets.UPDATE_URL }}
- name: Build Linux application
if: runner.os == 'Linux'
run: node ./deploy/build.js --prerelease
run: node ./deploy/build.mjs prerelease
env:
UPDATE_URL: ${{ secrets.UPDATE_URL }}
- name: Build Windows application
if: runner.os == 'Windows'
run: node ./deploy/build.js --prerelease
run: node ./deploy/build.mjs prerelease
env:
UPDATE_URL: ${{ secrets.UPDATE_URL }}
- name: Deploy
run: node ./deploy/deploy.js --prerelease
run: node ./deploy/deploy.mjs prerelease
env:
HOST: ${{ secrets.HOST }}
USERNAME: ${{ secrets.USERNAME }}
+8 -9
View File
@@ -27,20 +27,19 @@ jobs:
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: Use Node.js 20
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 20
node-version: 18.x
- name: Install Node.js dependencies
run: npm ci
- name: Compile JavaScript files
run: npm run js:build
- name: Build database cache
run: npm run buildCache
working-directory: ./src/database/contents
- name: Compile Sass files
run: npm run css:build
- name: Build macOS application
if: runner.os == 'macOS'
run: node ./deploy/build.js --stable
run: node ./deploy/build.mjs stable
env:
APPLEID: ${{ secrets.APPLEID }}
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
@@ -50,16 +49,16 @@ jobs:
UPDATE_URL: ${{ secrets.UPDATE_URL }}
- name: Build Linux application
if: runner.os == 'Linux'
run: node ./deploy/build.js --stable
run: node ./deploy/build.mjs stable
env:
UPDATE_URL: ${{ secrets.UPDATE_URL }}
- name: Build Windows application
if: runner.os == 'Windows'
run: node ./deploy/build.js --stable
run: node ./deploy/build.mjs stable
env:
UPDATE_URL: ${{ secrets.UPDATE_URL }}
- name: Deploy
run: node ./deploy/deploy.js --stable
run: node ./deploy/deploy.mjs stable
env:
HOST: ${{ secrets.HOST }}
USERNAME: ${{ secrets.USERNAME }}
@@ -1,42 +1,48 @@
name: CI
name: Integration
on:
push:
branches:
- master
- develop
- develop-v2
pull_request:
branches:
- master
- develop
- develop-v2
jobs:
tests:
name: Tests
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/checkout@v2
with:
node-version: 20
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 18.x
- name: Install Node.js dependencies
run: npm ci
- name: Compile TypeScript files
run: npm run ts:build
- name: Run tests
run: npm test
linting:
name: Linting
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/checkout@v2
with:
node-version: 20
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 18.x
- name: Install Node.js dependencies
run: npm ci
- name: Run linter
-2
View File
@@ -1,6 +1,4 @@
.DS_Store
node_modules/
/.webpack/**/*
npm-debug.log*
.vs/
/data/
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "app/Database"]
path = src/database/contents
path = app/Database
url = https://github.com/elyukai/optolith-data.git
-5
View File
@@ -1,5 +0,0 @@
arrowParens: avoid
printWidth: 100
semi: false
tabWidth: 2
trailingComma: all
+2 -9
View File
@@ -15,14 +15,7 @@
"typescriptreact"
],
"eslint.lintTask.enable": true,
"eslint.lintTask.options": "\"src/**\"",
"eslint.experimental.useFlatConfig": true,
"eslint.lintTask.options": "\"src/**\" -c \"./.eslintrc.yaml\" --ext=\"ts\" --ext=\"tsx\" --ignore-pattern=\"./src/App/Utilities/YAML/Schema/**\"",
"editor.tabSize": 2,
"editor.detectIndentation": false,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.detectIndentation": false
}
+11 -10
View File
@@ -12,7 +12,7 @@
"command": "npm start"
},
{
"label": "test",
"label": "jest",
"command": "npm test",
"group": {
"kind": "test",
@@ -42,9 +42,18 @@
"group": "watch"
}
},
{
"label": "sass watch",
"command": "npm run css:watch",
"isBackground": true,
"group": "build",
"presentation": {
"group": "watch"
}
},
{
"label": "watch",
"dependsOn": ["tsc watch", "webpack watch"],
"dependsOn": ["tsc watch", "webpack watch", "sass watch"],
"group": {
"kind": "build",
"isDefault": true
@@ -55,14 +64,6 @@
"command": "npm run release:pre",
"problemMatcher": []
},
{
"label": "build database cache",
"command": "npm run buildCache",
"options": {
"cwd": "${workspaceFolder}/src/database/contents"
},
"problemMatcher": []
},
{
"label": "create release",
"command": "npm run release:main",
+1
View File
@@ -1 +1,2 @@
Lukas Obermann <lukas.obermann@icloud.com>
Philipp Borucki <p.borucki@icloud.com>
+7
View File
@@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [1.5.2](https://github.com/elyukai/optolith-client/compare/v1.5.2-alpha.4...v1.5.2) (2025-01-02)
### Bug Fixes
* throws during startup due to changed devtools identifiers ([7f5f944](https://github.com/elyukai/optolith-client/commit/7f5f94491e1432d66add1315889075fb5369f700)), closes [#1634](https://github.com/elyukai/optolith-client/issues/1634)
### [1.5.2-alpha.4](https://github.com/elyukai/optolith-client/compare/v1.5.2-alpha.3...v1.5.2-alpha.4) (2022-07-22)
+23 -11
View File
@@ -54,7 +54,7 @@ Clone the repo, including the private submodule.
git clone --recurse-submodules https://github.com/elyukai/optolith-client.git
```
Make sure Node.js (lastest version) is installed, nagivate into the created project folder and install all dependencies using the following command:
Make sure Node.js (lastest version) is installed and run
```sh
npm i
@@ -64,17 +64,27 @@ This installs all necessary packages.
### First run
Compile the source code. `js:` is for TypeScript/Webpack. Webpack does not check the TypeScript validity, to check this, use `ts:` commands.
Compile the source code. `js:` is for TypeScript/Webpack, `re:` for Reason/OCaml and `css:` for CSS/Sass. Webpack does not check the TypeScript validity, to check this, use `ts:` commands.
Build the source by running
You have to run the Reason build first as it generates TypeScript files...
```sh
npm run ts:build
npm run css:build
npm run js:build
```
(There's also a shorthand for that:)
```sh
npm run build
```
&hellip;or watch it for better performance for subsequent compiling on save (you need to use separate terminal instances then).
```sh
npm run re:watch
npm run css:watch
npm run js:watch
```
@@ -91,20 +101,22 @@ Run the app:
npm start
```
The [debug](https://www.npmjs.com/package/debug) package is used for some general logging. If you want to see its output, you need to set the `DEBUG` environment variable.
### Stylesheets
To compile the SCSS files, use
```sh
DEBUG=* npm start
```
In PowerShell you need to write this instead:
```sh
$env:DEBUG='*'; npm start
npm run css:build
```
### Miscellaneous
Clean the files built by the Reason compiler, if there are any issue with it.
```sh
npm run re:clean
```
Lint all project TS files using ESLint.
```sh
+1 -10
View File
@@ -2,21 +2,12 @@
Welcome to the repository for Optolith, a desktop application for The Dark Eye 5th Edition. Nice to see you here! 👋🏻
> [!CAUTION]
> This is the branch of the upcoming V2 version. This is **not production-ready**. If you need access to the V1 line of releases, check out the `develop-V1` branch.
>
> Note that the self-building constraints below also apply to the V1.
## Install
You can download the app [here (German page)](http://www.ulisses-ebooks.de/product/209711) or here [here (English page)](http://www.drivethrurpg.com/product/220253) (same app on both pages). You'll get an installer for Windows with Auto Update support and a DMG for Mac. For Linux, you get an AppImage, which _should_ support Auto Update but it doesnt seem to work currently. You also get a tar.gz if you don't like AppImage.
You can download the app [here (German page)](http://www.ulisses-ebooks.de/product/209711) or here [here (English page)](http://www.drivethrurpg.com/product/220253) (same app on both pages). You'll get an installer for Windows with Auto Update support and a DMG for Mac. For Linux, you get an AppImage, which *should* support Auto Update but it doesnt seem to work currently. You also get a tar.gz if you don't like AppImage.
It is available for Windows 7+ as well as for Linux (Ubuntu 12.04+, Fedora 21, Debian 8) and macOS 10.9+ x64.
### Self-Building and Package Registries
Please note that self-building is only possible in a restricted way, as the underlying database is private due to licensing reasons. You can follow the in the [Contributing Guide](CONTRIBUTING.md) to obtain a version of the database from an already existing release. Please note that due to these licensing restrictions distribution via package managers like `brew` or `apt`
## Goal
Optolith was built and is developed based on a few key criteria:
+35
View File
@@ -0,0 +1,35 @@
export interface RawConfig {
herolistSortOrder: "name" | "datemodified" | "dateModified"
herolistVisibilityFilter: "all" | "own" | "shared"
racesSortOrder: "name" | "cost"
racesValueVisibility?: boolean
culturesSortOrder: "name" | "cost"
culturesVisibilityFilter: "all" | "common"
culturesValueVisibility?: boolean
professionsSortOrder: "name" | "cost"
professionsVisibilityFilter: "all" | "common"
professionsGroupVisibilityFilter: 0 | 1 | 2 | 3
professionsFromExpansionsVisibility?: boolean
advantagesDisadvantagesCultureRatingVisibility: boolean
talentsSortOrder: "name" | "group" | "ic"
talentsCultureRatingVisibility: boolean
combatTechniquesSortOrder: "name" | "group" | "ic"
specialAbilitiesSortOrder: "name" | "group" | "groupname"
spellsSortOrder: "name" | "group" | "property" | "ic"
spellsUnfamiliarVisibility: boolean
liturgiesSortOrder: "name" | "group" | "ic"
equipmentSortOrder: "name" | "groupname" | "where" | "weight"
equipmentGroupVisibilityFilter: number
sheetCheckAttributeValueVisibility?: boolean
sheetUseParchment?: boolean
sheetShowRules?: boolean
sheetZoomFactor?: number
enableActiveItemHints: boolean
locale?: string
fallbackLocale?: string
theme?: "light" | "dark"
enableEditingHeroAfterCreationPhase?: boolean
meleeItemTemplatesCombatTechniqueFilter?: string
rangedItemTemplatesCombatTechniqueFilter?: string
enableAnimations?: boolean
}
@@ -1,90 +1,74 @@
{
"$schema": "https://json-schema.org/draft-07/schema",
"$id": "/config.schema.json",
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "Schema/Config/Config.l10n.schema.json",
"$ref": "#/definitions/Config",
"definitions": {
"Config": {
"description": "The user-configurable app-wide settings.",
"title": "RawConfig",
"type": "object",
"properties": {
"locale": {
"type": "string"
},
"fallbackLocale": {
"type": "string"
},
"herolistSortOrder": {
"$ref": "#/definitions/CharactersSortOrder"
"enum": ["name", "datemodified", "dateModified"]
},
"herolistVisibilityFilter": {
"oneOf": [
{
"const": "all"
},
{
"const": "own"
},
{
"const": "shared"
}
]
"enum": ["all", "own", "shared"]
},
"racesSortOrder": {
"$ref": "#/definitions/RacesSortOrder"
"enum": ["name", "cost"]
},
"racesValueVisibility": {
"type": "boolean"
},
"culturesSortOrder": {
"$ref": "#/definitions/CulturesSortOrder"
"enum": ["name", "cost"]
},
"culturesVisibilityFilter": {
"$ref": "#/definitions/CulturesVisibilityFilter"
"enum": ["all", "common"]
},
"culturesValueVisibility": {
"type": "boolean"
},
"professionsSortOrder": {
"$ref": "#/definitions/ProfessionsSortOrder"
"enum": ["name", "cost"]
},
"professionsVisibilityFilter": {
"$ref": "#/definitions/ProfessionsVisibilityFilter"
},
"professionsGroupVisibilityFilter": {
"$ref": "#/definitions/ProfessionsGroupVisibilityFilter"
"enum": ["all", "common"]
},
"professionsFromExpansionsVisibility": {
"type": "boolean"
},
"professionsGroupVisibilityFilter": {
"enum": [0, 1, 2, 3]
},
"advantagesDisadvantagesCultureRatingVisibility": {
"type": "boolean"
},
"talentsSortOrder": {
"$ref": "#/definitions/SkillsSortOrder"
"enum": ["name", "group", "ic"]
},
"talentsCultureRatingVisibility": {
"type": "boolean"
},
"combatTechniquesSortOrder": {
"$ref": "#/definitions/CombatTechniquesSortOrder"
"enum": ["name", "group", "ic"]
},
"specialAbilitiesSortOrder": {
"$ref": "#/definitions/SpecialAbilitiesSortOrder"
"enum": ["name", "group", "groupname"]
},
"spellsSortOrder": {
"$ref": "#/definitions/SpellsSortOrder"
"enum": ["name", "group", "property", "ic"]
},
"spellsUnfamiliarVisibility": {
"type": "boolean"
},
"liturgiesSortOrder": {
"$ref": "#/definitions/LiturgiesSortOrder"
"enum": ["name", "group", "ic"]
},
"equipmentSortOrder": {
"$ref": "#/definitions/EquipmentSortOrder"
"enum": ["name", "groupname", "where", "weight"]
},
"equipmentGroupVisibilityFilter": {
"type": "number"
"type": "integer"
},
"sheetCheckAttributeValueVisibility": {
"type": "boolean"
@@ -92,14 +76,23 @@
"sheetUseParchment": {
"type": "boolean"
},
"sheetShowRules": {
"type": "boolean"
},
"sheetZoomFactor": {
"type": "number"
},
"enableActiveItemHints": {
"type": "boolean"
},
"locale": {
"type": "string"
},
"fallbackLocale": {
"type": "string"
},
"theme": {
"$ref": "#/definitions/Theme"
"enum": ["light", "dark"]
},
"enableEditingHeroAfterCreationPhase": {
"type": "boolean"
@@ -136,101 +129,6 @@
"enableActiveItemHints"
],
"additionalProperties": false
},
"CharactersSortOrder": {
"enum": [
"name",
"datemodified",
"datecreated"
]
},
"RacesSortOrder": {
"enum": [
"name",
"cost"
]
},
"CulturesSortOrder": {
"enum": [
"name",
"cost"
]
},
"CulturesVisibilityFilter": {
"enum": [
"all",
"common"
]
},
"ProfessionsSortOrder": {
"enum": [
"name",
"cost"
]
},
"ProfessionsVisibilityFilter": {
"enum": [
"all",
"common"
]
},
"ProfessionsGroupVisibilityFilter": {
"enum": [
0,
1,
2,
3
]
},
"SkillsSortOrder": {
"enum": [
"name",
"group",
"ic"
]
},
"CombatTechniquesSortOrder": {
"enum": [
"name",
"group",
"ic"
]
},
"SpecialAbilitiesSortOrder": {
"enum": [
"name",
"group",
"groupname"
]
},
"SpellsSortOrder": {
"enum": [
"name",
"group",
"property",
"ic"
]
},
"LiturgiesSortOrder": {
"enum": [
"name",
"group",
"ic"
]
},
"EquipmentSortOrder": {
"enum": [
"name",
"groupname",
"where",
"weight"
]
},
"Theme": {
"enum": [
"light",
"dark"
]
}
}
}

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 275 KiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Before

Width:  |  Height:  |  Size: 265 KiB

After

Width:  |  Height:  |  Size: 265 KiB

Before

Width:  |  Height:  |  Size: 818 KiB

After

Width:  |  Height:  |  Size: 818 KiB

Before

Width:  |  Height:  |  Size: 689 B

After

Width:  |  Height:  |  Size: 689 B

Before

Width:  |  Height:  |  Size: 640 B

After

Width:  |  Height:  |  Size: 640 B

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Before

Width:  |  Height:  |  Size: 519 KiB

After

Width:  |  Height:  |  Size: 519 KiB

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 415 KiB

After

Width:  |  Height:  |  Size: 415 KiB

Before

Width:  |  Height:  |  Size: 2.3 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

+51
View File
@@ -0,0 +1,51 @@
{
"name": "optolith-client",
"version": "0.1.0",
"sources": [
{ "dir": "deploy" },
{ "dir": "src" },
{ "dir": "src/App/Actions" },
{ "dir": "src/App/Constants" },
{ "dir": "src/App/Containers" },
{ "dir": "src/App/Models" },
{ "dir": "src/App/Reducers" },
{ "dir": "src/App/Selectors" },
{ "dir": "src/App/Utilities" },
{ "dir": "src/App/Utilities/__tests__", "type": "dev" },
{ "dir": "src/App/Utilities/YAML" },
{ "dir": "src/App/Views" },
{ "dir": "src/App/Views/Universal" },
{ "dir": "src/Control" },
{ "dir": "src/Data" },
{ "dir": "src/Data/__tests__", "type": "dev" },
{ "dir": "src/Debug" },
{ "dir": "src/System" },
{ "dir": "src/shims" }
],
"package-specs": {
"module": "commonjs",
"in-source": true
},
"suffix": ".bs.js",
"bs-dependencies": [
"@glennsl/bs-json",
"@glennsl/bs-jest",
"reason-react"
],
"reason": {
"react-jsx": 3
},
"warnings": {
"error" : "+101"
},
"namespace": true,
"refmt": 3,
"gentypeconfig": {
"module": "commonjs",
"language": "typescript",
"shims": {
"Js": "Js",
"ReasonPervasives": "ReasonPervasives"
}
}
}
-124
View File
@@ -1,124 +0,0 @@
// @ts-check
import builder from "electron-builder"
import { notarize } from "./notarize.js"
const args = process.argv.slice(2)
// Detect channel
if (!args.includes("--stable") && !args.includes("--prerelease")) {
throw new TypeError(`Missing channel argument (either "--stable" or "--prerelease")`)
}
const isPrerelease = args.includes("--prerelease")
console.log(`Detected channel: ${isPrerelease ? "prerelease" : "stable"}`)
// Detect OS
let osKey
let osName
if (process.argv.includes("--linux") || process.platform === "linux") {
osKey = /** @type {const} */ ("LINUX")
osName = "Linux"
} else if (process.argv.includes("--mac") || process.platform === "darwin") {
osKey = /** @type {const} */ ("MAC")
osName = "macOS"
} else if (process.argv.includes("--win") || process.platform === "win32") {
osKey = /** @type {const} */ ("WINDOWS")
osName = "Windows"
} else {
throw new TypeError(`The target operating system cannot be inferred from the environment.`)
}
console.log(`Detected operating system: ${osName}`)
// Prepare electron-builder configuration
/**
* @type {import("electron-builder").Configuration}
*/
const config = {
appId: isPrerelease ? "com.lukasobermann.optolithinsider" : "com.lukasobermann.optolith",
productName: isPrerelease ? "Optolith Insider" : "Optolith",
copyright:
"© 2017present Lukas Obermann. This product was created under a license. Das Schwarze Auge and its logo as well as Aventuria, Dere, Myranor, Riesland, Tharun and Uthuria and their logos are trademarks of Significant GbR. The title and contents of this book are protected under the copyright laws of the United States of America. No part of this publication may be reproduced, stored in retrieval systems or transmitted, in any form or by any means, whether electronic, mechanical, photocopy, recording, or otherwise, without prior written consent by Ulisses Spiele GmbH, Waldems. This publication includes material that is protected under copyright laws by Ulisses Spiele and/or other authors. Such material is used under the Community Content Agreement for the SCRIPTORIUM AVENTURIS. All other original materials in this work is copyright 2017-present by Lukas Obermann and published under the Community Content Agreement for the SCRIPTORIUM AVENTURIS.",
files: [
".webpack/**",
"LICENSE",
"node_modules/optolith-database-schema/schema/**",
"src/database/contents/cache/**",
"src/database/contents/Compatibility/**",
"src/database/contents/Data/**",
],
asar: false, // otherwise optolith-database-schema package is not properly resolved
asarUnpack: [
"node_modules/optolith-character-schema/**",
"node_modules/optolith-database-schema/**",
"src/database/contents/**",
],
directories: {
output: isPrerelease ? "dist/insider" : "dist",
},
win: {
target: [
{
target: "nsis",
arch: ["x64", "ia32"],
},
],
icon: isPrerelease ? "src/assets/icon/AppList.targetsize-512.pre.png" : "src/assets/icon/icon.ico",
artifactName: isPrerelease ? "OptolithInsiderSetup_${version}.${ext}" : "OptolithSetup_${version}.${ext}",
},
nsis: {
oneClick: false,
perMachine: false,
allowToChangeInstallationDirectory: true,
differentialPackage: true,
},
linux: {
category: "RolePlaying",
target: [
{
target: "AppImage",
arch: ["x64"],
},
{
target: "tar.gz",
arch: ["x64"],
},
],
executableName: isPrerelease ? "OptolithInsider" : "Optolith",
icon: isPrerelease ? "src/assets/icon/icon.pre.png" : "src/assets/icon/AppList.targetsize-512.png",
artifactName: isPrerelease ? "OptolithInsider_${version}.${ext}" : "Optolith_${version}.${ext}",
},
mac: {
category: "public.app-category.role-playing-games",
type: "distribution",
target: [
{
target: "default",
arch: "universal",
},
],
icon: isPrerelease ? "src/assets/icon/AppIcon.pre.icns" : "src/assets/icon/AppIcon.icns",
artifactName: isPrerelease ? "OptolithInsider_${version}.${ext}" : "Optolith_${version}.${ext}",
mergeASARs: false,
darkModeSupport: true,
gatekeeperAssess: true,
},
publish: {
provider: "generic",
url: isPrerelease ? `${process.env.UPDATE_URL}/insider/\${os}` : `${process.env.UPDATE_URL}/\${os}`,
channel: "latest",
},
afterSign: osKey === "MAC" ? notarize : undefined,
}
await builder.build({
config,
targets: builder.Platform[osKey].createTarget(),
})
console.log(`Build finished successfully.`)
-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.debugger</key>
<true/>
</dict>
</plist>
-58
View File
@@ -1,58 +0,0 @@
// @ts-check
import { notarize as electronNotarize } from "@electron/notarize"
/**
* Notarizes the app package after it has been built. It detects the
* authentication method from the environment variables.
* @param context {import("electron-builder").AfterPackContext}
*/
export const notarize = async context => {
const { electronPlatformName, appOutDir } = context
if (electronPlatformName === "darwin") {
const appName = context.packager.appInfo.productFilename
if (
typeof process.env.APPLEID === "string" &&
typeof process.env.APPLEIDPASS === "string" &&
typeof process.env.TEAMID === "string"
) {
console.log(`Notarizing "${appName}.app" via Apple ID ...`)
await electronNotarize({
tool: "notarytool",
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLEID,
appleIdPassword: process.env.APPLEIDPASS,
teamId: process.env.TEAMID,
})
console.log(`Notarization successful`)
} else if (
typeof process.env.APPLEAPIKEY === "string" &&
typeof process.env.APPLEAPIKEYID === "string" &&
typeof process.env.APPLEAPIISSUER === "string"
) {
console.log(`Notarizing "${appName}.app" via App Store Connect API ...`)
await electronNotarize({
tool: "notarytool",
appPath: `${appOutDir}/${appName}.app`,
appleApiKey: process.env.APPLEAPIKEY,
appleApiKeyId: process.env.APPLEAPIKEYID,
appleApiIssuer: process.env.APPLEAPIISSUER,
})
console.log(`Notarization successful`)
} else if (typeof process.env.KEYCHAINPROFILE === "string") {
console.log(`Notarizing "${appName}.app" via Keychain ...`)
await electronNotarize({
tool: "notarytool",
appPath: `${appOutDir}/${appName}.app`,
keychain: process.env.KEYCHAIN,
keychainProfile: process.env.KEYCHAINPROFILE,
})
console.log(`Notarization successful`)
} else {
throw new Error(
`Notarization failed: No valid authentication method found in environment. Please provide either APPLEID and APPLEIDPASS, or APPLEAPIKEY, APPLEAPIKEYID and APPLEAPIISSUER, or (optional) KEYCHAIN and KEYCHAINPROFILE.`,
)
}
}
}
+52
View File
@@ -0,0 +1,52 @@
// @ts-check
/**
* @param {"prerelease" | "stable"} channel
*/
const channelSuffix = channel => {
switch (channel) {
case "prerelease": return "Insider"
case "stable": return ""
}
}
/**
* @param {import("./platform.mjs").System} os
* @param {"prerelease" | "stable"} channel
*/
const prefix = (os, channel) => {
switch (os) {
case "win": return `Optolith${channelSuffix(channel)}Setup`
case "linux":
case "mac": return `Optolith${channelSuffix(channel)}`
}
}
/**
* @param {import("./platform.mjs").System} os
*/
const extensions = os => {
switch (os) {
case "win": return [".exe", ".exe.blockmap"]
case "linux": return [".AppImage", ".tar.gz"]
case "mac": return [".dmg", ".dmg.blockmap", ".zip", ".zip.blockmap"]
}
}
/**
* @param {import("./platform.mjs").System} os
* @param {"prerelease" | "stable"} channel
* @param {string} version
*/
export const getApplicationFileNames = (os, channel, version) =>
extensions(os).map(ext => `${prefix(os, channel)}_${version}${ext}`)
/**
* @param {"win" | "mac" | "linux"} os
*/
export const getUpdateFileName = os => {
switch (os) {
case "win": return "latest.yml"
case "linux": return "latest-linux.yml"
default: return "latest-mac.yml"
}
}
+83
View File
@@ -0,0 +1,83 @@
// @ts-check
import { notarize } from "./notarize.mjs"
/**
* @type {import("electron-builder").Configuration}
*/
const config = {
appId: "com.lukasobermann.optolith",
productName: "Optolith",
copyright: "© 2017present Lukas Obermann. This product was created under a license. Das Schwarze Auge and its logo as well as Aventuria, Dere, Myranor, Riesland, Tharun and Uthuria and their logos are trademarks of Significant GbR. The title and contents of this book are protected under the copyright laws of the United States of America. No part of this publication may be reproduced, stored in retrieval systems or transmitted, in any form or by any means, whether electronic, mechanical, photocopy, recording, or otherwise, without prior written consent by Ulisses Spiele GmbH, Waldems. This publication includes material that is protected under copyright laws by Ulisses Spiele and/or other authors. Such material is used under the Community Content Agreement for the SCRIPTORIUM AVENTURIS. All other original materials in this work is copyright 2017-present by Lukas Obermann and published under the Community Content Agreement for the SCRIPTORIUM AVENTURIS.",
directories: {
output: "dist"
},
files: [
"app/**/*",
"LICENSE"
],
win: {
target: [
{
target: "nsis",
arch: [
"x64",
"ia32"
]
}
],
icon: "app/icon.ico",
artifactName: "OptolithSetup_${version}.${ext}"
},
nsis: {
perMachine: true,
differentialPackage: true,
deleteAppDataOnUninstall: false,
},
linux: {
category: "RolePlaying",
executableName: "Optolith",
icon: "app",
target: [
{
target: "AppImage",
arch: [
"x64"
]
},
{
target: "tar.gz",
arch: [
"x64"
]
}
],
artifactName: "Optolith_${version}.${ext}"
},
mac: {
category: "public.app-category.role-playing-games",
type: "distribution",
icon: "app/icon.icns",
target: [
{
target: "default",
arch: "universal"
}
],
artifactName: "Optolith_${version}.${ext}",
entitlements: "deploy/entitlements.mac.plist",
entitlementsInherit: "deploy/entitlements.mac.plist",
hardenedRuntime: true,
gatekeeperAssess: false,
},
dmg: {
sign: false
},
publish: {
"provider": "generic",
"url": `${process.env.UPDATE_URL}/\${os}`,
"channel": "latest"
},
afterSign: notarize,
}
export default config
+27
View File
@@ -0,0 +1,27 @@
// @ts-check
import builder from "electron-builder"
import { getSystem } from "./platform.mjs"
const [channel] = process.argv.slice(2)
if (channel !== "stable" && channel !== "prerelease") {
throw new TypeError (`publishToServer requires a specified channel ("stable" or "prerelease"), but it received ${channel}`)
}
const config =
channel === "prerelease"
? (await import("./build.prerelease.config.mjs")).default
: (await import("./build.config.mjs")).default
process.on ('unhandledRejection', error => {
throw new Error(`Unhandled promise rejection: ${/** @type {Error} */ (error).toString ()}`)
})
const os = getSystem()
const plaformKey = os === "linux" ? "LINUX" : os === "mac" ? "MAC" : "WINDOWS"
await builder.build({
config,
targets: builder.Platform[plaformKey].createTarget(),
})
+84
View File
@@ -0,0 +1,84 @@
// @ts-check
import { notarize } from "./notarize.mjs"
/**
* @type {import ("electron-builder") .Configuration}
*/
const config = {
appId: "com.lukasobermann.optolithinsider",
productName: "Optolith Insider",
copyright: "© 2017present Lukas Obermann. This product was created under a license. Das Schwarze Auge and its logo as well as Aventuria, Dere, Myranor, Riesland, Tharun and Uthuria and their logos are trademarks of Significant GbR. The title and contents of this book are protected under the copyright laws of the United States of America. No part of this publication may be reproduced, stored in retrieval systems or transmitted, in any form or by any means, whether electronic, mechanical, photocopy, recording, or otherwise, without prior written consent by Ulisses Spiele GmbH, Waldems. This publication includes material that is protected under copyright laws by Ulisses Spiele and/or other authors. Such material is used under the Community Content Agreement for the SCRIPTORIUM AVENTURIS. All other original materials in this work is copyright 2017-present by Lukas Obermann and published under the Community Content Agreement for the SCRIPTORIUM AVENTURIS.",
directories: {
output: "dist/insider"
},
files: [
"app/**/*",
"LICENSE"
],
asarUnpack: "app/Database/**/*",
win: {
target: [
{
target: "nsis",
arch: [
"x64",
"ia32"
]
}
],
icon: "app/icon.ico",
artifactName: "OptolithInsiderSetup_${version}.${ext}"
},
nsis: {
perMachine: true,
differentialPackage: true,
deleteAppDataOnUninstall: false,
},
linux: {
category: "RolePlaying",
executableName: "OptolithInsider",
icon: "app",
target: [
{
target: "AppImage",
arch: [
"x64"
]
},
{
target: "tar.gz",
arch: [
"x64"
]
}
],
artifactName: "OptolithInsider_${version}.${ext}"
},
mac: {
category: "public.app-category.role-playing-games",
type: "distribution",
icon: "app/icon.icns",
target: [
{
target: "default",
arch: "universal"
}
],
artifactName: "OptolithInsider_${version}.${ext}",
entitlements: "deploy/entitlements.mac.plist",
entitlementsInherit: "deploy/entitlements.mac.plist",
hardenedRuntime: true,
gatekeeperAssess: false,
},
dmg: {
sign: false
},
publish: {
"provider": "generic",
"url": `${process.env.UPDATE_URL}/insider/\${os}`,
"channel": "latest"
},
afterSign: notarize,
}
export default config
-93
View File
@@ -1,93 +0,0 @@
// @ts-check
import { join } from "node:path"
import packageJson from "../package.json" assert { type: "json" }
import { run, upload } from "./uploader.js"
/**
* Needed env variables:
* - `HOST`
* - `USERNAME`
* - `PASSWORD`
* - `ROOT`
*
* Optional env variables:
* - `CI`
*/
const { HOST, USERNAME, PASSWORD, ROOT = "/", CI } = process.env
const args = process.argv.slice(2)
// Detect channel
if (!args.includes("--stable") && !args.includes("--prerelease")) {
throw new TypeError(`Missing channel argument (either "--stable" or "--prerelease")`)
}
const isPrerelease = args.includes("--prerelease")
console.log(`Detected channel: ${isPrerelease ? "prerelease" : "stable"}`)
// Detect OS
let os
let osName
if (process.argv.includes("--linux") || process.platform === "linux") {
os = /** @type {const} */ ("linux")
osName = "Linux"
} else if (process.argv.includes("--mac") || process.platform === "darwin") {
os = /** @type {const} */ ("mac")
osName = "macOS"
} else if (process.argv.includes("--win") || process.platform === "win32") {
os = /** @type {const} */ ("win")
osName = "Windows"
} else {
throw new TypeError(`The target operating system cannot be inferred from the environment.`)
}
console.log(`Detected operating system: ${osName}`)
// Infer files to upload
let fileExtensions
let updateInfoFileName
switch (os) {
case "win":
fileExtensions = [".exe", ".exe.blockmap"]
updateInfoFileName = "latest.yml"
break
case "linux":
fileExtensions = [".AppImage", ".tar.gz"]
updateInfoFileName = "latest-linux.yml"
break
case "mac":
fileExtensions = [".dmg", ".dmg.blockmap", ".zip", ".zip.blockmap"]
updateInfoFileName = "latest-mac.yml"
break
}
const fileNames = [
...fileExtensions.map(ext => {
const chPart = isPrerelease ? "Insider" : ""
const osPart = os === "win" ? "Setup" : ""
return `Optolith${chPart}${osPart}_${packageJson.version}${ext}`
}),
updateInfoFileName,
]
console.log(`Files to upload: ${fileNames.join(", ")}.`)
// Upload files
const localDir = isPrerelease ? join("dist", "insider") : join("dist")
const remoteDir = join(ROOT, isPrerelease ? "insider" : ".", os)
await run({ host: HOST, username: USERNAME, password: PASSWORD }, async client => {
for (const fileName of fileNames) {
console.log(`Uploading ${fileName} ...`)
await upload(client, localDir, remoteDir, fileName)
}
})
console.log(`Uploading files finished successfully.`)
+52
View File
@@ -0,0 +1,52 @@
// @ts-check
import packageJson from "../package.json" assert { type: "json" }
import { getApplicationFileNames, getUpdateFileName } from "./assetNames.mjs"
import { getLocalPath } from "./localPath.mjs"
import { getSystem, getSystemName } from "./platform.mjs"
import { run, upload } from "./remoteConnection.mjs"
import { getRemotePath } from "./remotePath.mjs"
/**
* Needed env variables:
* - `HOST`
* - `USERNAME`
* - `PASSWORD`
* - `ROOT`
*
* Optional env variables:
* - `CI`
*/
const {
HOST,
USERNAME,
PASSWORD,
ROOT = "/",
CI,
} = process.env
const [channel] = process.argv.slice(2)
const os = getSystem()
if (channel !== "stable" && channel !== "prerelease") {
throw new TypeError(`publishToServer requires a specified channel ("stable" or "prerelease"), but it received ${channel}`)
}
console.log(`Preparing to upload update files for "${getSystemName(os)}" on "${channel}" channel...`)
const localDir = getLocalPath(channel)
const remoteDir = getRemotePath(ROOT, channel, os)
const updateFileName = getUpdateFileName(os)
const applicationFileNames = getApplicationFileNames(os, channel, packageJson.version)
console.log(`Files to upload: ${[updateFileName, ...applicationFileNames] .join (", ")}.`)
await run({ host: HOST, username: USERNAME, password: PASSWORD }, async client => {
for (const applicationFileName of applicationFileNames) {
await upload(client, localDir, remoteDir, applicationFileName)
}
await upload(client, localDir, remoteDir, updateFileName)
})
console.log(`Uploading files finished successfully.`)
+12
View File
@@ -0,0 +1,12 @@
// @ts-check
import { join } from "path"
/**
* @param {"prerelease" | "stable"} channel
*/
export const getLocalPath = channel => {
switch (channel) {
case "prerelease": return join("dist", "insider")
case "stable": return join("dist")
}
}
+25
View File
@@ -0,0 +1,25 @@
// @ts-check
import { notarize as electronNotarize } from 'electron-notarize'
/**
* @param context {import("electron-builder").AfterPackContext}
*/
export const notarize = async (context) => {
const { electronPlatformName, appOutDir } = context
if (electronPlatformName === 'darwin') {
const appName = context.packager.appInfo.productFilename
console.log(`Notarizing "${appName}.app"...`)
await electronNotarize({
tool: "notarytool",
appPath: `${appOutDir}/${appName}.app`,
appleId: /** @type {string} */ (process.env.APPLEID),
appleIdPassword: /** @type {string} */ (process.env.APPLEIDPASS),
teamId: /** @type {string} */ (process.env.TEAMID),
})
console.log(`Notarization successful`)
}
}
+25
View File
@@ -0,0 +1,25 @@
// @ts-check
import { platform } from "os"
/**
* @typedef {"win" | "mac" | "linux"} System
* @returns {System}
*/
export const getSystem = () => {
switch (platform()) {
case "win32": return "win"
case "darwin": return "mac"
default: return "linux"
}
}
/**
* @param {System} system
*/
export const getSystemName = system => {
switch (system) {
case "win": return "Windows"
case "mac": return "macOS"
case "linux": return "Linux"
}
}
+41
View File
@@ -0,0 +1,41 @@
// @ts-check
import { join } from "path"
import { join as joinPosix } from "path/posix"
import Client from "ssh2-sftp-client"
/**
* @param {Client.ConnectOptions} options
* @param {(client: Client) => Promise<void>} action
*/
export const run = async (options, action) => {
const client = new Client()
console.log("Connecting to server ...")
await client.connect(options)
console.log(`Server connection established.`)
await action(client)
await client.end ()
console.log("Closed server connection.")
}
/**
* @param {Client} client
* @param {string} localDir
* @param {string} remoteDir
* @param {string} fileName
*/
export const upload = async (client, localDir, remoteDir, fileName) => {
console.log(`Uploading ${fileName} ...`);
await client.fastPut (
join (localDir, fileName),
joinPosix (remoteDir, fileName),
{
step: !process.env.CI ? (totalTransferred, _, total) => {
const percent = Math.floor (totalTransferred / total * 100)
console.log(`Progress: ${percent}%`);
} : undefined
}
)
console.log(`Upload done: ${fileName}.`);
}
+31
View File
@@ -0,0 +1,31 @@
// @ts-check
import { join } from "path/posix"
/**
* @param {"prerelease" | "stable"} channel
*/
const remoteChannelPath = channel => {
switch (channel) {
case "prerelease": return "insider"
case "stable": return "."
}
}
/**
* @param {import("./platform.mjs").System} os
*/
const remoteOsPath = os => {
switch (os) {
case "win": return "win"
case "linux": return "linux"
default: return "mac"
}
}
/**
* @param {string} root
* @param {"prerelease" | "stable"} channel
* @param {import("./platform.mjs").System} os
*/
export const getRemotePath = (root, channel, os) =>
join(root, remoteChannelPath(channel), remoteOsPath(os))
-51
View File
@@ -1,51 +0,0 @@
// @ts-check
import { join } from "path"
import { join as joinPosix } from "path/posix"
import Client from "ssh2-sftp-client"
/**
* Create a new sftp/ssh client and use it for the callback. The connection will
* be cleaned up afterwards.
* @param {Client.ConnectOptions} options
* @param {(client: Client) => Promise<void>} action
*/
export const run = async (options, action) => {
const client = new Client()
console.log("Connecting to server ...")
await client.connect(options)
console.log(`Server connection established.`)
await action(client)
await client.end()
console.log("Closed server connection.")
}
/**
* Uploads a file from a local directory to a remote directory using the given
* client.
* @param {Client} client
* @param {string} localDir
* @param {string} remoteDir
* @param {string} fileName
*/
export const upload = async (client, localDir, remoteDir, fileName) => {
console.log(`Uploading ${fileName} ...`)
let didStep = false
await client.fastPut(join(localDir, fileName), joinPosix(remoteDir, fileName), {
step: !process.env.CI
? (totalTransferred, _, total) => {
if (didStep) {
process.stdout.clearLine(0)
process.stdout.cursorTo(0)
} else {
didStep = true
}
const percent = Math.floor((totalTransferred / total) * 100)
process.stdout.write(`Progress: ${percent}%`)
}
: undefined,
})
console.log(`Upload done: ${fileName}`)
}
-20
View File
@@ -1,20 +0,0 @@
# `.icns` file from `.iconset` folder
```sh
iconutil -c icns <PATH_TO_FOLDER>
```
Make sure all of the following files are available. The tool is **case-sensitive**.
```
icon_16.png
icon_16@2x.png
icon_32.png
icon_32@2x.png
icon_128.png
icon_128@2x.png
icon_256.png
icon_256@2x.png
icon_512.png
icon_512@2x.png
```
+18
View File
@@ -0,0 +1,18 @@
{
"appId": "com.lukasobermann.optolith",
"productName": "Optolith",
"directories": { "output": "dist" },
"files": ["app/**/*", "LICENSE"],
"npmRebuild": false,
"mac": {
"category": "public.app-category.role-playing-games",
"icon": "app/icon.icns",
"target": [
{ "target": "dmg", "arch": ["arm64", "x64"] },
{ "target": "zip", "arch": ["arm64", "x64"] }
],
"artifactName": "Optolith_${version}_${arch}.${ext}",
"identity": null
},
"dmg": { "sign": false }
}
+49
View File
@@ -0,0 +1,49 @@
{
"appId": "com.lukasobermann.optolith",
"productName": "Optolith",
"directories": {
"output": "dist"
},
"files": [
"app/**/*",
"LICENSE"
],
"npmRebuild": false,
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
],
"icon": "app/icon.ico",
"artifactName": "OptolithSetup_${version}.${ext}"
},
"nsis": {
"perMachine": false,
"deleteAppDataOnUninstall": false
},
"linux": {
"category": "RolePlaying",
"executableName": "Optolith",
"icon": "app",
"target": [
{
"target": "AppImage",
"arch": [
"x64"
]
},
{
"target": "tar.gz",
"arch": [
"x64"
]
}
],
"artifactName": "Optolith_${version}.${ext}"
},
"publish": null
}
-298
View File
@@ -1,298 +0,0 @@
// @ts-check
import { FlatCompat } from "@eslint/eslintrc"
import js from "@eslint/js"
import eslintConfigPrettier from "eslint-config-prettier"
import jsdoc from "eslint-plugin-jsdoc"
import react from "eslint-plugin-react"
import globals from "globals"
import ts from "typescript-eslint"
const compat = new FlatCompat({
baseDirectory: import.meta.dirname,
})
/** @type {import('eslint').Linter.Config[]} */
export default [
js.configs.recommended,
react.configs.flat.recommended,
react.configs.flat['jsx-runtime'],
...compat.extends("plugin:react-hooks/recommended"),
.../** @type {import('eslint').Linter.Config[]} */ (ts.configs.recommended),
jsdoc.configs["flat/recommended-typescript-error"],
{
languageOptions: {
globals: {
...globals.node,
...globals.browser,
},
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
// Possible problems
"require-atomic-updates": "error",
"no-self-compare": "error",
"no-unmodified-loop-condition": "error",
// Suggestions
"accessor-pairs": "error",
"arrow-body-style": "error",
"class-methods-use-this": "warn",
"default-case": "error",
eqeqeq: "error",
"guard-for-in": "error",
"init-declarations": "error",
"no-alert": "error",
"no-caller": "error",
"no-eval": "error",
"no-extend-native": [
2,
{
exceptions: ["Array", "Map", "Set"],
},
],
"no-extra-bind": "error",
"no-floating-decimal": "error",
"no-implicit-coercion": "error",
"no-implied-eval": "error",
"no-iterator": "error",
"no-labels": "error",
"no-lone-blocks": "error",
"no-lonely-if": "error",
"no-loop-func": "error",
"no-multi-str": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-wrappers": "error",
"no-octal": "error",
"no-octal-escape": "error",
"no-param-reassign": "error",
"no-proto": "error",
"no-redeclare": "off",
"no-return-assign": "error",
"no-script-url": "error",
"no-throw-literal": "error",
"no-useless-call": "error",
"no-useless-computed-key": "error",
"no-useless-concat": "error",
"no-useless-rename": "error",
"no-useless-return": "error",
"no-var": "error",
"no-void": "error",
"no-warning-comments": "error",
"object-shorthand": "error",
"prefer-arrow-callback": "error",
"prefer-const": "error",
"prefer-destructuring": [
2,
{
VariableDeclarator: {
array: true,
object: true,
},
AssignmentExpression: {
array: false,
object: false,
},
},
],
"prefer-named-capture-group": "warn",
"prefer-numeric-literals": "error",
"prefer-object-has-own": "error",
"prefer-promise-reject-errors": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",
radix: "error",
"require-unicode-regexp": "error",
"symbol-description": "error",
yoda: "error",
// TypeScript
"@typescript-eslint/adjacent-overload-signatures": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/member-delimiter-style": [
2,
{
multiline: {
delimiter: "none",
},
singleline: {
delimiter: "semi",
requireLast: false,
},
},
],
"@typescript-eslint/naming-convention": [
"error",
{
selector: "interface",
format: ["PascalCase"],
custom: {
regex: "^I[A-Z]",
match: false,
},
},
],
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-redeclare": "error",
"@typescript-eslint/no-shadow": [
"error",
{
ignoreTypeValueShadow: true,
}
],
"no-unused-expressions": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-unused-expressions": "error",
"@typescript-eslint/no-use-before-define": "error",
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/prefer-for-of": "error",
"@typescript-eslint/prefer-readonly": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/strict-boolean-expressions": "error",
"@typescript-eslint/switch-exhaustiveness-check": "error",
// React
"react/boolean-prop-naming": "error",
"react/destructuring-assignment": "error",
"react/no-access-state-in-setstate": "error",
"react/no-array-index-key": "error",
"react/no-children-prop": "error",
"react/no-danger": "error",
"react/no-danger-with-children": "error",
"react/no-deprecated": "error",
"react/no-did-mount-set-state": "error",
"react/no-did-update-set-state": "warn",
"react/no-direct-mutation-state": "error",
"react/no-find-dom-node": "error",
"react/no-is-mounted": "error",
"react/no-redundant-should-component-update": "error",
"react/no-render-return-value": "error",
"react/no-typos": "error",
"react/no-string-refs": "error",
"react/no-this-in-sfc": "error",
"react/no-unescaped-entities": "error",
"react/no-unsafe": "error",
"react/no-unused-prop-types": "error",
"react/no-unused-state": "error",
"react/no-will-update-set-state": "error",
"react/prefer-es6-class": "error",
"react/prefer-stateless-function": "error",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"react/require-render-return": "error",
"react/self-closing-comp": "error",
"react/state-in-constructor": [2, "never"],
"react/style-prop-object": "error",
"react/void-dom-elements-no-children": "error",
"react/jsx-boolean-value": [2, "never"],
"react/jsx-child-element-spacing": "error",
"react/jsx-closing-tag-location": "error",
"react/jsx-curly-newline": "error",
"react/jsx-curly-spacing": "error",
"react/jsx-equals-spacing": "error",
"react/jsx-first-prop-new-line": "error",
"react/jsx-indent-props": [2, 2],
"react/jsx-no-bind": "error",
"react/jsx-no-comment-textnodes": "error",
"react/jsx-no-literals": "error",
"react/jsx-no-target-blank": "error",
"react/jsx-one-expression-per-line": [
2,
{
allow: "single-child",
},
],
"react/jsx-curly-brace-presence": [
2,
{
props: "never",
children: "ignore",
},
],
"react/jsx-fragments": "error",
"react/jsx-pascal-case": "error",
"react/jsx-props-no-multi-spaces": "error",
"react/jsx-props-no-spreading": "error",
"react/jsx-tag-spacing": "error",
"react/jsx-wrap-multilines": [
2,
{
declaration: "parens-new-line",
assignment: "parens-new-line",
return: "parens-new-line",
arrow: "parens-new-line",
condition: "parens-new-line",
logical: "parens-new-line",
prop: "ignore",
},
],
// React Hooks
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",
// JSDoc
"jsdoc/check-tag-names": [
"error",
{
definedTags: ["main", "integer", "minItems"],
},
],
"jsdoc/require-jsdoc": [
"error",
{
contexts: [
"TSInterfaceDeclaration",
"TSMethodSignature",
"TSEnumDeclaration",
"TSTypeAliasDeclaration",
"ExportNamedDeclaration > VariableDeclaration",
],
publicOnly: true,
require: {
ArrowFunctionExpression: true,
ClassDeclaration: true,
ClassExpression: true,
FunctionDeclaration: true,
FunctionExpression: true,
MethodDefinition: true,
},
},
],
"jsdoc/require-param": "off",
"jsdoc/require-returns": "off",
"jsdoc/require-description": [
"error",
{
contexts: [
"any",
]
},
],
},
settings: {
react: {
version: "detect",
},
},
},
eslintConfigPrettier,
{
ignores: [
".webpack/**",
"*.config.js",
"deploy/**",
"src/App/**",
"src/Control/**",
"src/Data/**",
"src/Debug/**",
"src/System/**",
"src/database/contents/**",
],
},
]
+11
View File
@@ -0,0 +1,11 @@
// @ts-check
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
globals: {
"ts-jest": {
tsconfig: "tsconfig.test.json"
}
}
}
-16
View File
@@ -1,16 +0,0 @@
// @ts-check
import { join } from "node:path"
import { jsonSchema } from "optolith-tsjsonschemamd/renderers"
/** @type {import("optolith-tsjsonschemamd").GeneratorOptions} */
export default {
sourceDir: join("src", "shared", "schema"),
outputs: [
{
targetDir: join("src", "assets", "schema"),
renderer: jsonSchema({ spec: "Draft_07" }),
}
],
clean: true,
verbose: false
}
+15260 -16262
View File
File diff suppressed because it is too large Load Diff
+82 -66
View File
@@ -1,7 +1,7 @@
{
"name": "optolith",
"productName": "Optolith",
"version": "2.0.0-alpha.1",
"version": "1.6.0",
"description": "A generator for heroes for the P&P-RPG 'The Dark Eye'.",
"author": {
"name": "Lukas Obermann",
@@ -9,82 +9,98 @@
},
"homepage": "https://optolith.app",
"license": "MPL-2.0",
"main": ".webpack/main.cjs",
"main": "app/main.js",
"dependencies": {
"@optolith/entity-descriptions": "^0.2.1",
"@reduxjs/toolkit": "^2.2.7",
"adm-zip": "^0.5.16",
"debug": "^4.3.7",
"electron-updater": "^6.3.9",
"@electron/remote": "^2.0.8",
"adm-zip": "^0.5.1",
"ajv": "6.12.6",
"electron-devtools-installer": "^3.2.1",
"electron-log": "^4.4.8",
"electron-updater": "^5.0.5",
"electron-window-state": "5.0.3",
"immer": "^10.1.1",
"moment": "^2.30.1",
"moment": "^2.29.4",
"mousetrap": "1.6.5",
"optolith-character-schema": "^0.0.3",
"optolith-database-schema": "^0.17.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-markdown": "^9.0.1",
"react-redux": "^9.1.2",
"rehype-raw": "^7.0.0",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.0",
"semver": "^7.5.4"
"react": "16.13.1",
"react-custom-scrollbars": "4.2.1",
"react-dom": "16.13.1",
"react-markdown": "^8.0.0",
"react-portal": "4.2.1",
"react-redux": "7.2.1",
"react-textfit": "1.1.0",
"redux": "4.0.5",
"redux-devtools-extension": "2.13.8",
"redux-thunk": "2.3.0",
"rehype-raw": "^5.1.0",
"remark-breaks": "^2.0.2",
"remark-gfm": "^3.0.1",
"reselect": "4.0.0",
"semver": "7.3.2",
"tslib": "^2.3.1",
"yaml": "1.10.0"
},
"devDependencies": {
"@electron/notarize": "^2.5.0",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.11.1",
"@rspack/cli": "^1.0.8",
"@rspack/core": "^1.0.8",
"@types/mousetrap": "^1.6.15",
"@types/node": "^22.7.4",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.0",
"@types/react-redux": "^7.1.34",
"@types/semver": "^7.5.8",
"@types/ssh2-sftp-client": "^9.0.4",
"css-loader": "^7.1.2",
"electron": "^32.1.2",
"electron-builder": "^25.1.7",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jsdoc": "^50.3.1",
"eslint-plugin-react": "^7.37.1",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-tsdoc": "^0.3.0",
"glob": "^11.0.0",
"globals": "^15.10.0",
"html-webpack-plugin": "^5.6.0",
"optolith-tsjsonschemamd": "^0.10.7",
"resolve-url-loader": "^5.0.0",
"sass": "^1.79.4",
"sass-loader": "^16.0.2",
"ssh2-sftp-client": "^11.0.0",
"standard-version": "^9.5.0",
"tsx": "^4.19.1",
"typescript": "^5.6.2",
"typescript-eslint": "^8.8.0"
"@commitlint/cli": "11.0.0",
"@commitlint/config-conventional": "11.0.0",
"@types/classnames": "2.2.11",
"@types/jest": "26.0.15",
"@types/mousetrap": "1.6.4",
"@types/node": "14.14.7",
"@types/react": "16.9.56",
"@types/react-custom-scrollbars": "4.0.7",
"@types/react-dom": "16.9.9",
"@types/react-portal": "4.0.2",
"@types/react-redux": "7.1.11",
"@types/semver": "7.3.4",
"@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.10.0",
"custom-env": "2.0.1",
"dotenv": "8.2.0",
"electron": "^19.0.8",
"electron-builder": "^23.1.0",
"electron-notarize": "^1.2.1",
"eslint": "^8.7.0",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-tsdoc": "^0.2.14",
"fs-extra": "9.0.1",
"gentype": "3.38.0",
"html-webpack-plugin": "^5.5.0",
"husky": "^4.3.8",
"jest": "^26.6.3",
"sass": "1.29.0",
"ssh2-sftp-client": "^9.0.1",
"standard-version": "^9.3.1",
"ts-jest": "^26.5.6",
"ts-loader": "^9.2.6",
"typescript": "^4.5.5",
"webpack": "^5.67.0",
"webpack-cli": "^4.9.2"
},
"scripts": {
"prerelease:win": "set NODE_ENV=development && node ./build/build.js --win --prerelease",
"prerelease:linux": "NODE_ENV=development node -r dotenv/config ./build/build.js --linux --prerelease",
"prerelease:mac": "NODE_ENV=development node ./build/build.js --mac --prerelease",
"stable:win": "set NODE_ENV=production && node ./build/build.js --win --stable",
"stable:linux": "NODE_ENV=production node ./build/build.js --linux --stable",
"stable:mac": "NODE_ENV=production node ./build/build.js --mac --stable",
"js:build": "rspack build",
"js:watch": "rspack build -w",
"prerelease:win": "set NODE_ENV=development && node ./deploy/buildInsiderWin.js",
"prerelease:linux": "NODE_ENV=development node -r dotenv/config ./deploy/buildInsiderLinux.js",
"prerelease:mac": "NODE_ENV=development node ./deploy/buildInsiderMac.js",
"stable:win": "set NODE_ENV=production && node ./deploy/buildWin.js",
"stable:linux": "NODE_ENV=production node ./deploy/buildLinux.js",
"stable:mac": "NODE_ENV=production node ./deploy/buildMac.js",
"css:build": "sass --style=compressed src/Main.scss app/main.css",
"css:watch": "sass --watch --style=compressed src/Main.scss app/main.css",
"build": "npm run ts:build && npm run css:build && npm run js:build",
"js:build": "webpack",
"js:watch": "webpack -w",
"ts:build": "tsc",
"ts:watch": "tsc -w",
"createschemas": "otjsmd",
"lint": "eslint \"src/**\"",
"start": "electron --js-flags='--stack-size=5000' .",
"test": "glob -c \"node --import tsx --test\" \"./src/shared/**/*.test.ts\"",
"lint": "eslint \"src/\" --ext \".ts\" --ext \".tsx\"",
"start": "electron .",
"test": "jest",
"circ": "madge -c ./src",
"release:main": "standard-version --skip.commit --skip.tag",
"release:pre": "standard-version --prerelease=alpha",
"buildicns": "iconutil -c icns src/assets/icon/AppIcon.iconset && iconutil -c icns src/assets/icon/AppIcon.pre.iconset"
"release:pre": "standard-version --prerelease=alpha"
},
"repository": "github:elyukai/optolith-client",
"type": "module"
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
-136
View File
@@ -1,136 +0,0 @@
// @ts-check
import { resolve } from "node:path"
import { getOptimization } from "./rspack.optimization.js"
import { rendererPlugins } from "./rspack.plugins.js"
import { rules } from "./rspack.rules.js"
/**
* @param {string} name
* @param {EnvironmentMode} mode
* @returns {import("@rspack/core").Configuration[]}
*/
const createRendererConfig = (name, mode) => [
{
name: `renderer_${name}`,
mode,
entry: {
[`renderer_${name}`]: `./src/${name}_window/index.tsx`,
},
target: "electron-renderer",
optimization: getOptimization(`renderer_${name}`),
module: {
rules: rules,
},
resolve: {
symlinks: false,
conditionNames: ["browser", "default", "import", "require"],
},
output: {
path: resolve(import.meta.dirname, ".webpack"),
filename: "[name].js",
globalObject: "globalThis",
},
plugins: rendererPlugins(mode),
externals: {
"electron/renderer": "commonjs electron/renderer",
"electron/common": "commonjs electron/common",
},
externalsPresets: {
electronRenderer: true,
},
profile: true,
devtool: "source-map",
},
{
name: `renderer_${name}_preload`,
mode,
entry: {
[`renderer_${name}_preload`]: `./src/${name}_window_preload/index.ts`,
},
target: "electron-preload",
module: {
rules: rules,
},
resolve: {
symlinks: false,
},
output: {
path: resolve(import.meta.dirname, ".webpack"),
filename: "[name].cjs",
},
externals: {
"electron/renderer": "commonjs electron/renderer",
"electron/common": "commonjs electron/common",
},
externalsPresets: {
electronPreload: true,
},
},
]
/**
* @typedef {"development" | "production"} EnvironmentMode
* @returns {import("@rspack/core").Configuration[]}
*/
export default () => {
/** @type {EnvironmentMode} */
const mode = process.env.NODE_ENV === "production" ? "production" : "development"
return [
{
name: "main",
mode,
entry: {
main: "./src/main/index.ts",
},
target: "electron-main",
optimization: getOptimization("main"),
module: {
rules: rules,
},
resolve: {
symlinks: false,
},
output: {
path: resolve(import.meta.dirname, ".webpack"),
filename: "[name].cjs",
},
externals: {
"electron/main": "commonjs electron/main",
"electron/common": "commonjs electron/common",
},
externalsPresets: {
electronMain: true,
},
},
{
name: "database",
mode,
entry: {
database: "./src/database/index.ts",
},
target: "electron-main",
optimization: getOptimization("database"),
module: {
rules: rules,
},
resolve: {
symlinks: false,
},
output: {
path: resolve(import.meta.dirname, ".webpack"),
filename: "[name].cjs",
},
externalsPresets: {
electronMain: true,
},
externals: {
"optolith-database-schema": "module optolith-database-schema",
},
},
...createRendererConfig("main", mode),
...createRendererConfig("updater", mode),
...createRendererConfig("settings", mode),
]
}
-24
View File
@@ -1,24 +0,0 @@
// @ts-check
/**
* @param {string} processType
* @returns {import("@rspack/core").Configuration["optimization"]}
*/
export const getOptimization = (processType) => ({
splitChunks: {
chunks: "all",
cacheGroups: {
default: {
name: processType,
minChunks: 2,
priority: -20,
},
defaultVendors: {
name: `${processType}_vendors`,
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
},
},
})
-20
View File
@@ -1,20 +0,0 @@
// @ts-check
import rspack from "@rspack/core"
import HtmlWebpackPlugin from "html-webpack-plugin"
/**
*
* @param {import("./rspack.config.js").EnvironmentMode} mode
* @returns {import("@rspack/core").Configuration["plugins"]}
*/
export const rendererPlugins = (mode) => [
new HtmlWebpackPlugin({
filename: "[name].html",
template: "./src/template.html",
}),
new rspack.CssExtractRspackPlugin({
filename: `assets/css/${mode === "development" ? "[name].css" : "[name].[hash].css"}`,
chunkFilename: `assets/css/${mode === "development" ? "[id].css" : "[id].[hash].css"}`,
}),
]
-73
View File
@@ -1,73 +0,0 @@
// @ts-check
import rspack from "@rspack/core"
/**
* @type {Required<import("@rspack/core").ModuleOptions>["rules"]}
*/
export const rules = [
{
test: /\.tsx?$/,
exclude: [/node_modules/],
loader: "builtin:swc-loader",
resolve: {
extensionAlias: {
'.js': ['.tsx', '.ts', 'jsx', '.js'],
},
},
options: {
jsc: {
parser: {
syntax: 'typescript',
},
transform: {
react: {
runtime: 'automatic',
throwIfNamespace: true,
useBuiltins: false,
},
},
},
},
type: 'javascript/auto',
},
{
test: /\.(png|jpe?g|svg)$/i,
type: "asset/resource",
generator: {
filename: "assets/images/[name].[hash][ext][query]",
},
},
{
test: /\.(icns|ico)$/i,
type: "asset/resource",
generator: {
filename: "assets/icons/[name].[hash][ext][query]",
},
},
{
test: /\.(ttf|otf|woff2?)$/i,
type: "asset/resource",
generator: {
filename: "assets/fonts/[name].[hash][ext][query]",
},
},
{
test: /\.(css|sass|scss)$/,
use: [
rspack.CssExtractRspackPlugin.loader,
"css-loader",
"resolve-url-loader",
{
loader: "sass-loader",
options: {
sourceMap: true,
},
},
],
generator: {
filename: "assets/css/[name].[hash][ext][query]",
},
type: "javascript/auto",
},
]
+6 -13
View File
@@ -1,18 +1,11 @@
import { Action, UnknownAction } from "redux"
import { Action, AnyAction } from "redux"
import { ThunkAction, ThunkDispatch } from "redux-thunk"
import { AppStateRecord } from "../Models/AppState"
export type ReduxAction<R = void, D extends Action = UnknownAction> = ThunkAction<
R,
AppStateRecord,
undefined,
D
>
export type ReduxAction<R = void, D extends Action = AnyAction> =
ThunkAction<R, AppStateRecord, undefined, D>
export type ReduxActions<R = void, D extends Action = UnknownAction> = ReduxAction<R, D> | D
export type ReduxActions<R = void, D extends Action = AnyAction> = ReduxAction<R, D> | D
export type ReduxDispatch<A extends Action = UnknownAction> = ThunkDispatch<
AppStateRecord,
undefined,
A
>
export type ReduxDispatch<A extends Action = AnyAction> =
ThunkDispatch<AppStateRecord, undefined, A>
+17
View File
@@ -34,6 +34,7 @@ import { translate, translateP } from "../Utilities/I18n"
import { pipe, pipe_ } from "../Utilities/pipe"
import { misNumberM } from "../Utilities/typeCheckUtils"
import { getWikiEntry } from "../Utilities/WikiUtils"
import { WikiInfoSelector } from "../Views/InlineWiki/WikiInfo"
import { ReduxAction, ReduxDispatch } from "./Actions"
import { addAlert, addNotEnoughAPAlert, AlertOptions } from "./AlertActions"
@@ -466,6 +467,14 @@ export const switchRatingVisibility = (): SwitchDisAdvRatingVisibilityAction =>
type: ActionTypes.SWITCH_DISADV_RATING_VISIBILITY,
})
export const setRule = (selector: WikiInfoSelector, rule: string): SaveRuleAction => ({
type: ActionTypes.SET_CUSTOM_RULE,
payload: {
rule,
selector,
},
})
export interface SetActiveAdvantagesFilterTextAction {
type: ActionTypes.SET_ADVANTAGES_FILTER_TEXT
payload: {
@@ -525,3 +534,11 @@ export const setInactiveDisadvantagesFilterText =
filterText,
},
})
export interface SaveRuleAction {
type: ActionTypes.SET_CUSTOM_RULE
payload: {
rule: string
selector: WikiInfoSelector
}
}
+2
View File
@@ -53,6 +53,8 @@ export const requestConfigSave: ReduxAction<Promise<boolean>> =
Just (UISSA.sheetCheckAttributeValueVisibility (uiSettingsState)),
sheetUseParchment:
Just (UISSA.sheetUseParchment (uiSettingsState)),
sheetShowRules:
Just (UISSA.sheetShowRules (uiSettingsState)),
sheetZoomFactor:
UISSA.sheetZoomFactor (uiSettingsState),
theme: Just (UISSA.theme (uiSettingsState)),
+13 -1
View File
@@ -1,4 +1,9 @@
import { SWITCH_SHEET_ATTR_VALUE_VISIBILITY, SWITCH_SHEET_USE_PARCHMENT, SET_SHEET_ZOOM_FACTOR } from "../Constants/ActionTypes"
import {
SWITCH_SHEET_ATTR_VALUE_VISIBILITY,
SWITCH_SHEET_USE_PARCHMENT,
SET_SHEET_ZOOM_FACTOR,
SWITCH_SHEET_SHOW_RULES
} from "../Constants/ActionTypes"
export interface SwitchSheetAttributeValueVisibilityAction {
type: SWITCH_SHEET_ATTR_VALUE_VISIBILITY
@@ -16,6 +21,13 @@ export const switchUseParchment = (): SwitchSheetUseParchmentAction => ({
type: SWITCH_SHEET_USE_PARCHMENT,
})
export interface SwitchSheetShowRules {
type: SWITCH_SHEET_SHOW_RULES
}
export const switchShowRules = (): SwitchSheetShowRules => ({
type: SWITCH_SHEET_SHOW_RULES,
})
export interface SetSheetZoomFactor {
type: SET_SHEET_ZOOM_FACTOR
payload: {
+6
View File
@@ -321,6 +321,9 @@ export type SET_DISADV_TIER = "SET_DISADV_TIER"
export const SWITCH_DISADV_RATING_VISIBILITY = "SWITCH_DISADV_RATING_VISIBILITY"
export type SWITCH_DISADV_RATING_VISIBILITY = "SWITCH_DISADV_RATING_VISIBILITY"
export const SET_CUSTOM_RULE = "SET_CUSTOM_RULE"
export type SET_CUSTOM_RULE = "SET_CUSTOM_RULE"
export const ADD_TALENT_POINT = "ADD_TALENT_POINT"
export type ADD_TALENT_POINT = "ADD_TALENT_POINT"
export const REMOVE_TALENT_POINT = "REMOVE_TALENT_POINT"
@@ -615,6 +618,9 @@ export type SWITCH_SHEET_ATTR_VALUE_VISIBILITY = "SWITCH_SHEET_ATTR_VALUE_VISIBI
export const SWITCH_SHEET_USE_PARCHMENT = "SWITCH_SHEET_USE_PARCHMENT"
export type SWITCH_SHEET_USE_PARCHMENT = "SWITCH_SHEET_USE_PARCHMENT"
export const SWITCH_SHEET_SHOW_RULES = "SWITCH_SHEET_SHOW_RULES"
export type SWITCH_SHEET_SHOW_RULES = "SWITCH_SHEET_SHOW_RULES"
export const SET_SHEET_ZOOM_FACTOR = "SET_SHEET_ZOOM_FACTOR"
export type SET_SHEET_ZOOM_FACTOR = "SET_SHEET_ZOOM_FACTOR"
+123
View File
@@ -0,0 +1,123 @@
import { connect } from "react-redux"
import { fromJust, isJust, Maybe } from "../../Data/Maybe"
import { ReduxDispatch } from "../Actions/Actions"
import * as AttributesActions from "../Actions/AttributesActions"
import * as SubwindowsActions from "../Actions/SubwindowsActions"
import { EnergyId } from "../Constants/Ids"
import { AppStateRecord } from "../Models/AppState"
import { getAdjustmentValue, getAttributesForView, getAttributeSum, getAvailableAdjustmentIds } from "../Selectors/attributeSelectors"
import { getDerivedCharacteristics } from "../Selectors/derivedCharacteristicsSelectors"
import { getMaxTotalAttributeValues } from "../Selectors/elSelectors"
import { getIsInCharacterCreation, getIsRemovingEnabled } from "../Selectors/phaseSelectors"
import { getAddPermanentEnergy, getCurrentAttributeAdjustmentId, getEditPermanentEnergy } from "../Selectors/stateSelectors"
import { Attributes, AttributesDispatchProps, AttributesOwnProps, AttributesStateProps } from "../Views/Attributes/Attributes"
const mapStateToProps =
(state: AppStateRecord, ownProps: AttributesOwnProps): AttributesStateProps => ({
adjustmentValue: getAdjustmentValue (state, ownProps),
attributes: getAttributesForView (state, ownProps),
availableAttributeIds: getAvailableAdjustmentIds (state, ownProps),
currentAttributeId: getCurrentAttributeAdjustmentId (state),
isInCharacterCreation: getIsInCharacterCreation (state),
isRemovingEnabled: getIsRemovingEnabled (state),
derived: getDerivedCharacteristics (state, ownProps),
maxTotalAttributeValues: getMaxTotalAttributeValues (state),
sum: getAttributeSum (state, ownProps),
getEditPermanentEnergy: getEditPermanentEnergy (state),
getAddPermanentEnergy: getAddPermanentEnergy (state),
})
const mapDispatchToProps = (dispatch: ReduxDispatch): AttributesDispatchProps => ({
addPoint: async (id: string) => {
await dispatch (AttributesActions.addAttributePoint (id))
},
removePoint: (id: string) => {
dispatch (AttributesActions.removeAttributePoint (id))
},
addLifePoint: async () => {
await dispatch (AttributesActions.addLifePoint)
},
addArcaneEnergyPoint: async () => {
await dispatch (AttributesActions.addArcaneEnergyPoint)
},
addKarmaPoint: async () => {
await dispatch (AttributesActions.addKarmaPoint)
},
removeLifePoint: () => {
dispatch (AttributesActions.removeLifePoint ())
},
removeArcaneEnergyPoint: () => {
dispatch (AttributesActions.removeArcaneEnergyPoint ())
},
removeKarmaPoint: () => {
dispatch (AttributesActions.removeKarmaPoint ())
},
addLostLPPoint: () => {
dispatch (AttributesActions.addLostLPPoint ())
},
removeLostLPPoint: () => {
dispatch (AttributesActions.removeLostLPPoint ())
},
addLostLPPoints: (value: number) => {
dispatch (AttributesActions.addLostLPPoints (value))
},
addBoughtBackAEPoint: async () => {
await dispatch (AttributesActions.addBoughtBackAEPoint)
},
removeBoughtBackAEPoint: () => {
dispatch (AttributesActions.removeBoughtBackAEPoint ())
},
addLostAEPoint: () => {
dispatch (AttributesActions.addLostAEPoint ())
},
removeLostAEPoint: () => {
dispatch (AttributesActions.removeLostAEPoint ())
},
addLostAEPoints: (value: number) => {
dispatch (AttributesActions.addLostAEPoints (value))
},
addBoughtBackKPPoint: async () => {
await dispatch (AttributesActions.addBoughtBackKPPoint)
},
removeBoughtBackKPPoint: () => {
dispatch (AttributesActions.removeBoughtBackKPPoint ())
},
addLostKPPoint: () => {
dispatch (AttributesActions.addLostKPPoint ())
},
removeLostKPPoint: () => {
dispatch (AttributesActions.removeLostKPPoint ())
},
addLostKPPoints: (value: number) => {
dispatch (AttributesActions.addLostKPPoints (value))
},
openAddPermanentEnergyLoss: (energy: EnergyId) => {
console.log (`openAddPermanentEnergyLoss (${energy})`)
dispatch (SubwindowsActions.openAddPermanentEnergyLoss (energy))
},
closeAddPermanentEnergyLoss: () => {
console.log (`closeAddPermanentEnergyLoss ()`)
dispatch (SubwindowsActions.closeAddPermanentEnergyLoss ())
},
openEditPermanentEnergy: (energy: EnergyId) => {
console.log (`openEditPermanentEnergy (${energy})`)
dispatch (SubwindowsActions.openEditPermanentEnergy (energy))
},
closeEditPermanentEnergy: () => {
console.log (`closeEditPermanentEnergy ()`)
dispatch (SubwindowsActions.closeEditPermanentEnergy ())
},
setAdjustmentId: (id: Maybe<string>) => {
if (isJust (id)) {
dispatch (AttributesActions.setAdjustmentId (fromJust (id)))
}
},
})
const connectAttributes =
connect<AttributesStateProps, AttributesDispatchProps, AttributesOwnProps, AppStateRecord> (
mapStateToProps,
mapDispatchToProps
)
export const AttributesContainer = connectAttributes (Attributes)
@@ -0,0 +1,50 @@
import { connect } from "react-redux"
import { ReduxDispatch } from "../Actions/Actions"
import * as CombatTechniquesActions from "../Actions/CombatTechniquesActions"
import { AppStateRecord } from "../Models/AppState"
import { CombatTechniquesSortOptions } from "../Models/Config"
import { getAttributesForSheet } from "../Selectors/attributeSelectors"
import { getFilteredCombatTechniques } from "../Selectors/combatTechniquesSelectors"
import { getIsRemovingEnabled } from "../Selectors/phaseSelectors"
import { getCombatTechniquesFilterText } from "../Selectors/stateSelectors"
import { getCombatTechniquesSortOrder } from "../Selectors/uisettingsSelectors"
import { CombatTechniques, CombatTechniquesDispatchProps, CombatTechniquesOwnProps, CombatTechniquesStateProps } from "../Views/CombatTechniques/CombatTechniques"
const mapStateToProps = (
state: AppStateRecord,
ownProps: CombatTechniquesOwnProps
): CombatTechniquesStateProps => ({
attributes: getAttributesForSheet (state, ownProps),
isRemovingEnabled: getIsRemovingEnabled (state),
list: getFilteredCombatTechniques (state, ownProps),
sortOrder: getCombatTechniquesSortOrder (state),
filterText: getCombatTechniquesFilterText (state),
})
const mapDispatchToProps = (dispatch: ReduxDispatch) => ({
async addPoint (id: string) {
await dispatch (CombatTechniquesActions.addCombatTechniquePoint (id))
},
removePoint (id: string) {
dispatch (CombatTechniquesActions.removeCombatTechniquePoint (id))
},
setSortOrder (sortOrder: CombatTechniquesSortOptions) {
dispatch (CombatTechniquesActions.setCombatTechniquesSortOrder (sortOrder))
},
setFilterText (filterText: string) {
dispatch (CombatTechniquesActions.setCombatTechniquesFilterText (filterText))
},
})
const connectCombatTechniques =
connect<
CombatTechniquesStateProps,
CombatTechniquesDispatchProps,
CombatTechniquesOwnProps,
AppStateRecord
> (
mapStateToProps,
mapDispatchToProps
)
export const CombatTechniquesContainer = connectCombatTechniques (CombatTechniques)
+58
View File
@@ -0,0 +1,58 @@
import { ipcRenderer } from "electron"
import { connect } from "react-redux"
import { fromJust, isJust, Maybe } from "../../Data/Maybe"
import { ReduxDispatch } from "../Actions/Actions"
import * as ConfigActions from "../Actions/ConfigActions"
import * as FallbackLocaleActions from "../Actions/FallbackLocaleActions"
import * as IOActions from "../Actions/IOActions"
import * as LocaleActions from "../Actions/LocaleActions"
import { AppStateRecord } from "../Models/AppState"
import { Locale, Theme } from "../Models/Config"
import { getUserSelectableSupportedLanguages } from "../Selectors/localeSelectors"
import { getFallbackLocaleId, getFallbackLocaleType, getLocaleId, getLocaleType } from "../Selectors/stateSelectors"
import { areAnimationsEnabled, getIsEditingHeroAfterCreationPhaseEnabled, getTheme } from "../Selectors/uisettingsSelectors"
import { IPCChannels } from "../Utilities/IPCChannels"
import { Settings, SettingsDispatchProps, SettingsOwnProps, SettingsStateProps } from "../Views/Settings/Settings"
const mapStateToProps = (state: AppStateRecord): SettingsStateProps => ({
localeString: getLocaleId (state),
localeType: getLocaleType (state),
fallbackLocaleString: getFallbackLocaleId (state),
fallbackLocaleType: getFallbackLocaleType (state),
isEditingHeroAfterCreationPhaseEnabled: getIsEditingHeroAfterCreationPhaseEnabled (state),
areAnimationsEnabled: areAnimationsEnabled (state),
theme: getTheme (state),
languages: getUserSelectableSupportedLanguages (state),
isCheckForUpdatesDisabled: !(ipcRenderer.sendSync (IPCChannels.IsUpdaterEnabled) as boolean),
})
const mapDispatchToProps = (dispatch: ReduxDispatch): SettingsDispatchProps => ({
setTheme (theme: Maybe<Theme>) {
if (isJust (theme)) {
dispatch (ConfigActions.setTheme (fromJust (theme)))
}
},
switchEnableEditingHeroAfterCreationPhase () {
dispatch (ConfigActions.switchEnableEditingHeroAfterCreationPhase ())
},
async saveConfig () {
await dispatch (IOActions.requestConfigSave)
},
setLocale (id: Maybe<Locale>) {
dispatch (LocaleActions.setLocale (id))
},
setFallbackLocale (id: Maybe<Locale>) {
dispatch (FallbackLocaleActions.setFallbackLocale (id))
},
switchEnableAnimations () {
dispatch (ConfigActions.switchEnableAnimations ())
},
})
const connectSettings =
connect<SettingsStateProps, SettingsDispatchProps, SettingsOwnProps, AppStateRecord> (
mapStateToProps,
mapDispatchToProps
)
export const SettingsContainer = connectSettings (Settings)

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