diff --git a/README.md b/README.md index 50bdf73c5195e6b6a86b4077599ffd36716ae182..846d63bec60ce8156021a3692e3c5e683ab4ed66 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,103 @@ Rookie is a robotic chess board. Checkout the source code for the [chess engine] This project is the middleware that links the chess board arduino and the chess engine by tracking and displaying the current state of the board. Built with Vue and Electron. -Notes: -- board (arduino) to GUI: node.js stream to USB -- GUI to engine: fastcgi?? +[TOC] -- board sends board state to GUI, GUI passes that data to engine as a move list -- engine sends a move list to GUI, GUI converts to board state and sends to board +## Flow -GUI features -- FEN box -- sidebar area for rating (or just the number) -- no click, drag, etc. - all moves should be handled programatically +1. Game starts when button in GUI is clicked to start the game. This triggers the GUI to start continually reading for serial data updates from arduino B (defined below). +1. User makes a move on the board (always white, so always makes the first move). +1. Arduino A is connected to the board/robotic arm and reads from sensors in the board for moves. Communicates with arduino B via bluetooth (Matthew will manage code for communication between arduinos). +1. Arduino B constantly reads from arduino A for the "state change" and "board state" flags and writes the data it receives serially. +1. GUI constantly reads from arduino B for the "state change" (used for both user and robot moves) and "board state" flags. Data comes serially and needs to be converted to array. +1. GUI writes back to arduino B with "acknowledge" flag (note to self: this flag might be unnecessary and end up not being used). +1. GUI converts the board state bytes to a move list (algerbraic notation, matthew needs to determine what this will look like exactly) and updates the visual board state to match. +1. The GUI sends the move list to the engine (HTTP request?). +1. Engine reads the move list with the user's move from the GUI (HTTP request?) and updates it with the move the robot should make. Sends the updated move list back to the GUI (HTTP request?). +1. GUI fetches (HTTP request?) updated move list from engine. Converts the move list to array with "move"/"capture"/etc flags and starting and destination positions and writes the data serially. +1. Arduino B constantly reading from GUI for new writes (with "move"/"capture"/etc flags). Picks up the write and converts the serial data to a byte array. Sends the data on to arduino A (Matthew handles this). +1. Arduino A communicates with the robot arm to physically move piece from starting position to destination position (+ perform "capture"/"castle"/"promotion" based on flags, if needed). Writes the updated board state bytes when complete. +1. Arduino B reads the new board state as bytes, writes it serially from byte array with "state change" and "board state" flags. +1. GUI reads the data from arduino B and writes back with "acknowledge" flag set. +1. GUI updates the visual board state. Nothing needs to be passed on to the engine because the engine was the last to make a move. Waits for another write from the arduino. +1. Arduino A detects a user move with sensors, passes the data to Arduino B, and etc... -[TOC] +### Misc Flow + +- Robot is always black, user always white. +- GUI needs to keep track of which move is made by who in local storage to determine which side is moving/capturing/etc. +- GUI never sends "board state" or "state change" flags or board state bytes to arduino B, always the other way around. +- GUI only actually keeps the data it reads from arduino B if the "state change" or "board state" flags are set. +- User can click button in GUI to ask arduino B for the current board state. + 1. This will cause the GUI to write data serially with the "ask" flag. + 1. Arduino B will read the "ask" flag from the GUI and write the board state bytes serially with the "board state" flag set (but NOT the "state change" flag). + 1. The GUI will read the updated board state and update the visual board state (if needed). It will then write an "acknowledge" flag serially. +- User can click button in GUI to end/reset the game state. + 1. This would make the GUI reset the board state visually and in local storage and close the serial port communication with arduino B. + 1. Arduino A and B would continue reading board state as before and arduino B would continue writing changes serially (the GUI just wouldn't be listening for these changes). + 1. The user would be expected to manually move the pieces back to their original positions instead of the robot arm. +- User can click button in GUI to pause game. + 1. Would work same as ending the game but instead of resetting state, would just disable serial communication but maintain existing state in local storage. + 1. Start button would be changed to "resume" and would just reopen serial communication with existing state when clicked. + +## Data + +GUI sends this to arduino B: `[start word, header, starting position, destination position, ...255 x 6]` (total of 10 bytes). +- ex: `byte arr[10] = {"GO", 0b00000001, 32, 43, 255, 255, 255, 255, 255, 255}`. +- Start word can be anything but should be unique enough so it doesnt accidentally appear in bitmap (does that actually matter though?) +- Header is 1 byte of 8 bits, each bit representing a flag in this order: "ask", "acknowledge"?, "board state", "state change", "promotion", "castle", "capture", "move". + - i.e. `0b00000001` = all flags are off except "move". + - Each flag is 1 = on, 0 = off. + - "ask" = GUI is asking arduino B for board state update + - "acknowledge" = GUI is acknowledging it received "board state" from arduino B. + - "board state" = Not sent from GUI. + - "state change" = Not sent from GUI. + - "promotion" = GUI informs arduino B that a promotion has happened. + - "castle" = GUI informs arduino B that a castle has happened. + - "capture" = GUI informs arduino B that a capture has happened. + - "move" = GUI informs arduino B that a move has happened. + - Header can be expanded to use 2 bytes (so 8 more bits for flags) if needed (this would make total bytes 11 instead of 10). +- Starting position = 0-63. 0 = A1 and 7 = A8, 8 = B1 ... 63 = H8 (white is always on the side with A). +- Destination position = 0-63. 0 = A1 and 7 = A8, 8 = B1 ... 63 = H8 (white is always on the side with A). +- `arr[5]...arr[9]` = 255, where 255 = N/A (because these bytes are only used for "board state" which is not sent from GUI). + +Arduino B sends this to GUI: `[start word, header, ...board state]` (total of 10 bytes). +- ex: `byte arr[10] = {"GO", 0b00100000, 0b11111111, 0b11111111, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11111111, 0b11111111}`. +- Start word same as GUI's probably? Need to confirm if it matters. +- Header is 1 byte of 8 bits, each bit representing a flag in this order: "ask", "acknowledge"?, "board state", "state change", "promotion", "castle", "capture", "move" (same as GUI). + - i.e. `0b00100000` = all flags are off except "board state". + - Each flag is 1 = on, 0 = off. + - "ask" = Not sent from arduino B. + - "acknowledge" = Not sent from arduino B. + - "board state" = Arduino B is sending whole state of board (represented as bits over 8 bytes) to GUI. + - "state change" = Arduino B is notifying GUI that board state has changed (either user or robot moved). + - "promotion" = Not sent from arduino B. + - "castle" = Not sent from arduino B. + - "capture" = Not sent from arduino B. + - "move" = Not sent from arduino B. + +## Code Notes + +- Can use `bitRead()` function in arduino B to check bit flags easily (i.e. `if (bitRead(arr[1], 0) == 1) {}` checks if the flag/bit at position 0 (rightmost bit, in this case "move") is enabled). +- GUI to engine: fastcgi?? + +GUI features to include: +- FEN box (manual fen NOT ALLOWED with custom engine/rookie) +- visual red light/green light indicating connection to arduino (and engine?) +- sidebar area for rating (like chess.com, not lichess -- wants the number to display as well as the visual) (note to self: white is pos num, black is neg num) +- button to export pgn (dont display the textbox like lichess does) +- move list sidebar (note to self: this info is what gets passed to engine, NOT pgn) +- some visual to display pieces that have been captured +- no click, drag, etc. - all moves should be handled programatically (can add toggle to enable/disable moveability) +- Start, reset, pause buttons for connection with arduino. +- buttons to differentiate reset, resign, and force rookie to resign (they will all functionally do the same thing -- cut arduino communication and reset the board -- but will be logged to db differently for indicating who won/lost). should have a confirm modal for these. +- Database to store all games with columns for who won/lost. +- user name option (for storing in database) +- history viewer (low priority) +- stockfish engine support with and without rookie (low priority) +- toggle for engines (stockfish or custom) and toggle for type (robot or gui) (low priority) +- no undo move options +- no time controls with robot (can optionally add when playing in gui but low priority) ## Local Development diff --git a/commitlint.config.js b/commitlint.config.js index 441af52728a29165ba23cc0cbb763638be8f74a1..fcf25be3a2c68a81f3b288279cfbade5643acdef 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,7 +1,5 @@ const config = { - 'extends': [ - '@commitlint/config-conventional', - ], + 'extends': ['@commitlint/config-conventional'], }; export default config; diff --git a/eslint.config.js b/eslint.config.js index f756732db9bc556410b93584e27d0a549be1989d..6d7fffd5e19442cbefb19143bb3cae970395bcc6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,12 +1,13 @@ import {defineConfig, globalIgnores} from 'eslint/config'; import js from '@eslint/js'; +import jsdoc from 'eslint-plugin-jsdoc'; import json from '@eslint/json'; import markdown from '@eslint/markdown'; import vue from 'eslint-plugin-vue'; import stylistic from '@stylistic/eslint-plugin'; import globals from 'globals'; -export default defineConfig([ +const config = defineConfig([ globalIgnores([ '**/*.*', '!**/*.js', @@ -21,35 +22,22 @@ export default defineConfig([ 'releases/', ]), - /** - * JS Files (Node, ESM, Vue, etc.) + /* + * ------------------------------- + * JS FILES (Node, ESM, Vue, etc.) + * ------------------------------- */ - { - files: [ - '**/*.js', - '**/*.mjs', - '**/*.cjs', - '**/*.vue', - ], - languageOptions: { - globals: { - MAIN_WINDOW_VITE_DEV_SERVER_URL: 'readonly', - MAIN_WINDOW_VITE_NAME: 'readonly', - ...globals.browser, - }, - }, - }, { files: [ '*.js', - 'src/main/**/*.js', - 'src/preload/**/*.js', + 'src/server/**/*.js', + 'src/ipc/**/*.js', '*.mjs', - 'src/main/**/*.mjs', - 'src/preload/**/*.mjs', + 'src/server/**/*.mjs', + 'src/ipc/**/*.mjs', '*.cjs', - 'src/main/**/*.cjs', - 'src/preload/**/*.cjs', + 'src/server/**/*.cjs', + 'src/ipc/**/*.cjs', ], languageOptions: { globals: { @@ -62,516 +50,671 @@ export default defineConfig([ '**/*.js', '**/*.mjs', '**/*.cjs', + '**/*.vue', ], + languageOptions: { + globals: { + MAIN_WINDOW_VITE_DEV_SERVER_URL: 'readonly', + MAIN_WINDOW_VITE_NAME: 'readonly', + ...globals.browser, + }, + }, plugins: { js, - '@stylistic': stylistic, + stylistic, + vue, + jsdoc, }, 'extends': [ 'js/recommended', + jsdoc.configs['flat/recommended-error'], + vue.configs['flat/base'], + vue.configs['flat/essential'], ], linterOptions: { reportUnusedDisableDirectives: 'error', reportUnusedInlineConfigs: 'error', }, rules: { - /** - * ---------------------------------------- - * ESLINT RECOMMENDED CONFIG OVERRIDES - * - * Rules initially set by the - * `@eslint/js` plugin's `recommended` - * config and overridden by us. - * ---------------------------------------- - */ - /** * ESLint-specific overrides. * * @see https://eslint.org/docs/latest/rules/ */ - 'array-callback-return': 'error', - 'block-scoped-var': 'error', - camelcase: 'error', - 'capitalized-comments': ['error', 'always', { - ignorePattern: 'webpackChunkName', - }], - complexity: 'error', - curly: 'error', - 'default-case': 'error', - 'default-case-last': 'error', - 'default-param-last': 'error', - 'dot-notation': 'error', - eqeqeq: 'error', - 'func-style': 'error', - 'grouped-accessor-pairs': ['error', 'getBeforeSet'], - 'line-comment-position': 'error', - 'max-classes-per-file': 'error', - 'max-depth': 'error', - 'max-nested-callbacks': 'error', - 'max-params': ['error', 4], - 'new-cap': 'error', - 'no-alert': 'error', - 'no-array-constructor': 'error', - 'no-await-in-loop': 'error', - 'no-bitwise': 'error', - 'no-caller': 'error', - 'no-console': ['error', { - allow: ['error', 'warn'], - }], - 'no-constant-binary-expression': 'error', - 'no-constructor-return': 'error', - 'no-continue': 'error', - 'no-div-regex': 'error', - 'no-duplicate-imports': 'error', - 'no-else-return': 'error', - 'no-empty-function': 'error', - 'no-empty-static-block': 'error', - 'no-eq-null': 'error', - 'no-eval': 'error', - 'no-extend-native': 'error', - 'no-extra-bind': 'error', - 'no-extra-label': 'error', - 'no-implicit-coercion': 'error', - 'no-implicit-globals': 'error', - 'no-implied-eval': 'error', - 'no-iterator': 'error', - 'no-label-var': 'error', - 'no-labels': 'error', - 'no-lone-blocks': 'error', - 'no-lonely-if': 'error', - 'no-loop-func': 'error', - 'no-multi-assign': 'error', - 'no-multi-str': 'error', - 'no-negated-condition': 'error', - 'no-nested-ternary': 'error', - 'no-new': 'error', - 'no-new-func': 'error', - 'no-new-native-nonconstructor': 'error', - 'no-new-wrappers': 'error', - 'no-object-constructor': 'error', - 'no-octal-escape': 'error', - 'no-param-reassign': 'error', - 'no-promise-executor-return': 'error', - 'no-proto': 'error', - 'no-return-assign': 'error', - 'no-script-url': 'error', - 'no-self-compare': 'error', - 'no-sequences': 'error', - 'no-shadow': 'error', - 'no-template-curly-in-string': 'error', - 'no-throw-literal': 'error', - 'no-undef-init': 'error', - 'no-underscore-dangle': 'error', - 'no-unneeded-ternary': 'error', - 'no-unreachable-loop': 'error', - 'no-unused-expressions': 'error', - 'no-unused-private-class-members': 'error', - 'no-use-before-define': 'error', - 'no-useless-call': 'error', - 'no-useless-computed-key': 'error', - 'no-useless-concat': 'error', - 'no-useless-constructor': 'error', - 'no-useless-rename': 'error', - 'no-useless-return': 'error', - 'no-var': 'error', - 'no-void': 'error', - 'object-shorthand': 'error', - 'one-var': ['error', 'never'], - 'operator-assignment': 'error', - 'prefer-arrow-callback': 'error', - 'prefer-const': 'error', - 'prefer-destructuring': 'error', - 'prefer-exponentiation-operator': 'error', - 'prefer-object-has-own': 'error', - 'prefer-object-spread': 'error', - 'prefer-promise-reject-errors': ['error', { - allowEmptyReject: true, - }], - 'prefer-regex-literals': ['error', { - disallowRedundantWrapping: true, - }], - 'prefer-rest-params': 'error', - 'prefer-spread': 'error', - 'prefer-template': 'error', - 'require-atomic-updates': 'error', - 'sort-imports': ['error', { - ignoreCase: true, - ignoreDeclarationSort: true, - allowSeparatedGroups: true, - }], - 'sort-vars': 'error', - strict: ['error', 'never'], - 'symbol-description': 'error', - 'vars-on-top': 'error', - yoda: 'error', + ...{ + 'array-callback-return': 'error', + 'block-scoped-var': 'error', + camelcase: 'error', + 'capitalized-comments': [ + 'error', + 'always', + { + ignorePattern: 'webpackChunkName', + }, + ], + complexity: 'error', + curly: 'error', + 'default-case': 'error', + 'default-case-last': 'error', + 'default-param-last': 'error', + 'dot-notation': 'error', + eqeqeq: 'error', + 'func-style': 'error', + 'grouped-accessor-pairs': ['error', 'getBeforeSet'], + 'line-comment-position': 'error', + 'max-classes-per-file': 'error', + 'max-depth': 'error', + 'max-nested-callbacks': 'error', + 'max-params': ['error', 4], + 'new-cap': 'error', + 'no-alert': 'error', + 'no-array-constructor': 'error', + 'no-await-in-loop': 'error', + 'no-bitwise': 'error', + 'no-caller': 'error', + 'no-console': [ + 'error', + { + allow: ['error', 'warn', 'info'], + }, + ], + 'no-constant-binary-expression': 'error', + 'no-constructor-return': 'error', + 'no-continue': 'error', + 'no-div-regex': 'error', + 'no-duplicate-imports': 'error', + 'no-else-return': 'error', + 'no-empty-function': 'error', + 'no-empty-static-block': 'error', + 'no-eq-null': 'error', + 'no-eval': 'error', + 'no-extend-native': 'error', + 'no-extra-bind': 'error', + 'no-extra-label': 'error', + 'no-implicit-coercion': 'error', + 'no-implicit-globals': 'error', + 'no-implied-eval': 'error', + 'no-iterator': 'error', + 'no-label-var': 'error', + 'no-labels': 'error', + 'no-lone-blocks': 'error', + 'no-lonely-if': 'error', + 'no-loop-func': 'error', + 'no-multi-assign': 'error', + 'no-multi-str': 'error', + 'no-negated-condition': 'error', + 'no-nested-ternary': 'error', + 'no-new': 'error', + 'no-new-func': 'error', + 'no-new-native-nonconstructor': 'error', + 'no-new-wrappers': 'error', + 'no-object-constructor': 'error', + 'no-octal-escape': 'error', + 'no-param-reassign': 'error', + 'no-promise-executor-return': 'error', + 'no-proto': 'error', + 'no-return-assign': 'error', + 'no-script-url': 'error', + 'no-self-compare': 'error', + 'no-sequences': 'error', + 'no-shadow': 'error', + 'no-template-curly-in-string': 'error', + 'no-throw-literal': 'error', + 'no-undef-init': 'error', + 'no-underscore-dangle': 'error', + 'no-unneeded-ternary': 'error', + 'no-unreachable-loop': 'error', + 'no-unused-expressions': 'error', + 'no-unused-private-class-members': 'error', + 'no-unused-vars': [ + 'error', + { + args: 'none', + }, + ], + 'no-use-before-define': 'error', + 'no-useless-call': 'error', + 'no-useless-computed-key': 'error', + 'no-useless-concat': 'error', + 'no-useless-constructor': 'error', + 'no-useless-rename': 'error', + 'no-useless-return': 'error', + 'no-var': 'error', + 'no-void': 'error', + 'object-shorthand': 'error', + 'one-var': ['error', 'never'], + 'operator-assignment': 'error', + 'prefer-arrow-callback': 'error', + 'prefer-const': 'error', + 'prefer-destructuring': 'error', + 'prefer-exponentiation-operator': 'error', + 'prefer-object-has-own': 'error', + 'prefer-object-spread': 'error', + 'prefer-promise-reject-errors': [ + 'error', + { + allowEmptyReject: true, + }, + ], + 'prefer-regex-literals': [ + 'error', + { + disallowRedundantWrapping: true, + }, + ], + 'prefer-rest-params': 'error', + 'prefer-spread': 'error', + 'prefer-template': 'error', + 'require-atomic-updates': [ + 'error', + { + allowProperties: true, + }, + ], + 'sort-imports': [ + 'error', + { + ignoreCase: true, + ignoreDeclarationSort: true, + allowSeparatedGroups: true, + }, + ], + 'sort-vars': 'error', + strict: ['error', 'never'], + 'symbol-description': 'error', + 'vars-on-top': 'error', + yoda: 'error', + }, /** * `@stylistic/eslint-plugin` plugin-specific overrides. * * @see https://eslint.style/rules */ - '@stylistic/array-bracket-newline': ['error', 'consistent'], - '@stylistic/array-bracket-spacing': 'error', - '@stylistic/array-element-newline': ['error', 'consistent'], - '@stylistic/arrow-parens': 'error', - '@stylistic/arrow-spacing': 'error', - '@stylistic/block-spacing': 'error', - '@stylistic/brace-style': 'error', - '@stylistic/comma-dangle': ['error', 'always-multiline'], - '@stylistic/comma-spacing': 'error', - '@stylistic/comma-style': 'error', - '@stylistic/computed-property-spacing': 'error', - '@stylistic/dot-location': ['error', 'property'], - '@stylistic/eol-last': 'error', - '@stylistic/function-call-argument-newline': ['error', 'consistent'], - '@stylistic/function-call-spacing': 'error', - '@stylistic/function-paren-newline': ['error', 'multiline-arguments'], - '@stylistic/generator-star-spacing': ['error', { - before: false, - after: true, - }], - '@stylistic/implicit-arrow-linebreak': 'error', - '@stylistic/indent': ['error', 4, { - SwitchCase: 1, - VariableDeclarator: 'first', - }], - '@stylistic/key-spacing': 'error', - '@stylistic/keyword-spacing': 'error', - '@stylistic/lines-around-comment': ['error', { - beforeBlockComment: true, - afterBlockComment: false, - allowBlockStart: true, - allowBlockEnd: true, - allowObjectStart: true, - allowObjectEnd: false, - allowArrayStart: true, - allowArrayEnd: false, - allowClassStart: true, - allowClassEnd: false, - afterHashbangComment: true, - }], - '@stylistic/lines-between-class-members': 'error', - '@stylistic/max-statements-per-line': 'error', - '@stylistic/multiline-ternary': ['error', 'always-multiline'], - '@stylistic/new-parens': 'error', - '@stylistic/newline-per-chained-call': ['error', { - ignoreChainWithDepth: 4, - }], - '@stylistic/no-confusing-arrow': 'error', - '@stylistic/no-extra-semi': 'error', - '@stylistic/no-floating-decimal': 'error', - '@stylistic/no-mixed-operators': 'error', - '@stylistic/no-mixed-spaces-and-tabs': 'error', - '@stylistic/no-multi-spaces': ['error', { - exceptions: { - Property: false, - }, - }], - '@stylistic/no-multiple-empty-lines': ['error', { - max: 1, - maxEOF: 1, - maxBOF: 0, - }], - '@stylistic/no-tabs': 'error', - '@stylistic/no-trailing-spaces': 'error', - '@stylistic/no-whitespace-before-property': 'error', - '@stylistic/object-curly-newline': ['error', { - ObjectExpression: { - minProperties: 1, - consistent: true, - }, - ObjectPattern: { - consistent: true, - minProperties: 4, - multiline: true, - }, - ImportDeclaration: { - consistent: true, - minProperties: 4, - multiline: true, - }, - ExportDeclaration: { - consistent: true, - minProperties: 1, - }, - }], - '@stylistic/object-curly-spacing': 'error', - '@stylistic/object-property-newline': 'error', - '@stylistic/operator-linebreak': ['error', 'before'], - '@stylistic/padded-blocks': ['error', 'never'], - '@stylistic/padding-line-between-statements': ['error', - { - blankLine: 'always', - prev: '*', - next: [ - 'block', - 'block-like', - 'break', - 'cjs-export', - 'cjs-import', - 'class', - 'const', - 'continue', - 'debugger', - 'directive', - 'do', - 'empty', - 'export', - 'expression', - 'for', - 'function', - 'if', - 'iife', - 'import', - 'let', - 'return', - 'switch', - 'throw', - 'try', - 'var', - 'while', - 'with', - ], - }, - { - blankLine: 'always', - prev: [ - 'block', - 'block-like', - 'break', - 'cjs-export', - 'cjs-import', - 'class', - 'const', - 'continue', - 'debugger', - 'directive', - 'do', - 'empty', - 'export', - 'expression', - 'for', - 'function', - 'if', - 'iife', - 'import', - 'let', - 'return', - 'switch', - 'throw', - 'try', - 'var', - 'while', - 'with', - ], - next: '*', - }, - { - blankLine: 'any', - prev: ['const', 'let', 'var'], - next: ['const', 'let', 'var'], - }, - { - blankLine: 'never', - prev: ['case', 'default'], - next: '*', - }, - { - blankLine: 'any', - prev: 'expression', - next: 'expression', - }, - { - blankLine: 'any', - prev: 'directive', - next: 'directive', - }, - { - blankLine: 'any', - prev: 'import', - next: 'import', - }, - { - blankLine: 'any', - prev: 'cjs-import', - next: 'cjs-import', - }], - '@stylistic/quote-props': ['error', 'as-needed', { - keywords: true, - }], - '@stylistic/quotes': ['error', 'single', { - avoidEscape: true, - }], - '@stylistic/rest-spread-spacing': 'error', - '@stylistic/semi': 'error', - '@stylistic/semi-spacing': 'error', - '@stylistic/semi-style': 'error', - '@stylistic/space-before-blocks': 'error', - '@stylistic/space-before-function-paren': ['error', { - anonymous: 'always', - named: 'never', - asyncArrow: 'always', - }], - '@stylistic/space-in-parens': 'error', - '@stylistic/space-infix-ops': 'error', - '@stylistic/space-unary-ops': ['error', { - words: true, - nonwords: false, - }], - '@stylistic/spaced-comment': ['error', 'always', { - block: { - balanced: true, - }, - }], - '@stylistic/switch-colon-spacing': 'error', - '@stylistic/template-curly-spacing': 'error', - '@stylistic/template-tag-spacing': 'error', - '@stylistic/wrap-iife': ['error', 'inside'], - '@stylistic/wrap-regex': 'error', - '@stylistic/yield-star-spacing': 'error', - }, - }, - { - files: [ - '**/*.vue', - ], - plugins: { - vue, - }, - 'extends': [ - vue.configs['flat/base'], - vue.configs['flat/essential'], - ], - rules: { + ...{ + 'stylistic/array-bracket-newline': [ + 'error', + { + multiline: true, + minItems: null, + }, + ], + 'stylistic/array-bracket-spacing': 'error', + 'stylistic/array-element-newline': [ + 'error', + { + consistent: true, + multiline: true, + minItems: 4, + }, + ], + 'stylistic/arrow-parens': 'error', + 'stylistic/arrow-spacing': 'error', + 'stylistic/block-spacing': 'error', + 'stylistic/brace-style': 'error', + 'stylistic/comma-dangle': ['error', 'always-multiline'], + 'stylistic/comma-spacing': 'error', + 'stylistic/comma-style': 'error', + 'stylistic/computed-property-spacing': 'error', + 'stylistic/dot-location': ['error', 'property'], + 'stylistic/eol-last': 'error', + 'stylistic/function-call-argument-newline': ['error', 'consistent'], + 'stylistic/function-call-spacing': 'error', + 'stylistic/function-paren-newline': ['error', 'multiline-arguments'], + 'stylistic/generator-star-spacing': [ + 'error', + { + before: false, + after: true, + }, + ], + 'stylistic/implicit-arrow-linebreak': 'error', + 'stylistic/indent': [ + 'error', + 4, + { + SwitchCase: 1, + VariableDeclarator: 'first', + }, + ], + 'stylistic/key-spacing': 'error', + 'stylistic/keyword-spacing': 'error', + 'stylistic/lines-around-comment': [ + 'error', + { + beforeBlockComment: true, + afterBlockComment: false, + allowBlockStart: true, + allowBlockEnd: true, + allowObjectStart: true, + allowObjectEnd: false, + allowArrayStart: true, + allowArrayEnd: false, + allowClassStart: true, + allowClassEnd: false, + afterHashbangComment: true, + }, + ], + 'stylistic/lines-between-class-members': 'error', + 'stylistic/max-statements-per-line': 'error', + 'stylistic/multiline-ternary': ['error', 'always-multiline'], + 'stylistic/new-parens': 'error', + 'stylistic/newline-per-chained-call': [ + 'error', + { + ignoreChainWithDepth: 4, + }, + ], + 'stylistic/no-confusing-arrow': 'error', + 'stylistic/no-extra-semi': 'error', + 'stylistic/no-floating-decimal': 'error', + 'stylistic/no-mixed-operators': 'error', + 'stylistic/no-mixed-spaces-and-tabs': 'error', + 'stylistic/no-multi-spaces': [ + 'error', + { + exceptions: { + Property: false, + }, + }, + ], + 'stylistic/no-multiple-empty-lines': [ + 'error', + { + max: 1, + maxEOF: 1, + maxBOF: 0, + }, + ], + 'stylistic/no-tabs': 'error', + 'stylistic/no-trailing-spaces': 'error', + 'stylistic/no-whitespace-before-property': 'error', + 'stylistic/object-curly-newline': [ + 'error', + { + ObjectExpression: { + consistent: true, + minProperties: 1, + }, + ObjectPattern: { + consistent: true, + minProperties: 4, + multiline: true, + }, + ImportDeclaration: { + consistent: true, + minProperties: 4, + multiline: true, + }, + ExportDeclaration: { + consistent: true, + minProperties: 1, + }, + }, + ], + 'stylistic/object-curly-spacing': 'error', + 'stylistic/object-property-newline': 'error', + 'stylistic/operator-linebreak': ['error', 'before'], + 'stylistic/padded-blocks': ['error', 'never'], + 'stylistic/padding-line-between-statements': [ + 'error', + { + blankLine: 'always', + prev: '*', + next: [ + 'block', + 'block-like', + 'break', + 'cjs-export', + 'cjs-import', + 'class', + 'const', + 'continue', + 'debugger', + 'directive', + 'do', + 'empty', + 'export', + 'expression', + 'for', + 'function', + 'if', + 'iife', + 'import', + 'let', + 'return', + 'switch', + 'throw', + 'try', + 'var', + 'while', + 'with', + ], + }, + { + blankLine: 'always', + prev: [ + 'block', + 'block-like', + 'break', + 'cjs-export', + 'cjs-import', + 'class', + 'const', + 'continue', + 'debugger', + 'directive', + 'do', + 'empty', + 'export', + 'expression', + 'for', + 'function', + 'if', + 'iife', + 'import', + 'let', + 'return', + 'switch', + 'throw', + 'try', + 'var', + 'while', + 'with', + ], + next: '*', + }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + { + blankLine: 'never', + prev: ['case', 'default'], + next: '*', + }, + { + blankLine: 'any', + prev: 'expression', + next: 'expression', + }, + { + blankLine: 'any', + prev: 'directive', + next: 'directive', + }, + { + blankLine: 'any', + prev: 'import', + next: 'import', + }, + { + blankLine: 'any', + prev: 'cjs-import', + next: 'cjs-import', + }, + ], + 'stylistic/quote-props': [ + 'error', + 'as-needed', + { + keywords: true, + }, + ], + 'stylistic/quotes': [ + 'error', + 'single', + { + avoidEscape: true, + }, + ], + 'stylistic/rest-spread-spacing': 'error', + 'stylistic/semi': 'error', + 'stylistic/semi-spacing': 'error', + 'stylistic/semi-style': 'error', + 'stylistic/space-before-blocks': 'error', + 'stylistic/space-before-function-paren': [ + 'error', + { + anonymous: 'always', + named: 'never', + asyncArrow: 'always', + }, + ], + 'stylistic/space-in-parens': 'error', + 'stylistic/space-infix-ops': 'error', + 'stylistic/space-unary-ops': [ + 'error', + { + words: true, + nonwords: false, + }, + ], + 'stylistic/spaced-comment': [ + 'error', + 'always', + { + block: { + balanced: true, + }, + }, + ], + 'stylistic/switch-colon-spacing': 'error', + 'stylistic/template-curly-spacing': 'error', + 'stylistic/template-tag-spacing': 'error', + 'stylistic/wrap-iife': ['error', 'inside'], + 'stylistic/wrap-regex': 'error', + 'stylistic/yield-star-spacing': 'error', + }, + /** * `eslint-plugin-vue` plugin-specific overrides. * * @see https://eslint.vuejs.org/rules/ */ - 'vue/array-bracket-newline': [ - 'error', - { - multiline: true, - minItems: 4, - }, - ], - 'vue/array-bracket-spacing': 'error', - 'vue/array-element-newline': [ - 'error', - { - multiline: true, - minItems: 4, - }, - ], - 'vue/arrow-spacing': 'error', - 'vue/block-spacing': 'error', - 'vue/block-tag-newline': [ - 'error', - { - maxEmptyLines: 1, - }, - ], - 'vue/brace-style': 'error', - 'vue/comma-dangle': ['error', 'always-multiline'], - 'vue/comma-spacing': 'error', - 'vue/comma-style': 'error', - 'vue/dot-location': 'error', - 'vue/first-attribute-linebreak': [ - 'error', - { - singleline: 'beside', - }, - ], - 'vue/func-call-spacing': 'error', - 'vue/html-closing-bracket-newline': 'error', - 'vue/html-closing-bracket-spacing': 'error', - 'vue/html-indent': [ - 'error', - 4, - { - attribute: 1, - baseIndent: 1, - closeBracket: 0, - alignAttributesVertically: true, - }, - ], - 'vue/html-quotes': 'error', - 'vue/key-spacing': 'error', - 'vue/keyword-spacing': 'error', - 'vue/max-attributes-per-line': [ - 'error', - { - singleline: { - max: 3, - }, - multiline: { - max: 1, - }, - }, - ], - 'vue/multiline-html-element-content-newline': 'error', - 'vue/multiline-ternary': ['error', 'always-multiline'], - 'vue/mustache-interpolation-spacing': 'error', - 'vue/no-multi-spaces': 'error', - 'vue/object-curly-newline': [ - 'error', - { - ObjectExpression: 'always', - ObjectPattern: { + ...{ + 'vue/array-bracket-newline': [ + 'error', + { multiline: true, - minProperties: 4, + minItems: null, }, - ImportDeclaration: { + ], + 'vue/array-bracket-spacing': 'error', + 'vue/array-element-newline': [ + 'error', + { + consistent: true, multiline: true, - minProperties: 4, + minItems: 4, }, - ExportDeclaration: { - multiline: true, - minProperties: 4, + ], + 'vue/arrow-spacing': 'error', + 'vue/block-spacing': 'error', + 'vue/block-tag-newline': [ + 'error', + { + maxEmptyLines: 1, }, - }, - ], - 'vue/object-curly-spacing': ['error', 'always'], - 'vue/object-property-newline': 'error', - 'vue/operator-linebreak': 'error', - 'vue/quote-props': ['error', 'consistent-as-needed'], - 'vue/script-indent': [ - 'error', - 4, - { - baseIndent: 1, - }, - ], - 'vue/space-in-parens': 'error', - 'vue/space-infix-ops': 'error', - 'vue/space-unary-ops': [ - 'error', - { - words: true, - nonwords: false, - }, - ], - 'vue/template-curly-spacing': 'error', + ], + 'vue/brace-style': 'error', + 'vue/comma-dangle': ['error', 'always-multiline'], + 'vue/comma-spacing': 'error', + 'vue/comma-style': 'error', + 'vue/dot-location': ['error', 'property'], + 'vue/first-attribute-linebreak': [ + 'error', + { + singleline: 'beside', + }, + ], + 'vue/func-call-spacing': 'error', + 'vue/html-closing-bracket-newline': 'error', + 'vue/html-closing-bracket-spacing': 'error', + 'vue/html-indent': [ + 'error', + 4, + { + attribute: 1, + baseIndent: 1, + closeBracket: 0, + alignAttributesVertically: true, + }, + ], + 'vue/html-quotes': 'error', + 'vue/key-spacing': 'error', + 'vue/keyword-spacing': 'error', + 'vue/max-attributes-per-line': [ + 'error', + { + singleline: { + max: 3, + }, + multiline: { + max: 1, + }, + }, + ], + 'vue/multiline-html-element-content-newline': 'error', + 'vue/multiline-ternary': ['error', 'always-multiline'], + 'vue/mustache-interpolation-spacing': 'error', + 'vue/no-multi-spaces': 'error', + 'vue/object-curly-newline': [ + 'error', + { + ObjectExpression: { + consistent: true, + minProperties: 1, + }, + ObjectPattern: { + consistent: true, + minProperties: 4, + multiline: true, + }, + ImportDeclaration: { + consistent: true, + minProperties: 4, + multiline: true, + }, + ExportDeclaration: { + consistent: true, + minProperties: 1, + }, + }, + ], + 'vue/object-curly-spacing': 'error', + 'vue/object-property-newline': 'error', + 'vue/operator-linebreak': ['error', 'before'], + 'vue/quote-props': [ + 'error', + 'as-needed', + { + keywords: true, + }, + ], + 'vue/script-indent': [ + 'error', + 4, + { + baseIndent: 1, + }, + ], + 'vue/space-in-parens': 'error', + 'vue/space-infix-ops': 'error', + 'vue/space-unary-ops': [ + 'error', + { + words: true, + nonwords: false, + }, + ], + 'vue/template-curly-spacing': 'error', + }, + + /** + * `eslint-plugin-jsdoc` plugin-specific overrides. + * + * @see https://github.com/gajus/eslint-plugin-jsdoc#rules + */ + ...{ + 'jsdoc/check-indentation': [ + 'error', + { + excludeTags: ['example'], + }, + ], + 'jsdoc/check-line-alignment': ['error', 'always'], + 'jsdoc/check-param-names': [ + 'error', + { + disableMissingParamChecks: true, + }, + ], + 'jsdoc/informative-docs': 'error', + 'jsdoc/lines-before-block': 'error', + 'jsdoc/no-blank-block-descriptions': 'error', + 'jsdoc/no-blank-blocks': 'error', + 'jsdoc/require-asterisk-prefix': 'error', + 'jsdoc/require-description-complete-sentence': 'error', + 'jsdoc/require-description': [ + 'error', + { + exemptedBy: ['inheritdoc'], + }, + ], + 'jsdoc/require-jsdoc': [ + 'error', + { + checkGetters: false, + checkSetters: false, + enableFixer: false, + require: { + ArrowFunctionExpression: true, + ClassDeclaration: true, + ClassExpression: true, + FunctionExpression: true, + MethodDefinition: true, + }, + contexts: [':not(Property) > FunctionExpression'], + }, + ], + 'jsdoc/sort-tags': [ + 'error', + { + reportIntraTagGroupSpacing: false, + }, + ], + 'jsdoc/tag-lines': [ + 'error', + 'any', + { + applyToEndTag: false, + startLines: 1, + }, + ], + }, + }, + }, + { + files: ['**/*.vue'], + rules: { + 'stylistic/indent': 'off', }, }, - /** - * JSON Files + /* + * ------------------------------- + * JSON FILES + * ------------------------------- */ { - files: [ - '**/*.json', - ], - ignores: [ - 'package-lock.json', - ], + files: ['**/*.json'], + ignores: ['package-lock.json'], plugins: { json, }, - 'extends': [ - 'json/recommended', - ], + 'extends': ['json/recommended'], language: 'json/json', rules: { /** @@ -591,28 +734,24 @@ export default defineConfig([ }, }, { - files: [ - 'package.json', - ], + files: ['package.json'], rules: { 'json/sort-keys': 'off', }, }, - /** - * Markdown Files + /* + * ------------------------------- + * MARKDOWN FILES + * ------------------------------- */ { name: 'md', - files: [ - '**/*.md', - ], + files: ['**/*.md'], plugins: { markdown, }, - 'extends': [ - 'markdown/recommended', - ], + 'extends': ['markdown/recommended'], language: 'markdown/gfm', rules: { /** @@ -623,9 +762,14 @@ export default defineConfig([ 'markdown/no-bare-urls': 'error', 'markdown/no-duplicate-headings': 'error', 'markdown/no-html': 'error', - 'markdown/no-missing-label-refs': ['error', { - allowLabels: ['TOC'], - }], + 'markdown/no-missing-label-refs': [ + 'error', + { + allowLabels: ['TOC'], + }, + ], }, }, ]); + +export default config; diff --git a/forge.config.js b/forge.config.js index edc288409287747979a4a801108588115e146f48..b1fbfc72e3155b434b065e717d7afe76e3183d68 100644 --- a/forge.config.js +++ b/forge.config.js @@ -42,13 +42,13 @@ const config = { // If you are familiar with Vite configuration, it will look really familiar. build: [ { - entry: 'src/main/index.js', - config: 'vite.main.config.js', + entry: 'src/server/index.js', + config: 'vite.server.config.js', target: 'main', }, { - entry: 'src/preload/index.js', - config: 'vite.preload.config.js', + entry: 'src/ipc/index.js', + config: 'vite.ipc.config.js', target: 'preload', }, ], @@ -56,7 +56,7 @@ const config = { { // Entry defined in index.html. name: 'main_window', - config: 'vite.renderer.config.js', + config: 'vite.client.config.js', }, ], concurrent: false, diff --git a/index.html b/index.html index aa27c8b99928a1818b4ff2042307e6afcf00d198..51504901abeefcc51ce598d8190bed8cdfe4ae73 100644 --- a/index.html +++ b/index.html @@ -5,9 +5,8 @@
Welcome to your Electron application.
- + + +