Compare commits

..

498 Commits

Author SHA1 Message Date
jeffvli a19673d3c2 Replace mutation error types with AxiosError 2023-05-09 05:53:57 -07:00
jeffvli 3efeaa7359 Improve multi-server controller 2023-05-09 05:49:05 -07:00
jeffvli 63be8c8fb8 Add authenticate function to controller 2023-05-09 05:48:11 -07:00
jeffvli 975c31635a Remove old API implementation 2023-05-09 05:45:55 -07:00
jeffvli 9b5bce34a0 Fix jellyfin auth endpoint 2023-05-09 05:06:32 -07:00
jeffvli bb27758310 Re-serialize subsonic array params 2023-05-09 05:05:15 -07:00
jeffvli 2d7c52a6b6 Improve UX for edit server form
- Auto focus the password field on edit server form
- Don't disable save button when fields blank
- Add tooltip for modified fields
2023-05-09 02:40:49 -07:00
jeffvli cbb15ac7ee Fix various issues 2023-05-09 02:25:57 -07:00
jeffvli b2db2b27da Refactor server list to object instead of array
- Improve performance due to frequency of accessing the list
2023-05-09 00:39:11 -07:00
jeffvli 3dfeed1432 Invalidate playlist list on creation 2023-05-08 03:35:51 -07:00
jeffvli 2101f1e9a7 Fix legacy normalizations 2023-05-08 03:35:23 -07:00
jeffvli 8a0a8e4d54 Refactor jellyfin api with ts-rest/axios 2023-05-08 03:34:15 -07:00
jeffvli a9ca3f9083 Add additional undefined check for custom filters 2023-05-08 03:33:38 -07:00
jeffvli 6d5e10a31c Add albumCount and songCount to genre 2023-05-08 02:42:38 -07:00
jeffvli 5b616d5928 Update initial list store filters 2023-04-30 22:53:11 -07:00
jeffvli 62670964c0 Add menu in error boundary 2023-04-30 22:05:06 -07:00
jeffvli 314bd766df Refactor all api instances in components 2023-04-30 22:01:52 -07:00
jeffvli bdd023fde3 Refactor remaining queries/mutations for new controller 2023-04-30 18:00:50 -07:00
jeffvli 40aabd2217 Additional refactor for navidrome api controller types 2023-04-30 17:55:23 -07:00
jeffvli b9d5447b4f Allow serverId to be undefined 2023-04-27 22:20:35 -07:00
jeffvli 68a1cb9aaa Refactor all mutation hooks 2023-04-27 21:44:25 -07:00
jeffvli bf3024939a Refactor all query hooks 2023-04-27 21:25:57 -07:00
jeffvli df9464f762 Additional refactor to api and types 2023-04-27 20:34:28 -07:00
jeffvli 17cf624f6a Add generic query/mutation types 2023-04-27 20:32:56 -07:00
jeffvli 8f042ad448 Pass full server to controller 2023-04-25 16:25:26 -07:00
jeffvli 1cbd61888f Refactor server list as hash table 2023-04-25 01:36:26 -07:00
jeffvli 2ce49fc54e Add new server api to main controller 2023-04-24 01:22:58 -07:00
jeffvli bec328f1f4 Add Subsonic API and types 2023-04-24 01:21:29 -07:00
jeffvli ea8c63b71b Add new navidrome api controller 2023-04-23 19:57:10 -07:00
jeffvli 52049ce163 Add missing elements from Navidrome API 2023-04-23 19:54:36 -07:00
jeffvli 70c62c8b52 Refactor api client to support dynamic server 2023-04-23 14:26:41 -07:00
jeffvli fa79b4cbe0 Fix artist path 2023-04-23 14:25:09 -07:00
jeffvli 438085633b Modify navidrome responses to include header 2023-04-23 02:09:48 -07:00
jeffvli fe043d1823 Add function to modify base response 2023-04-23 02:09:25 -07:00
jeffvli 9bd12df8f6 Add navidrome API and types 2023-04-23 01:39:47 -07:00
jeffvli 637d420e1c Add ts-rest and axios 2023-04-23 01:25:16 -07:00
jeffvli c593b7bc46 Fix slider styles to account for transparent thumb (#85) 2023-04-20 01:54:51 -07:00
jeffvli 5e90139b17 Fix styles from mantine upgrade 2023-04-20 01:47:42 -07:00
jeffvli ed86d8ffd2 Bump mantine to v6.0.8 2023-04-20 01:45:27 -07:00
jeffvli bcaaaac586 Set auto-update as default 2023-04-03 18:26:56 -07:00
jeffvli a614a6ff9c Bump electron builder 2023-04-03 05:31:37 -07:00
jeffvli 08a1cb1ae9 Update lockfiles 2023-04-03 04:38:02 -07:00
jeffvli 75e93b8ea2 Fix linux build 2 2023-04-03 04:30:31 -07:00
jeffvli 5d28abae91 Fix linux build 2023-04-03 04:26:47 -07:00
jeffvli c483cdb871 Add optional chain to mpvPlayer to fix web 2023-04-03 04:26:00 -07:00
jeffvli fe14ca25c6 Remove mpris-service from renderer deps 2023-04-03 04:15:20 -07:00
jeffvli 0965efab21 Increase default sidebar width 2023-04-03 04:13:56 -07:00
jeffvli 598b627bb4 Supress errors from main process 2023-04-03 04:13:41 -07:00
jeffvli b8614495f6 Bump to 0.0.1-alpha6 2023-04-03 03:53:30 -07:00
jeffvli e02643123c Fix various mpv setting options 2023-04-03 03:53:30 -07:00
jeffvli 5d8cad06d7 Fix filters on album list detail 2023-04-03 03:53:30 -07:00
jeffvli 2b8a97b8c2 Remove modal overflow 2023-04-03 03:45:08 -07:00
jeffvli 44cd1b33bf Adjust light theme 2023-04-03 03:44:13 -07:00
jeffvli 90b503906f Set grid view to use local state 2023-04-03 03:42:51 -07:00
jeffvli 77bfb916ba MPV player enhancements
- start the player from the renderer
- dynamically modify settings without restart
2023-04-02 21:41:32 -07:00
jeffvli f35152a169 Add hotkey settings tab 2023-03-31 07:26:10 -07:00
jeffvli 0d9224bc09 Style fixes 2023-03-31 06:22:04 -07:00
jeffvli cf4f80c82b Split linux build to ubuntu runner (#57) 2023-03-31 06:15:04 -07:00
jeffvli fa717b9bca Downgrade electron 23 -> 22
- To support windows native deps (nodert)
2023-03-31 06:15:04 -07:00
jeffvli 8e2dce34f0 Change default card display to POSTER 2023-03-31 06:15:04 -07:00
jeffvli dce6fc7e7d Fix macOS window bar color 2023-03-31 06:15:04 -07:00
jeffvli 781e3c3c4d Add app version to settings page 2023-03-31 06:15:04 -07:00
jeffvli 0d4d5b5de0 Add reset to default for settings 2023-03-31 06:15:04 -07:00
jeffvli 293d8ec584 Add setting to disable auto update 2023-03-31 06:15:04 -07:00
jeffvli 6ccef6e515 Prevent auto checking of audio devices 2023-03-31 06:15:04 -07:00
jeffvli 5f7b005626 Refactor layout components 2023-03-31 06:14:59 -07:00
jeffvli b590636303 Fix invalid import 2023-03-31 05:06:54 -07:00
jeffvli a17e0adf44 Prevent header play button from being squished 2023-03-30 08:22:40 -07:00
jeffvli 2b1c1d5e59 Add tray settings (#49) 2023-03-30 08:09:20 -07:00
jeffvli eecbcddea3 Refactor settings store and components 2023-03-30 06:44:33 -07:00
jeffvli 373441e4c6 Adjust shadow on playerbar image 2023-03-30 05:02:58 -07:00
jeffvli dbcda5b634 Fix grid layout for web window bar 2023-03-30 04:57:51 -07:00
jeffvli bc5f1f13f0 Move settings to route instead of modal 2023-03-30 03:01:31 -07:00
jeffvli 0c13b09029 Fix window controls when sidebar queue enabled (#36) 2023-03-29 20:39:59 -07:00
jeffvli 4ffc544e87 Remove unused preload items 2023-03-29 20:38:37 -07:00
jeffvli cf00992d71 Fix song repeating when disabled (#55) 2023-03-29 18:17:56 -07:00
jeffvli 3848e9840d Add additional fix to song list header play button (#28) 2023-03-29 15:07:43 -07:00
jeffvli 930bbb33fd Rename titlebar to windowbar 2023-03-29 14:54:10 -07:00
jeffvli 4332a9ea3a Improve sidebar playlist resize performance 2023-03-29 14:27:25 -07:00
jeffvli ccfe0bfd9d Prevent titlebar drag when using windowbar 2023-03-29 01:19:02 -07:00
jeffvli f5fc56eee1 Remove boilerplate issue templates 2023-03-29 00:52:27 -07:00
jeffvli cd6bf25011 Prevent second app instance (#62) 2023-03-29 00:40:29 -07:00
jeffvli 335287d119 Decrease size of play button 2023-03-29 00:31:32 -07:00
jeffvli 50af8f4d3a Split sidebar action bar to separate component 2023-03-29 00:31:09 -07:00
jeffvli 58c7370536 Add dedicated OS window bars (#22) 2023-03-28 23:59:51 -07:00
jeffvli ececc394e2 Fix filled button styles 2023-03-28 16:13:18 -07:00
jeffvli 219a9ed613 Change grid size to items per row 2023-03-28 15:37:50 -07:00
Jeff e47fcfc62e Add fullscreen player view (#27)
* Add store controls for fullscreen player

* Normalize styles for playback config

* Add fullscreen player component

* Add option component

* Update player controls to use option/popover components

* Add esc hotkey to close player

* Add usePlayerData hook
2023-03-28 14:19:23 -07:00
ssnarf 6cfdb8ff84 Fixes #51. Update titleCombined datatype. (#59) 2023-03-28 14:15:51 -07:00
jeffvli ef4cdfa028 Set artist links to use outline button 2023-03-09 18:16:57 -08:00
jeffvli 1eed26abab Set genres to use outline button 2023-03-09 18:14:40 -08:00
jeffvli a2851dd700 Use generic for play button 2023-03-09 18:10:27 -08:00
jeffvli 563db1138e Fix list store for artist detail 2023-03-09 18:09:59 -08:00
jeffvli 84587da701 Add additional vars to base components 2023-03-09 18:08:15 -08:00
jeffvli f0a836fc1f Fix loading skeleton for poster card 2023-03-09 16:37:54 -08:00
jeffvli 5539e2cd4e Adjust playerbar background 2023-03-09 13:41:59 -08:00
jeffvli 30b013dfa5 Decrease gap between grid items 2023-03-09 13:41:41 -08:00
jeffvli 8343f4f80b Fix typo on mpv params placeholder 2023-03-09 13:34:39 -08:00
jeffvli e8dcba0456 Add pointer-events to grid card components
- Prevent delay on hover event
2023-03-09 13:23:36 -08:00
jeffvli 27cbc23d87 Set default mpv gapless-audio config to weak (#45) 2023-03-09 12:51:30 -08:00
jeffvli 275d68ec5b Fix mpv stopping after first playback
- On startup, the first time a song is played, mpv will stop after playback
- This adds a loop to the queue handler to automatically retry when failing to add to the queue
2023-03-09 12:45:13 -08:00
jeffvli 7f9de4b180 Fix transition props 2023-03-09 10:59:29 -08:00
jeffvli 231f10cbe2 Allow adding server without password (#48) 2023-03-09 10:45:44 -08:00
jeffvli b4664f45b4 Adjust default grid sizing and handler 2023-03-09 02:36:23 -08:00
jeffvli 3153cdd6c4 Auto scale grid items (#30) 2023-03-09 02:26:09 -08:00
jeffvli 69292a083d Fix web volume handler (#35) 2023-03-09 01:40:08 -08:00
jeffvli 123478a24f Normalize album artist list store 2023-03-05 21:02:05 -08:00
jeffvli 828cca9c19 Fix playlist pagination 2023-03-05 19:31:28 -08:00
jeffvli f7740407c3 Migrate transition props 2023-03-05 18:49:38 -08:00
jeffvli 157ac9f3a2 Keep playlist store separate 2023-03-05 18:47:24 -08:00
jeffvli f21c9010ac Darken default playerbar 2023-03-05 18:38:22 -08:00
jeffvli 7c045e5b23 Bump to mantine 6 stable 2023-03-05 18:38:22 -08:00
jeffvli ae292e3a5f Begin normalizing list stores 2023-03-05 18:38:22 -08:00
jeffvli 918b77eebb Adjust default dropdown styling 2023-03-05 18:38:22 -08:00
Adam 661751f306 Fix playback being interrupted by clicking maximize. #39 (#42) 2023-03-03 18:23:59 -08:00
jeffvli 2260caba00 Fix preview URLs 2023-02-28 03:02:32 -08:00
jeffvli 3fe0873dc1 Add preview images to README 2023-02-28 03:01:45 -08:00
jeffvli 7c6ec73617 Update README to add features and remove deprecated information 2023-02-28 02:45:18 -08:00
jeffvli 76dcd1c28e Bump to mantine v6 alpha 7 2023-02-27 19:39:29 -08:00
jeffvli 4fb1f4d2cb Bump to electron v23 2023-02-27 19:12:03 -08:00
jeffvli 92039b95c3 Fix types on top song request 2023-02-27 12:44:25 -08:00
jeffvli c0a703be7a Add top song list for jellyfin 2023-02-27 12:17:22 -08:00
jeffvli f08538cbfb Remove electronmon from default 2023-02-25 19:02:02 -08:00
jeffvli 1fa975ccec Clean up unused wrapper component 2023-02-25 19:01:42 -08:00
jeffvli ac62c26099 Fix type 2023-02-25 18:31:51 -08:00
Jeff 7ae3d9d99a Fix list view breaking on undefined rating value (#32) 2023-02-25 16:35:19 -08:00
Jeff a9cfcaeda6 Fix artist song list play behavior (#29) 2023-02-22 12:22:39 -08:00
jeffvli 3d8b25922e Fix date picker props 2023-02-11 00:21:39 -08:00
jeffvli a9089859ce Fix radius for last item in context menu 2023-02-11 00:21:39 -08:00
Jeff c878e36015 Ignore CORS & SSL (#23)
* Add toggle to ignore CORS

* Add option to ignore SSL
2023-02-10 11:53:26 -08:00
jeffvli 8eec6b6b8a Bump to version 0.0.1-alpha5 2023-02-09 00:37:28 -08:00
jeffvli 60219c2522 Minor player adjustments 2023-02-09 00:36:55 -08:00
jeffvli cdb5cdf442 Fix sizing of drawer queue, add border 2023-02-09 00:18:25 -08:00
Jeff 23f84d68e8 Add MPRIS support (#25)
* Stop mpv on app close for linux/macOS (#20)

* Add initial MPRIS support

* Fix mpv path check
2023-02-08 23:57:06 -08:00
jeffvli 0f7f4b969f Fix drawer border radius 2023-02-08 17:00:07 -08:00
jeffvli 563a4b3a7c Add button to open browser devtools 2023-02-08 14:42:13 -08:00
jeffvli 4700772e6d Add padding for dropdown label 2023-02-08 14:39:59 -08:00
jeffvli ffb7f915c3 Add context menu to queue items 2023-02-08 11:46:39 -08:00
jeffvli 17d5ef1f6b Use flex instead of grid for context menu item 2023-02-08 11:46:02 -08:00
jeffvli 9dcc42ff28 Add border radius for all dropdown items (#22) 2023-02-08 11:45:29 -08:00
jeffvli 2845476d83 Migrate sidebar playlist to react-window 2023-02-08 03:44:37 -08:00
jeffvli 147b155d60 Add hook for hideable scrollbar 2023-02-08 03:44:05 -08:00
jeffvli 8b5e463546 Remove tanstack/react-virtual package 2023-02-08 03:43:18 -08:00
jeffvli 822dcd8429 Fix error on paginated table persistence 2023-02-08 10:05:10 -08:00
Jeff 9f2e873366 Redesign sidebar / header and other misc. improvements (#24)
* Remove 1920px max width

* Fix position of list controls menu

* Match size and color of search input

* Adjust library header sizing

* Move app menu to sidebar

* Increase row buffer on play queue list

* Fix query builder styles

* Fix playerbar slider track bg

* Adjust titlebar styles

* Fix invalid modal prop

* Various adjustments to detail pages

* Fix sidebar height calculation

* Fix list null indicators, add filter indicator

* Adjust playqueue styles

* Fix jellyfin releaseYear normalization

* Suppress browser context menu on ag-grid

* Add radius to drawer queue -- normalize layout

* Add modal styles to provider theme

* Fix playlist song list pagination

* Add disc number to albums with more than one disc

* Fix query builder boolean values

* Adjust input placeholder color

* Properly handle rating/favorite from context menu on table

* Conform dropdown menu styles to context menu

* Increase sort type select width

* Fix drawer queue radius

* Change primary color

* Prevent volume wheel from invalid values

* Add icons to query builder dropdowns

* Update notification styles

* Update scrollbar thumb styles

* Remove "add to playlist" on smart playlists

* Fix "add to playlist" from context menu
2023-02-07 22:47:23 -08:00
jeffvli d2c0d4c11f Fix modal styles for mantine v6 2023-02-06 02:21:11 -08:00
jeffvli 48b6e8bf93 Remove box shadow from filter header 2023-02-06 02:17:47 -08:00
jeffvli 17a6b37545 Fix active tab color 2023-02-06 02:17:25 -08:00
jeffvli eedcef8f52 Remove custom image component for grid images 2023-02-06 01:57:49 -08:00
jeffvli 757eddd6f1 Fix disabled input styles for mantine v6 2023-02-06 01:52:07 -08:00
jeffvli 13f48711a9 Use local seekvalue to smooth out slider drag 2023-02-06 01:45:56 -08:00
jeffvli 1bbdf09dcc Add padding to playlist list items 2023-02-05 23:18:42 -08:00
jeffvli 3b7c6ce25e Set transparent window control background 2023-02-05 23:13:44 -08:00
jeffvli 38118e74ae Update to new list header style 2023-02-05 22:41:47 -08:00
jeffvli 6872a7e8b2 Adjust various base components 2023-02-05 20:52:25 -08:00
jeffvli ab3236230b Use virtualized list on sidebar playlists 2023-02-05 18:59:39 -08:00
jeffvli 6ef88e56ec Adjust scrollarea to add styles and omit header 2023-02-05 18:02:27 -08:00
jeffvli 18c18ea322 Bump packages 2023-02-05 17:59:37 -08:00
Jeff 22fec8f9d3 Add ratings support (#21)
* Update rating types for multiserver support

* Add rating mutation

* Add rating support to table views

* Add rating support on playerbar

* Add hovercard component

* Handle rating from context menu

- Improve context menu components
- Allow left / right icons
- Allow nested menus

* Add selected item count

* Fix context menu auto direction

* Add transition and move portal for context menu

* Re-use context menu for all item dropdowns

* Add ratings to detail pages / double click to clear

* Bump react-query package
2023-02-05 05:19:01 -08:00
jeffvli f50ec5cf31 Fix error boundary styles 2023-01-30 21:34:56 -08:00
jeffvli 4cbc28a087 Add volume wheel scroll & new slider component 2023-01-30 21:34:27 -08:00
jeffvli 01fdd25406 Remove react-slider dependency 2023-01-30 21:28:37 -08:00
jeffvli 320f583660 Fix misc. styles 2023-01-30 20:16:43 -08:00
jeffvli 8cc5ec6797 Fix workflow name for PR binary comment 2023-01-30 20:03:18 -08:00
jeffvli edfbc2538d Set PR publish to upload binaries separately by OS 2023-01-30 20:03:08 -08:00
Jeff 484c96187c Add scrobble functionality (#19)
* Fix slider bar background to use theme

* Add "scrobbleAtDuration" to settings store

* Add subscribeWithSelector and playCount incrementor

* Add scrobbling API and mutation

* Add scrobble settings

* Begin support for multi-server queue handling

* Dynamically set version on auth header

* Add scrobbling functionality for navidrome/jellyfin
2023-01-30 20:01:57 -08:00
jeffvli 85bf910d65 Add additional controls to playerbar 2023-01-30 02:39:25 -08:00
jeffvli 5ddd0872ef Adjust various styles 2023-01-30 01:36:36 -08:00
jeffvli 2700774469 Fix player button styles 2023-01-30 01:05:23 -08:00
jeffvli c79a992829 Increase text size of table cells 2023-01-30 01:05:02 -08:00
jeffvli 5e693313d8 Adjust context menu styles 2023-01-30 01:04:38 -08:00
Jeff 59f4f43e84 Add ability to add/remove songs from playlist (#17)
* Add api for add/remove playlist items

* Add playlistItemId property to normalized Song

- This is used for Navidrome to delete songs from playlists

* Add mutations for add/remove from playlist

* Add context modal for playlist add

* Add remove from playlist from context menu

* Set jellyfin to use playlistItemId

* Adjust font sizing

* Add playlist add from detail pages

* Bump mantine to v6-alpha.2
2023-01-29 18:40:26 -08:00
jeffvli be39c2bc1f Add pr package script 2023-01-29 18:27:14 -08:00
jeffvli b49ba2d04c Fix pr build 2023-01-29 18:10:39 -08:00
jeffvli d7c87efe10 Add PR build workflows 2023-01-28 21:06:41 -08:00
Jeff 44a4b88809 Migrate to mantine v6 (#15)
* Add letter spacing to cell text

* Set window control height in px

* Add temp unused routes

* Migrate text title font weights

* Bump mantine to v6 alpha

* Migrate modals / notifications

* Increase header bar to 65px

* Adjust play button props

* Migrate various components

* Migrate various pages and root styles

* Adjust default badge padding

* Fix sidebar spacing

* Fix list header badges

* Adjust default theme
2023-01-28 20:46:07 -08:00
jeffvli 768269f074 Bump to v0.0.1-alpha4 2023-01-15 22:22:22 -08:00
jeffvli d23ae2a8db Replace default navidrome artist image placeholder path 2023-01-15 22:08:50 -08:00
jeffvli 88591697a2 Set route button to uppercase for consistency 2023-01-15 22:01:56 -08:00
jeffvli dcbb00f7c4 Remove filters button from album artist list 2023-01-15 21:58:50 -08:00
jeffvli e063ee0c29 Add smart playlist builder to create playlist form 2023-01-15 21:58:25 -08:00
jeffvli e5f478218e Forward playlist query filters 2023-01-15 21:57:44 -08:00
jeffvli 9a809a61dd Fix query not setting properly on render after save 2023-01-15 21:20:11 -08:00
jeffvli 4058ab7491 Change default smart playlist sort to album asc 2023-01-15 21:15:35 -08:00
jeffvli 48ccebd4c2 Clean up persisted list state when switching servers 2023-01-15 21:10:06 -08:00
jeffvli fcf00b9de1 Use native img on table images 2023-01-15 21:03:24 -08:00
jeffvli f7919b296b Change modal bg color 2023-01-15 20:48:34 -08:00
jeffvli 0b6af1fd21 Add additional padding to context menu items 2023-01-15 20:46:45 -08:00
jeffvli 3f424b72f6 Remove scroll persistence when viewing artist songs 2023-01-15 20:41:58 -08:00
jeffvli fc9d4616ba Adjust max height for settings modal 2023-01-15 20:40:23 -08:00
jeffvli 2e74f7533a Reuse song list for artist songs 2023-01-15 20:39:43 -08:00
jeffvli 784da2f8b9 Adjust context menu styles 2023-01-15 16:38:38 -08:00
jeffvli dbc29568ca Fix table params 2023-01-15 16:34:30 -08:00
jeffvli 5614ad54f2 Add view artist discography 2023-01-15 16:22:07 -08:00
jeffvli 67523f1e7b Adjust spacing between sections 2023-01-15 16:16:13 -08:00
jeffvli fc57605219 Select router type based on desktop/web 2023-01-15 16:11:26 -08:00
jeffvli a31a2ffdbf Increase page transition animation speed from 0.8 -> 0.5 2023-01-15 16:10:00 -08:00
jeffvli 900d47d6f9 Fix various types 2023-01-15 16:07:02 -08:00
jeffvli 6bdf0736ec Add genres to album detail page 2023-01-13 14:07:57 -08:00
jeffvli 92cde507d9 Add artist top songs list 2023-01-13 13:51:19 -08:00
jeffvli 8afd626806 Adjust filters 2023-01-13 01:54:35 -08:00
jeffvli 53f3758d2a Set static width for drawer queue 2023-01-13 01:53:34 -08:00
jeffvli 1e6eb33408 Link to album artist page instead of artist 2023-01-13 01:50:48 -08:00
jeffvli 51e20a81b7 Add artist song list page 2023-01-13 01:44:47 -08:00
jeffvli 4e8dc868bb Add routes to artist song list / discography 2023-01-13 01:09:34 -08:00
jeffvli 9b8bcb05bd Add initial album artist detail route 2023-01-12 18:43:25 -08:00
jeffvli 55e2a9bf37 Fix delimiter color 2023-01-12 18:41:41 -08:00
jeffvli a82b087969 Add itemtype and optional pagination for carousel 2023-01-12 13:31:25 -08:00
jeffvli 45aef104fe Update album artist base route 2023-01-12 12:45:44 -08:00
jeffvli 6746903808 Increase white ignore threshold 2023-01-12 12:28:19 -08:00
jeffvli 36c1f4e736 Set text children to optional 2023-01-12 00:44:33 -08:00
jeffvli b0ca7ab127 Use updated cardrows component 2023-01-12 00:43:39 -08:00
jeffvli 2026bc8f48 Add react-virtual package 2023-01-08 21:11:47 -08:00
jeffvli 6da8663a1d Fix card row array id assignment 2023-01-08 20:55:36 -08:00
jeffvli b4e9f48667 Add prop to force transparent grid header 2023-01-08 20:47:05 -08:00
jeffvli d58ba92cbd Add customizable scrollbar offset on scrollarea 2023-01-08 20:46:20 -08:00
jeffvli c51194cd03 Update album artist detail endpoints 2023-01-08 20:45:38 -08:00
jeffvli ba0ec909c8 Fix list header wrap on item count 2023-01-08 02:47:38 -08:00
jeffvli 0db1c36d86 Fix ND recently added sort 2023-01-08 02:39:35 -08:00
jeffvli 3e9fb521f0 Adjust sticky header to align with sidebar 2023-01-08 02:18:05 -08:00
jeffvli b5ad30a9bc Fix header being squished by table 2023-01-08 02:13:39 -08:00
jeffvli 5344493845 Fix mis-imported components 2023-01-08 02:08:19 -08:00
jeffvli 6f2108940e Adjust fixed table header 2023-01-08 02:04:07 -08:00
jeffvli 4f3e732891 Fix positioning of page header 2023-01-08 02:01:12 -08:00
jeffvli 0e2678575a Add favorite functionality to album detail 2023-01-08 01:45:56 -08:00
jeffvli d6035a5f97 Transform default number field value
- Navidrome allows for "string" number query values which will not work with the numberinput
2023-01-08 01:07:17 -08:00
jeffvli b82a5eda78 Fix undefined type for favorites 2023-01-08 00:53:30 -08:00
jeffvli d17f30f5e6 Add favorite handler to grid cards 2023-01-08 00:52:53 -08:00
jeffvli 7a3bdb531d Readd missing scroll restoration 2023-01-08 00:08:26 -08:00
jeffvli c6f3b49a6e Increase width of duration cell 2023-01-07 23:45:40 -08:00
jeffvli 99cd48ca6d Adjust color and size of favorite cell 2023-01-07 23:44:53 -08:00
jeffvli 02ed9b7a5c Move ag-grid header margin to specific components 2023-01-07 23:37:33 -08:00
jeffvli 0f66687843 Misc. optimizations 2023-01-07 23:09:58 -08:00
jeffvli 586f42867d Fix lodash import 2023-01-07 18:25:36 -08:00
jeffvli 14a7f0254d Move some ag-grid styles back to theme file
- Styles were not applying properly from the global
2023-01-07 18:23:49 -08:00
jeffvli 0aa0e51daa Add favoriting from context menu 2023-01-07 18:23:10 -08:00
jeffvli f4f06abd72 Use global libraryitem type on favorite query 2023-01-07 18:21:54 -08:00
jeffvli 7d8cb0bb45 Refactor context menu handler into hook 2023-01-07 18:16:19 -08:00
jeffvli 2edffa02d0 Add favoriting from table rows 2023-01-07 16:33:14 -08:00
jeffvli cfa4e5e45c Update favorite/rating endpoints
- Refactor subsonic api endpoints to set the default auth params
- The beforeRequest hook is unable to dynamically set existing params
2023-01-07 16:09:40 -08:00
jeffvli f879171398 Add renderer for last played date column 2023-01-07 14:30:17 -08:00
jeffvli 6bfebd2923 Adjust position of track/disc number cells 2023-01-07 04:09:32 -08:00
jeffvli 3c60f406ea Adjust ag-grid styles 2023-01-07 03:50:34 -08:00
jeffvli af1c16ee51 Adjust dropdown styling 2023-01-07 03:49:56 -08:00
jeffvli 1b25d88692 Rename add to queue text 2023-01-07 03:49:18 -08:00
jeffvli 8a48abbbc8 Additional changes to column defaults 2023-01-07 03:30:48 -08:00
jeffvli 6deab38c67 Add undefined check on scrollTo 2023-01-07 03:28:28 -08:00
jeffvli 915b0eb372 Add play handlers and item count to list pages 2023-01-07 03:28:03 -08:00
jeffvli 6bb0474d62 Change ND ALBUM_SONGS sort value 2023-01-07 03:27:13 -08:00
jeffvli 2df96c0d31 Add song filter as add queue type 2023-01-07 03:26:18 -08:00
jeffvli d94d7b5ee5 Export selector for list filters 2023-01-07 03:21:03 -08:00
jeffvli 2f7f6bead9 Remove persisted playqueue in localstorage
- This will break if there are enough songs in the queue. A new implementation will be added in the future
2023-01-07 01:42:20 -08:00
jeffvli 3bbddcf092 Move grid-specific styling out of theme styles 2023-01-06 23:52:05 -08:00
jeffvli 838c6a8b6a Adjust titlebar / window controls styling 2023-01-06 23:51:26 -08:00
jeffvli 5889b8976c Force header bg color in detail lists 2023-01-06 23:34:31 -08:00
jeffvli d06ddc9560 Update themes 2023-01-06 23:33:18 -08:00
jeffvli 9b1f4e7154 Add infinite table defaults 2023-01-06 18:24:31 -08:00
jeffvli b569ec31ae Move common table functions into base component 2023-01-06 17:59:02 -08:00
jeffvli f7b8e34905 Improve semantic html of default layout and add ids 2023-01-06 14:49:41 -08:00
jeffvli 3cf7127f56 Optimize current song image layout transition 2023-01-06 14:27:31 -08:00
jeffvli cb823d94e5 Add dynamic message for infinite scroll handler 2023-01-06 13:53:02 -08:00
jeffvli 4d0620c5df Update misc on detail pages 2023-01-06 13:50:40 -08:00
jeffvli 01371d0227 Add pageIndex on infinite query results
- Result selector runs on every page on every fetch, which means that the uniqueId is not immutable. We need a static index on each item since a playlist can have duplicate song ids
2023-01-06 13:48:29 -08:00
jeffvli 9e6a81cb62 Use rem instead of px for sticky header 2023-01-06 13:34:41 -08:00
jeffvli 7b616b44fa Add prop to deselect rows on outside click 2023-01-06 13:28:10 -08:00
jeffvli 4c275ea878 Add sticky table header to detail pages 2023-01-06 11:46:17 -08:00
jeffvli b59c86f78f Add hook to fix table header to detail header 2023-01-06 11:44:50 -08:00
jeffvli 14e6b4e7d6 Change sidebar icons 2023-01-06 10:46:09 -08:00
jeffvli 63cdefcb27 Increase white ignore threshold 2023-01-06 10:29:49 -08:00
jeffvli 11f9721abe Reduce global query cache time from 15min to 3min 2023-01-06 03:33:43 -08:00
jeffvli 0a82438beb Set 1 minute cache time on manual query fetches 2023-01-06 03:33:11 -08:00
jeffvli 8bd1cc80bc Fix rating property on card row 2023-01-06 03:32:35 -08:00
jeffvli 624b1bb94d Add tooltip delay for expand/collapse of player image 2023-01-06 01:59:37 -08:00
jeffvli cf904b5d51 Set outline color for rating component 2023-01-06 01:52:01 -08:00
jeffvli 2ab48f5c97 Set size/color of custom table headers 2023-01-06 01:51:29 -08:00
jeffvli d56799e519 Adjust sidebar style 2023-01-06 01:48:56 -08:00
jeffvli 083e219ed2 Improve average color matching
- Add threshold to ignored white/black values
- Add ignored transparency colors
2023-01-06 01:05:54 -08:00
jeffvli 1b379882f5 Reduce size of song imageUrl
- Increase efficiency of loading times for song lists
2023-01-06 01:03:58 -08:00
jeffvli ab031820f6 Add favorite/rating table columns 2023-01-06 00:39:49 -08:00
jeffvli d1dfbaedaa Move LibraryItem type 2023-01-05 21:59:07 -08:00
jeffvli 3070586104 Improve default table column/header defaults 2023-01-05 20:33:12 -08:00
jeffvli bcfb9dbec3 Fix various api types 2023-01-05 20:32:02 -08:00
jeffvli 888bab50c9 Adjust title line height to better fit 2023-01-05 20:14:46 -08:00
jeffvli 715ee0fa3f Reduce font-weight of table header 2023-01-05 19:57:21 -08:00
jeffvli 1963e93d2e Export non-polymorphic text component 2023-01-05 19:54:59 -08:00
jeffvli ad3728a55d Prevent blank screen on grid-view render
- Add loading prop to before rendering to check for itemCount
2023-01-05 11:03:24 -08:00
jeffvli df4f05b14c Finalize base features for smart playlist editor 2023-01-05 02:27:29 -08:00
jeffvli 0c7a0cc88a Add danger prop for menu items 2023-01-04 23:56:09 -08:00
jeffvli 98ef0b44ec Add error boundaries to individual routes 2023-01-04 22:38:27 -08:00
jeffvli 24f06db2b8 Add playlist "save as" form 2023-01-04 18:37:25 -08:00
jeffvli d63e5f5784 Add owner to playlist update query
- Support smart playlist rules
- Add user list query
2023-01-04 18:33:49 -08:00
jeffvli 75ef43dffb Add initial nd smart playlist ui 2023-01-04 15:54:25 -08:00
jeffvli 65974dbf28 temp 2023-01-04 04:09:24 -08:00
jeffvli 16433457ad Use global state for grid card views
- Prevent re-render when fetching already cached state
2023-01-03 17:41:03 -08:00
jeffvli 19eaf44394 Fix header link route 2023-01-03 17:28:41 -08:00
jeffvli 861fcec14f Change playlist detail bg calc algorithm 2023-01-03 11:48:02 -08:00
jeffvli 72cbd23089 Increase default sidebar width 2023-01-03 11:44:20 -08:00
jeffvli 1048431742 Bump to v0.0.1-alpha3 2023-01-03 03:29:14 -08:00
jeffvli 633c9f59d9 Add update playlist for jellyfin 2023-01-03 03:25:21 -08:00
jeffvli 0ed13c75af Fix stale state on playqueue when switching server 2023-01-03 03:16:53 -08:00
jeffvli b0bc4c3cf3 Wait for load before setting background color 2023-01-03 03:15:51 -08:00
jeffvli b8b8ca9f66 Add separate filter for album song list fetch 2023-01-03 03:15:09 -08:00
jeffvli f2e6a418b0 Add fallback to average color calculation 2023-01-03 02:28:59 -08:00
jeffvli 7fef7e4689 Adjust sidebar theme 2023-01-03 02:28:40 -08:00
jeffvli 21bf995335 Move toast notifications to bottom-center 2023-01-03 02:28:10 -08:00
jeffvli bd13fb63ae Add window reload on first server add
- Fixes controller server type
2023-01-03 02:27:28 -08:00
jeffvli 67ccc20147 Adjust duration normalization to ms 2023-01-03 02:27:00 -08:00
jeffvli 83991cf5a1 Remove placeholder 2023-01-03 02:26:43 -08:00
jeffvli dfb0ff42b3 Navigate home after switching servers 2023-01-03 02:13:40 -08:00
jeffvli 008c12626d Add play controls to playlist song list 2023-01-03 02:13:21 -08:00
jeffvli 19e3f435c4 Fix add from card 2023-01-03 02:13:04 -08:00
jeffvli ac6242ea94 Navigate to home if no issues resolved 2023-01-03 02:00:21 -08:00
jeffvli b87d7778df Remove image placeholders (performance issues?) 2023-01-03 01:49:01 -08:00
jeffvli acb906aad9 Remove sidebar play button, increase fw for labels 2023-01-03 01:48:07 -08:00
jeffvli 196cb1bd48 Fix scroll area display type 2023-01-03 01:34:18 -08:00
jeffvli 3981ad3eb5 Adjust sidebar playlist styles 2023-01-03 01:34:00 -08:00
jeffvli d54131b34a Remove console logs 2023-01-03 00:51:24 -08:00
jeffvli 6ad6617d88 Add delete playlist to context menu 2023-01-03 00:50:09 -08:00
jeffvli 52163534db Add update/delete playlist forms 2023-01-03 00:28:09 -08:00
jeffvli 5dd65b18b7 Add description property to playlist 2023-01-03 00:27:11 -08:00
jeffvli 1e77e1074a Add loading/disabled props for confirm modal 2023-01-03 00:19:33 -08:00
jeffvli 9537309fe2 Add custom confirm modal component 2023-01-03 00:12:07 -08:00
jeffvli 4dc8920ff4 Set overlay opacity based on theme 2023-01-02 18:54:48 -08:00
jeffvli 26e6f479b7 Implement new header on home page 2023-01-02 18:20:45 -08:00
jeffvli d93e6a612e Lighten overlay on header bg
- Increase support for light styles
2023-01-02 18:20:17 -08:00
jeffvli 0baa6f4488 Adjust header styles 2023-01-02 18:17:06 -08:00
jeffvli 6490118741 Fix ellipsis overflow styles 2023-01-02 18:14:44 -08:00
jeffvli 58827a1dcf Set header target to optional 2023-01-02 18:08:19 -08:00
jeffvli a3804808b4 Update album/playlist headers with shared styles 2023-01-02 17:57:49 -08:00
jeffvli d49bba42ef Bump framer motion to v8 2023-01-02 17:57:24 -08:00
jeffvli c56f6a355d Add duration string util 2023-01-02 17:56:09 -08:00
jeffvli 7b13e24ce4 Calculate duration playlist duration in ms 2023-01-02 17:55:50 -08:00
jeffvli 088f1d0f99 Adjust title style 2023-01-02 17:55:14 -08:00
jeffvli d7d611c6d1 Add missing overflow style value 2023-01-02 17:53:27 -08:00
jeffvli 65465d6cae Support dynamic page headers 2023-01-02 17:03:33 -08:00
jeffvli 3d8ba2e808 Add native scroll area component 2023-01-02 16:59:21 -08:00
jeffvli 152be5d7e6 Add library detail header component 2023-01-02 03:47:05 -08:00
jeffvli 4326f6cf91 Various cleanup 2023-01-02 02:05:30 -08:00
jeffvli 90dec929f4 Add playlist detail page 2023-01-02 02:04:23 -08:00
jeffvli d6dc880ef4 Add playlist image to type 2023-01-02 01:58:31 -08:00
jeffvli d0e2a798fe Account for playlist items in cover art url 2023-01-01 15:05:26 -08:00
jeffvli fecaa2e6b8 Use song-specific cover and add placeholder (#6) 2023-01-01 14:16:57 -08:00
jeffvli cdbd3f8c7b Remove dynamic queue header color 2023-01-01 14:04:16 -08:00
jeffvli b037329377 Handle jellyfin playlist creation 2023-01-01 14:02:03 -08:00
jeffvli 8b04f70106 Add dedicated playlist song list page 2023-01-01 13:58:05 -08:00
jeffvli 737a05e2c5 Update pagination
- Support id pages
- Set proper list max
2023-01-01 03:16:27 -08:00
jeffvli 78a30c2db4 Add ND playlist song type 2022-12-31 20:08:39 -08:00
jeffvli 5cef23944f Add playlist queue handler 2022-12-31 20:07:44 -08:00
jeffvli aa1cd742ad Move play queue handler to context 2022-12-31 19:26:58 -08:00
jeffvli 0f364f7c5c Add initial playlist detail page 2022-12-31 18:03:26 -08:00
jeffvli 11be5c811f Use size props for play button 2022-12-31 17:50:22 -08:00
jeffvli 6174dc128d Adjust base page headers 2022-12-31 17:50:05 -08:00
jeffvli 81455602ef Forward scrollarea ref 2022-12-31 16:50:20 -08:00
jeffvli d6936634db Update querykeys 2022-12-31 12:43:32 -08:00
jeffvli 88f53c17db Add create/update playlist mutations and form 2022-12-31 12:40:11 -08:00
jeffvli 82f107d835 Fix store name 2022-12-31 04:03:05 -08:00
jeffvli 1fee4c1946 Restore scroll on infinite lists 2022-12-31 04:02:47 -08:00
jeffvli ec79d91d30 Add playlist list 2022-12-31 03:46:12 -08:00
jeffvli 00a21269dd Set default color to undefined 2022-12-31 03:41:18 -08:00
jeffvli 58ed2f3706 Wait for background color before rendering content 2022-12-31 03:16:05 -08:00
jeffvli 0a9dcf36b9 Use prop for scrollbar width 2022-12-31 03:15:11 -08:00
jeffvli dc1e728a2e Increase minimum width from 200 -> 225 2022-12-31 01:00:51 -08:00
jeffvli 085a3856e0 Add search param to album artist list 2022-12-30 22:54:00 -08:00
jeffvli a693981333 Add query key to custom query options 2022-12-30 22:35:49 -08:00
jeffvli 2a797bd6c9 Add genre filter to navidrome song list 2022-12-30 22:34:59 -08:00
jeffvli 4a64f5fe9b Add play on double click for song list rows 2022-12-30 21:31:35 -08:00
jeffvli 1f232fa4da Add card placeholder images 2022-12-30 21:31:13 -08:00
jeffvli b3d95f765c Add page key for album detail page
- Fixes animation render when switching between detail pages
2022-12-30 21:12:27 -08:00
jeffvli f298e60929 Fix context menu add 2022-12-30 21:11:35 -08:00
jeffvli 4745c4a42d Add card/table types for album artists 2022-12-30 21:11:09 -08:00
jeffvli 6fddea552d Change default font to poppins 2022-12-30 21:04:30 -08:00
jeffvli 24af17b8fe Add album artist list route 2022-12-30 21:04:06 -08:00
jeffvli 185175aa89 Handle album artist play 2022-12-30 21:02:17 -08:00
jeffvli 38a4e1b749 Bump version to alpha2 2022-12-29 20:20:25 -08:00
jeffvli 5929360fd8 Add placeholder icon 2022-12-29 20:07:45 -08:00
jeffvli 25c96f2b18 Fix queue song color 2022-12-29 19:34:21 -08:00
jeffvli 93960d4605 Fix restart function 2022-12-29 19:30:55 -08:00
jeffvli 73fd57cf33 Remove unused 2022-12-29 19:25:37 -08:00
jeffvli 5ff89aff8e Fix textarea placeholder color 2022-12-29 19:25:22 -08:00
jeffvli b3b17013bf Adjust theme primary color 2022-12-29 19:25:11 -08:00
jeffvli bd9cbea9b7 Adjust play button styles 2022-12-29 19:23:07 -08:00
jeffvli 8f5115b9c6 Remove border 2022-12-29 19:14:07 -08:00
jeffvli 5aae7180e7 Navigate to album detail page on double click 2022-12-29 19:06:53 -08:00
jeffvli e7ccee4634 Clean trailing slash on server url 2022-12-29 19:06:29 -08:00
jeffvli 2e42e134e4 Fix title spacing / display name 2022-12-29 18:52:37 -08:00
jeffvli be52f61fdb Fix various issues 2022-12-29 18:50:57 -08:00
jeffvli 46a23318af Remove recently played carousel for jellyfin 2022-12-29 18:46:09 -08:00
jeffvli 1d82c84c9e Add initial album detail page 2022-12-29 18:45:01 -08:00
jeffvli b2f9c73300 Add additional song list column defaults 2022-12-29 18:41:35 -08:00
jeffvli f2e03266c2 Export play button behavior in its own hook 2022-12-29 18:36:55 -08:00
jeffvli d5435686bf Add error catch when fetching for playqueue 2022-12-29 18:29:24 -08:00
jeffvli d5ccf64bda Fix id type of play queue add from card 2022-12-29 18:28:56 -08:00
jeffvli a24816ad6d Fix error if data id udnefined 2022-12-29 17:52:40 -08:00
jeffvli 35c4f85085 Set dynamic height on feature carousel 2022-12-29 17:52:11 -08:00
jeffvli 8dd920b294 Remove unused import 2022-12-29 17:12:36 -08:00
jeffvli 3c86e6e28c Adjust style of context menu items 2022-12-29 17:11:51 -08:00
jeffvli f4ba82531c Add album detail api 2022-12-29 17:11:25 -08:00
jeffvli dc6936b22c Add shared items
- Play button
- Play types
2022-12-29 17:07:39 -08:00
jeffvli 131d7c5e3b Add dividers option to context menu 2022-12-29 17:03:49 -08:00
jeffvli a423a45352 Rename play to 'add to queue' 2022-12-29 17:03:02 -08:00
jeffvli 3ff46ce724 Add hook for average color 2022-12-29 16:50:05 -08:00
jeffvli e50c59c903 Add missing font files for Poppins 2022-12-29 16:48:39 -08:00
jeffvli 28c4646708 Preserve row order of selected context menu items 2022-12-28 19:50:35 -08:00
jeffvli 131e3b3c65 Add album list context menu
- Fix stale selected value when selecting single row with right click
2022-12-28 19:19:51 -08:00
jeffvli 9836d548a6 Accept string of ids by itemtype in playqueue add 2022-12-28 19:19:05 -08:00
jeffvli 694969cf41 Fix drag region name 2022-12-28 19:18:37 -08:00
jeffvli c86b452c90 Support fetching song list by album id 2022-12-28 19:17:55 -08:00
jeffvli b39d11c0cc Add song list context menu 2022-12-28 15:32:50 -08:00
jeffvli 4d5e4082bb Add base context menu provider/component 2022-12-28 15:32:02 -08:00
jeffvli 8ebe882236 Add 'byData' as playqueue add option 2022-12-28 15:31:04 -08:00
jeffvli 0edba7e222 Remove organize imports 2022-12-28 01:58:40 -08:00
jeffvli 3c39bdaa29 Adjust sidebar styles 2022-12-28 01:58:25 -08:00
jeffvli 04275a272b Adjust default themes 2022-12-28 01:57:59 -08:00
jeffvli 5c479e96ad Use initialWidth for persisted width 2022-12-28 01:45:08 -08:00
jeffvli b967c8cb19 Add album table view 2022-12-28 01:44:49 -08:00
jeffvli e5ad41b9da Adjust playqueue styles 2022-12-28 01:29:37 -08:00
jeffvli 7500046ac6 Misc fixes to song list page 2022-12-28 01:23:54 -08:00
jeffvli b9a03fc412 Add persisted width on colDef generator 2022-12-28 00:41:34 -08:00
jeffvli 552ad1b2a6 Adjust table pagination responsiveness 2022-12-28 00:39:36 -08:00
jeffvli 728b177e7a Adjust list search handler
- Prevent re-render when search value does not change
2022-12-27 14:22:57 -08:00
jeffvli 363f597e17 Remove unused packages 2022-12-27 14:22:57 -08:00
jeffvli b57ec3c966 Adjust pagination style 2022-12-27 14:22:57 -08:00
jeffvli 026b1f6ec2 Fix styles on control section 2022-12-27 14:22:57 -08:00
jeffvli 6bcc984b44 Bump packages 2022-12-27 14:22:57 -08:00
jeffvli 8a42a1bc6c Add song list functionality 2022-12-27 14:22:57 -08:00
jeffvli c7f588539d Update song list queries 2022-12-27 14:22:57 -08:00
jeffvli 2b0d4c44a6 Update table component 2022-12-27 14:22:54 -08:00
jeffvli d1c038ea6f Add additional song types 2022-12-27 02:00:39 -08:00
jeffvli e94783820e Add base motion containers 2022-12-26 16:43:37 -08:00
jeffvli 9841fa3c63 Use URLSearchParams for parser 2022-12-26 05:16:57 -08:00
jeffvli df5eba629a Change default md breakpoint 2022-12-26 05:16:57 -08:00
jeffvli feb4839ccd Decrease width of hidden icon 2022-12-26 05:16:57 -08:00
jeffvli d2ab8b470d Sort genre list in alphabetical order 2022-12-26 05:16:57 -08:00
jeffvli 926d7f714e Adjust album list header/filters 2022-12-26 05:16:57 -08:00
jeffvli 5ed06f79b3 Change type name, add paginated table 2022-12-26 05:16:52 -08:00
jeffvli 1883164150 Add prop to hide pagination dividers 2022-12-26 05:16:51 -08:00
jeffvli 69a10f4677 Fix number input button styles 2022-12-26 05:16:51 -08:00
jeffvli c673218a8b Add table pagination component 2022-12-26 05:16:51 -08:00
jeffvli d1507604f2 Add pagination component 2022-12-26 05:16:51 -08:00
jeffvli be3cc74e5d Add organizeImports to save action 2022-12-26 05:16:51 -08:00
jeffvli a8243c476c Allow deselect of music folder 2022-12-26 05:16:51 -08:00
jeffvli df9aad36c4 Add cell image placeholder 2022-12-26 05:16:51 -08:00
jeffvli 3b769233cf Use subtle color for search icon 2022-12-26 05:16:51 -08:00
jeffvli e08ffcf20d Set theme border on popover 2022-12-26 05:16:51 -08:00
jeffvli 84bdb30b57 Adjust default theme 2022-12-26 05:16:51 -08:00
jeffvli c10a4a9fd3 Add card row for releasedate 2022-12-26 05:16:51 -08:00
jeffvli 7cc3adedc4 Add responsive styles to header 2022-12-26 05:16:46 -08:00
jeffvli 7f62b583af Adjust card shadow 2022-12-25 01:59:00 -08:00
jeffvli 4353e81df3 Add checks for player type 2022-12-25 01:55:00 -08:00
jeffvli ae5afe868e Fix web player data 2022-12-25 01:26:01 -08:00
jeffvli 1fbdfe725c Add conditional for preload functions 2022-12-25 01:25:46 -08:00
jeffvli 4614358163 Force quit mpv on app close (#4) 2022-12-25 00:56:16 -08:00
jeffvli b6fd3a4f66 Adjust cache/stale times for other home items 2022-12-24 21:15:56 -08:00
jeffvli 58d557e4e8 Set consistent random carousel items when paging
- Set stale/cache time to 1 minute
2022-12-24 21:13:08 -08:00
jeffvli 6d037e4760 Fix order for recently played and recently added 2022-12-24 21:09:46 -08:00
jeffvli d203d287df Set title text for header 2022-12-24 21:07:20 -08:00
jeffvli 2c8057df70 Increase page transition duration
- Some pages benefit from having the extra load time to finish loading
2022-12-24 21:06:23 -08:00
jeffvli bc15bee747 Adjust modal style
- Decrease opacity
- Remove overlay blur
2022-12-24 21:05:16 -08:00
jeffvli 74b513f31a Adjust style of floating queue
- Increase transition speed
- Set max width
2022-12-24 21:03:18 -08:00
jeffvli ad3cfb01ce Fix refetch on search change 2022-12-24 20:49:59 -08:00
jeffvli 2e8d033614 Return updated filters on change 2022-12-24 20:20:30 -08:00
jeffvli 39e2212d1d Fix refetch on filter change 2022-12-24 20:20:17 -08:00
jeffvli 26ea4c0cc9 Add ref controls to infinite grid 2022-12-24 20:19:56 -08:00
jeffvli 520b7ce136 Fix favorited defaultOrder 2022-12-24 18:16:44 -08:00
jeffvli d011c29ce8 Add stricter loading check for cells 2022-12-24 18:12:49 -08:00
jeffvli ed12d59564 Adjust style for popover 2022-12-24 17:54:00 -08:00
jeffvli 8699bba067 Adjust style for active menu item 2022-12-24 14:10:34 -08:00
jeffvli f786b6f01b Add dynamic card rows based on sort type
- Split album list content into separate component
2022-12-24 14:09:24 -08:00
jeffvli 7d1083d1f7 Move card rows to separate component 2022-12-24 14:09:24 -08:00
jeffvli 6eb08243b7 Add generic to CardRow type
squash cardrow type

squash cardrow type
2022-12-24 14:09:02 -08:00
jeffvli 747633fb25 Add lastPlayedAt to album type 2022-12-24 13:32:27 -08:00
jeffvli a4584ecd5c Adjust base component styles 2022-12-22 01:59:02 -08:00
jeffvli 57c34637cf Add server-specific album filters 2022-12-22 01:58:11 -08:00
jeffvli 223cf469f4 Add genre list query 2022-12-22 01:56:59 -08:00
jeffvli b4301486f3 Add escape handler 2022-12-21 01:37:12 -08:00
jeffvli 226fea2c6d Fix imports 2022-12-21 01:29:00 -08:00
jeffvli 3dc3d6fe28 Set default context to empty object 2022-12-21 01:29:00 -08:00
jeffvli ae3c331061 Increase default header height 2022-12-21 01:29:00 -08:00
jeffvli 19f55b4a2e Add album list search 2022-12-21 01:29:00 -08:00
jeffvli b742b814c0 Split stores 2022-12-21 01:29:00 -08:00
jeffvli 2a858f3107 Add key for carousel artist items 2022-12-21 01:29:00 -08:00
jeffvli 9100b6197a Set current time on local state for prev 2022-12-21 01:29:00 -08:00
jeffvli a354cab797 Add music folders query 2022-12-21 01:28:59 -08:00
jeffvli 3399fc6bf6 Fix imports 2022-12-21 01:28:59 -08:00
jeffvli 45ecdadae2 Fix dependency cycle on imports 2022-12-21 01:28:59 -08:00
jeffvli 39a114aad9 Add loading skeleton to table cell rows 2022-12-21 01:28:59 -08:00
jeffvli a147b56485 Update lint rules 2022-12-21 01:28:59 -08:00
jeffvli 33f1e8d70a Fix skeleton animation loop 2022-12-21 01:28:59 -08:00
jeffvli 6685bfe9d3 Use relative imports on main 2022-12-21 01:28:59 -08:00
jeffvli e0883e7eb0 Fix toast dependency cycle 2022-12-21 01:28:59 -08:00
jeffvli c858479d57 Redo queue handler as hook 2022-12-21 01:28:59 -08:00
jeffvli 3dd9e620a8 Change default fonts 2022-12-21 01:28:56 -08:00
jeffvli 3c889d87ef Adjust settings defaults 2022-12-19 17:44:58 -08:00
jeffvli 93c6d046ee Restore MPV check 2022-12-19 17:44:49 -08:00
jeffvli b0cd967ae6 Adjust styles and remove subsonic server option 2022-12-19 17:44:48 -08:00
jeffvli 2445b334eb Fix display of action required route 2022-12-19 17:44:47 -08:00
jeffvli 325bf54abe Remove server files 2022-12-19 17:44:45 -08:00
jeffvli e87c814068 Add files 2022-12-19 17:44:40 -08:00
444 changed files with 71622 additions and 8354 deletions
+7
View File
@@ -0,0 +1,7 @@
{
"rules": {
"no-console": "off",
"global-require": "off",
"import/no-dynamic-require": "off"
}
}
+59
View File
@@ -0,0 +1,59 @@
/**
* Base webpack config used across other specific configs
*/
import webpack from 'webpack';
import { dependencies as externals } from '../../release/app/package.json';
import webpackPaths from './webpack.paths';
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
const configuration: webpack.Configuration = {
externals: [...Object.keys(externals || {})],
module: {
rules: [
{
exclude: /node_modules/,
test: /\.[jt]sx?$/,
use: {
loader: 'ts-loader',
options: {
// Remove this line to enable type checking in webpack builds
transpileOnly: true,
},
},
},
],
},
output: {
// https://github.com/webpack/webpack/issues/1114
library: {
type: 'commonjs2',
},
path: webpackPaths.srcPath,
},
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
}),
],
/**
* Determine the array of extensions that should be used to resolve modules.
*/
resolve: {
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
fallback: {
child_process: false,
},
plugins: [new TsconfigPathsPlugin({ baseUrl: webpackPaths.srcPath })],
modules: [webpackPaths.srcPath, 'node_modules'],
},
stats: 'errors-only',
};
export default configuration;
+3
View File
@@ -0,0 +1,3 @@
/* eslint import/no-unresolved: off, import/no-self-import: off */
module.exports = require('./webpack.config.renderer.dev').default;
+84
View File
@@ -0,0 +1,84 @@
/**
* Webpack config for production electron main process
*/
import path from 'path';
import TerserPlugin from 'terser-webpack-plugin';
import webpack from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import deleteSourceMaps from '../scripts/delete-source-maps';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
checkNodeEnv('production');
deleteSourceMaps();
const devtoolsConfig =
process.env.DEBUG_PROD === 'true'
? {
devtool: 'source-map',
}
: {};
const configuration: webpack.Configuration = {
...devtoolsConfig,
mode: 'production',
target: 'electron-main',
entry: {
main: path.join(webpackPaths.srcMainPath, 'main.ts'),
preload: path.join(webpackPaths.srcMainPath, 'preload.ts'),
},
output: {
path: webpackPaths.distMainPath,
filename: '[name].js',
},
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
}),
],
},
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
}),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
DEBUG_PROD: false,
START_MINIMIZED: false,
}),
],
/**
* Disables webpack processing of __dirname and __filename.
* If you run the bundle in node.js it falls back to these values of node.js.
* https://github.com/webpack/webpack/issues/2010
*/
node: {
__dirname: false,
__filename: false,
},
};
export default merge(baseConfig, configuration);
@@ -0,0 +1,70 @@
import path from 'path';
import webpack from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
checkNodeEnv('development');
}
const configuration: webpack.Configuration = {
devtool: 'inline-source-map',
mode: 'development',
target: 'electron-preload',
entry: path.join(webpackPaths.srcMainPath, 'preload.ts'),
output: {
path: webpackPaths.dllPath,
filename: 'preload.js',
},
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
}),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*
* By default, use 'development' as NODE_ENV. This can be overriden with
* 'staging', for example, by changing the ENV variables in the npm scripts
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
],
/**
* Disables webpack processing of __dirname and __filename.
* If you run the bundle in node.js it falls back to these values of node.js.
* https://github.com/webpack/webpack/issues/2010
*/
node: {
__dirname: false,
__filename: false,
},
watch: true,
};
export default merge(baseConfig, configuration);
@@ -0,0 +1,79 @@
/**
* Builds the DLL for development electron renderer process
*/
import path from 'path';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import { dependencies } from '../../package.json';
import checkNodeEnv from '../scripts/check-node-env';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
checkNodeEnv('development');
const dist = webpackPaths.dllPath;
const configuration: webpack.Configuration = {
context: webpackPaths.rootPath,
devtool: 'eval',
mode: 'development',
target: 'electron-renderer',
externals: ['fsevents', 'crypto-browserify'],
/**
* Use `module` from `webpack.config.renderer.dev.js`
*/
module: require('./webpack.config.renderer.dev').default.module,
entry: {
renderer: Object.keys(dependencies || {}),
},
output: {
path: dist,
filename: '[name].dev.dll.js',
library: {
name: 'renderer',
type: 'var',
},
},
plugins: [
new webpack.DllPlugin({
path: path.join(dist, '[name].json'),
name: '[name]',
}),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new webpack.LoaderOptionsPlugin({
debug: true,
options: {
context: webpackPaths.srcPath,
output: {
path: webpackPaths.dllPath,
},
},
}),
],
};
export default merge(baseConfig, configuration);
+186
View File
@@ -0,0 +1,186 @@
import 'webpack-dev-server';
import { execSync, spawn } from 'child_process';
import fs from 'fs';
import path from 'path';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import chalk from 'chalk';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
checkNodeEnv('development');
}
const port = process.env.PORT || 4343;
const manifest = path.resolve(webpackPaths.dllPath, 'renderer.json');
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const requiredByDLLConfig = module.parent!.filename.includes('webpack.config.renderer.dev.dll');
/**
* Warn if the DLL is not built
*/
if (!requiredByDLLConfig && !(fs.existsSync(webpackPaths.dllPath) && fs.existsSync(manifest))) {
console.log(
chalk.black.bgYellow.bold(
'The DLL files are missing. Sit back while we build them for you with "npm run build-dll"',
),
);
execSync('npm run postinstall');
}
const configuration: webpack.Configuration = {
devtool: 'inline-source-map',
mode: 'development',
target: ['web', 'electron-renderer'],
entry: [
`webpack-dev-server/client?http://localhost:${port}/dist`,
'webpack/hot/only-dev-server',
path.join(webpackPaths.srcRendererPath, 'index.tsx'),
],
output: {
path: webpackPaths.distRendererPath,
publicPath: '/',
filename: 'renderer.dev.js',
library: {
type: 'umd',
},
},
module: {
rules: [
{
test: /\.s?css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
},
},
'sass-loader',
],
include: /\.module\.s?(c|a)ss$/,
},
{
test: /\.s?css$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
exclude: /\.module\.s?(c|a)ss$/,
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
// Images
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
plugins: [
...(requiredByDLLConfig
? []
: [
new webpack.DllReferencePlugin({
context: webpackPaths.dllPath,
manifest: require(manifest),
sourceType: 'var',
}),
]),
new webpack.NoEmitOnErrorsPlugin(),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*
* By default, use 'development' as NODE_ENV. This can be overriden with
* 'staging', for example, by changing the ENV variables in the npm scripts
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
new ReactRefreshWebpackPlugin(),
new HtmlWebpackPlugin({
filename: path.join('index.html'),
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: false,
env: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV !== 'production',
nodeModules: webpackPaths.appNodeModulesPath,
}),
],
node: {
__dirname: false,
__filename: false,
},
devServer: {
port,
compress: true,
hot: true,
headers: { 'Access-Control-Allow-Origin': '*' },
static: {
publicPath: '/',
},
historyApiFallback: {
verbose: true,
},
setupMiddlewares(middlewares) {
console.log('Starting preload.js builder...');
const preloadProcess = spawn('npm', ['run', 'start:preload'], {
shell: true,
stdio: 'inherit',
})
.on('close', (code: number) => process.exit(code!))
.on('error', (spawnError) => console.error(spawnError));
console.log('Starting Main Process...');
spawn('npm', ['run', 'start:main'], {
shell: true,
stdio: 'inherit',
})
.on('close', (code: number) => {
preloadProcess.kill();
process.exit(code!);
})
.on('error', (spawnError) => console.error(spawnError));
return middlewares;
},
},
};
export default merge(baseConfig, configuration);
@@ -0,0 +1,131 @@
/**
* Build config for electron renderer process
*/
import path from 'path';
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import TerserPlugin from 'terser-webpack-plugin';
import webpack from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import deleteSourceMaps from '../scripts/delete-source-maps';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
checkNodeEnv('production');
deleteSourceMaps();
const devtoolsConfig =
process.env.DEBUG_PROD === 'true'
? {
devtool: 'source-map',
}
: {};
const configuration: webpack.Configuration = {
...devtoolsConfig,
mode: 'production',
target: ['web', 'electron-renderer'],
entry: [path.join(webpackPaths.srcRendererPath, 'index.tsx')],
output: {
path: webpackPaths.distRendererPath,
publicPath: './',
filename: 'renderer.js',
library: {
type: 'umd',
},
},
module: {
rules: [
{
test: /\.s?(a|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
},
},
'sass-loader',
],
include: /\.module\.s?(c|a)ss$/,
},
{
test: /\.s?(a|c)ss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
exclude: /\.module\.s?(c|a)ss$/,
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
// Images
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
}),
new CssMinimizerPlugin(),
],
},
plugins: [
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
DEBUG_PROD: false,
}),
new MiniCssExtractPlugin({
filename: 'style.css',
}),
new BundleAnalyzerPlugin({
analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: false,
isDevelopment: process.env.NODE_ENV !== 'production',
}),
],
};
export default merge(baseConfig, configuration);
+140
View File
@@ -0,0 +1,140 @@
import 'webpack-dev-server';
import path from 'path';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
checkNodeEnv('development');
}
const port = process.env.PORT || 4343;
const configuration: webpack.Configuration = {
devtool: 'inline-source-map',
mode: 'development',
target: ['web', 'electron-renderer'],
entry: [
`webpack-dev-server/client?http://localhost:${port}/dist`,
'webpack/hot/only-dev-server',
path.join(webpackPaths.srcRendererPath, 'index.tsx'),
],
output: {
path: webpackPaths.distRendererPath,
publicPath: '/',
filename: 'renderer.dev.js',
library: {
type: 'umd',
},
},
module: {
rules: [
{
test: /\.s?css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
},
},
'sass-loader',
],
include: /\.module\.s?(c|a)ss$/,
},
{
test: /\.s?css$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
exclude: /\.module\.s?(c|a)ss$/,
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
// Images
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*
* By default, use 'development' as NODE_ENV. This can be overriden with
* 'staging', for example, by changing the ENV variables in the npm scripts
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
new ReactRefreshWebpackPlugin(),
new HtmlWebpackPlugin({
filename: path.join('index.html'),
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: false,
env: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV !== 'production',
nodeModules: webpackPaths.appNodeModulesPath,
}),
],
node: {
__dirname: false,
__filename: false,
},
devServer: {
port,
compress: true,
hot: true,
headers: { 'Access-Control-Allow-Origin': '*' },
static: {
publicPath: '/',
},
historyApiFallback: {
verbose: true,
},
setupMiddlewares(middlewares) {
return middlewares;
},
},
};
export default merge(baseConfig, configuration);
+38
View File
@@ -0,0 +1,38 @@
const path = require('path');
const rootPath = path.join(__dirname, '../..');
const dllPath = path.join(__dirname, '../dll');
const srcPath = path.join(rootPath, 'src');
const srcMainPath = path.join(srcPath, 'main');
const srcRendererPath = path.join(srcPath, 'renderer');
const releasePath = path.join(rootPath, 'release');
const appPath = path.join(releasePath, 'app');
const appPackagePath = path.join(appPath, 'package.json');
const appNodeModulesPath = path.join(appPath, 'node_modules');
const srcNodeModulesPath = path.join(srcPath, 'node_modules');
const distPath = path.join(appPath, 'dist');
const distMainPath = path.join(distPath, 'main');
const distRendererPath = path.join(distPath, 'renderer');
const buildPath = path.join(releasePath, 'build');
export default {
rootPath,
dllPath,
srcPath,
srcMainPath,
srcRendererPath,
releasePath,
appPath,
appPackagePath,
appNodeModulesPath,
srcNodeModulesPath,
distPath,
distMainPath,
distRendererPath,
buildPath,
};
+1
View File
@@ -0,0 +1 @@
export default 'test-file-stub';
+8
View File
@@ -0,0 +1,8 @@
{
"rules": {
"no-console": "off",
"global-require": "off",
"import/no-dynamic-require": "off",
"import/no-extraneous-dependencies": "off"
}
}
+24
View File
@@ -0,0 +1,24 @@
// Check if the renderer and main bundles are built
import path from 'path';
import chalk from 'chalk';
import fs from 'fs';
import webpackPaths from '../configs/webpack.paths';
const mainPath = path.join(webpackPaths.distMainPath, 'main.js');
const rendererPath = path.join(webpackPaths.distRendererPath, 'renderer.js');
if (!fs.existsSync(mainPath)) {
throw new Error(
chalk.whiteBright.bgRed.bold(
'The main process is not built yet. Build it by running "npm run build:main"'
)
);
}
if (!fs.existsSync(rendererPath)) {
throw new Error(
chalk.whiteBright.bgRed.bold(
'The renderer process is not built yet. Build it by running "npm run build:renderer"'
)
);
}
+54
View File
@@ -0,0 +1,54 @@
import fs from 'fs';
import chalk from 'chalk';
import { execSync } from 'child_process';
import { dependencies } from '../../package.json';
if (dependencies) {
const dependenciesKeys = Object.keys(dependencies);
const nativeDeps = fs
.readdirSync('node_modules')
.filter((folder) => fs.existsSync(`node_modules/${folder}/binding.gyp`));
if (nativeDeps.length === 0) {
process.exit(0);
}
try {
// Find the reason for why the dependency is installed. If it is installed
// because of a devDependency then that is okay. Warn when it is installed
// because of a dependency
const { dependencies: dependenciesObject } = JSON.parse(
execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString()
);
const rootDependencies = Object.keys(dependenciesObject);
const filteredRootDependencies = rootDependencies.filter((rootDependency) =>
dependenciesKeys.includes(rootDependency)
);
if (filteredRootDependencies.length > 0) {
const plural = filteredRootDependencies.length > 1;
console.log(`
${chalk.whiteBright.bgYellow.bold(
'Webpack does not work with native dependencies.'
)}
${chalk.bold(filteredRootDependencies.join(', '))} ${
plural ? 'are native dependencies' : 'is a native dependency'
} and should be installed inside of the "./release/app" folder.
First, uninstall the packages from "./package.json":
${chalk.whiteBright.bgGreen.bold('npm uninstall your-package')}
${chalk.bold(
'Then, instead of installing the package to the root "./package.json":'
)}
${chalk.whiteBright.bgRed.bold('npm install your-package')}
${chalk.bold('Install the package to "./release/app/package.json"')}
${chalk.whiteBright.bgGreen.bold(
'cd ./release/app && npm install your-package'
)}
Read more about native dependencies at:
${chalk.bold(
'https://electron-react-boilerplate.js.org/docs/adding-dependencies/#module-structure'
)}
`);
process.exit(1);
}
} catch (e) {
console.log('Native dependencies could not be checked');
}
}
+16
View File
@@ -0,0 +1,16 @@
import chalk from 'chalk';
export default function checkNodeEnv(expectedEnv) {
if (!expectedEnv) {
throw new Error('"expectedEnv" not set');
}
if (process.env.NODE_ENV !== expectedEnv) {
console.log(
chalk.whiteBright.bgRed.bold(
`"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`
)
);
process.exit(2);
}
}
+16
View File
@@ -0,0 +1,16 @@
import chalk from 'chalk';
import detectPort from 'detect-port';
const port = process.env.PORT || '4343';
detectPort(port, (err, availablePort) => {
if (port !== String(availablePort)) {
throw new Error(
chalk.whiteBright.bgRed.bold(
`Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm start`
)
);
} else {
process.exit(0);
}
});
+17
View File
@@ -0,0 +1,17 @@
import rimraf from 'rimraf';
import process from 'process';
import webpackPaths from '../configs/webpack.paths';
const args = process.argv.slice(2);
const commandMap = {
dist: webpackPaths.distPath,
release: webpackPaths.releasePath,
dll: webpackPaths.dllPath,
};
args.forEach((x) => {
const pathToRemove = commandMap[x];
if (pathToRemove !== undefined) {
rimraf.sync(pathToRemove);
}
});
+8
View File
@@ -0,0 +1,8 @@
import path from 'path';
import rimraf from 'rimraf';
import webpackPaths from '../configs/webpack.paths';
export default function deleteSourceMaps() {
rimraf.sync(path.join(webpackPaths.distMainPath, '*.js.map'));
rimraf.sync(path.join(webpackPaths.distRendererPath, '*.js.map'));
}
+20
View File
@@ -0,0 +1,20 @@
import { execSync } from 'child_process';
import fs from 'fs';
import { dependencies } from '../../release/app/package.json';
import webpackPaths from '../configs/webpack.paths';
if (
Object.keys(dependencies || {}).length > 0 &&
fs.existsSync(webpackPaths.appNodeModulesPath)
) {
const electronRebuildCmd =
'../../node_modules/.bin/electron-rebuild --force --types prod,dev,optional --module-dir .';
const cmd =
process.platform === 'win32'
? electronRebuildCmd.replace(/\//g, '\\')
: electronRebuildCmd;
execSync(cmd, {
cwd: webpackPaths.appPath,
stdio: 'inherit',
});
}
+9
View File
@@ -0,0 +1,9 @@
import fs from 'fs';
import webpackPaths from '../configs/webpack.paths';
const { srcNodeModulesPath } = webpackPaths;
const { appNodeModulesPath } = webpackPaths;
if (!fs.existsSync(srcNodeModulesPath) && fs.existsSync(appNodeModulesPath)) {
fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction');
}
+30
View File
@@ -0,0 +1,30 @@
const { notarize } = require('electron-notarize');
const { build } = require('../../package.json');
exports.default = async function notarizeMacos(context) {
const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') {
return;
}
if (process.env.CI !== 'true') {
console.warn('Skipping notarizing step. Packaging is not running in CI');
return;
}
if (!('APPLE_ID' in process.env && 'APPLE_ID_PASS' in process.env)) {
console.warn(
'Skipping notarizing step. APPLE_ID and APPLE_ID_PASS env variables must be set'
);
return;
}
const appName = context.packager.appInfo.productFilename;
await notarize({
appBundleId: build.appId,
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_ID_PASS,
});
};
+34
View File
@@ -0,0 +1,34 @@
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Coverage directory used by tools like istanbul
coverage
.eslintcache
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
# OSX
.DS_Store
src/i18n
release/app/dist
release/build
.erb/dll
.idea
npm-debug.log.*
*.css.d.ts
*.sass.d.ts
*.scss.d.ts
# eslint ignores hidden directories by default:
# https://github.com/eslint/eslint/issues/8429
!.erb
+90
View File
@@ -0,0 +1,90 @@
module.exports = {
extends: ['erb', 'plugin:typescript-sort-keys/recommended'],
ignorePatterns: ['.erb/*', 'server'],
parser: '@typescript-eslint/parser',
parserOptions: {
createDefaultProgram: true,
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
project: './tsconfig.json',
sourceType: 'module',
tsconfigRootDir: './',
},
plugins: ['@typescript-eslint', 'import', 'sort-keys-fix'],
rules: {
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-shadow': ['off'],
'default-case': 'off',
'import/extensions': 'off',
'import/no-absolute-path': 'off',
// A temporary hack related to IDE not resolving correct package.json
'import/no-extraneous-dependencies': 'off',
'import/no-unresolved': 'error',
'import/order': [
'error',
{
alphabetize: {
caseInsensitive: true,
order: 'asc',
},
groups: ['builtin', 'external', 'internal', ['parent', 'sibling']],
'newlines-between': 'never',
pathGroups: [
{
group: 'external',
pattern: 'react',
position: 'before',
},
],
pathGroupsExcludedImportTypes: ['react'],
},
],
'import/prefer-default-export': 'off',
'jsx-a11y/click-events-have-key-events': 'off',
'jsx-a11y/interactive-supports-focus': 'off',
'jsx-a11y/media-has-caption': 'off',
'no-await-in-loop': 'off',
'no-console': 'off',
'no-nested-ternary': 'off',
'no-restricted-syntax': 'off',
'no-underscore-dangle': 'off',
'prefer-destructuring': 'off',
'react/jsx-props-no-spreading': 'off',
'react/jsx-sort-props': [
'error',
{
callbacksLast: true,
ignoreCase: false,
noSortAlphabetically: false,
reservedFirst: true,
shorthandFirst: true,
shorthandLast: false,
},
],
'react/no-array-index-key': 'off',
'react/react-in-jsx-scope': 'off',
'react/require-default-props': 'off',
'sort-keys-fix/sort-keys-fix': 'warn',
},
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
// See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json',
},
webpack: {
config: require.resolve('./.erb/configs/webpack.config.eslint.ts'),
},
},
},
};
-82
View File
@@ -1,82 +0,0 @@
{
"env": {
"browser": true,
"es2021": true
},
"ignorePatterns": [
"node_modules/*",
"dist/*",
"electron/preload/*",
"vite.config.ts",
"post-install.js"
],
"extends": [
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:typescript-sort-keys/recommended",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react",
"@typescript-eslint",
"import",
"sort-keys-fix",
"promise"
],
"rules": {
"react-hooks/exhaustive-deps": [
"warn",
{ "enableDangerousAutofixThisMayCauseInfiniteLoops": true }
],
"react/jsx-sort-props": [
"error",
{
"callbacksLast": true,
"ignoreCase": false,
"noSortAlphabetically": false,
"reservedFirst": true,
"shorthandFirst": true,
"shorthandLast": false
}
],
"import/order": [
"error",
{
"groups": ["builtin", "external", "internal", ["parent", "sibling"]],
"pathGroups": [
{
"pattern": "react",
"group": "external",
"position": "before"
}
],
"pathGroupsExcludedImportTypes": ["react"],
"newlines-between": "never",
"alphabetize": {
"order": "asc",
"caseInsensitive": true
}
}
],
"sort-keys-fix/sort-keys-fix": "warn",
"@typescript-eslint/no-explicit-any": "off",
"consistent-return": "off",
"object-curly-newline": "off",
"indent": "off",
"no-tabs": "off",
"react/jsx-indent": "off",
"react/jsx-indent-props": "off",
"react/react-in-jsx-scope": "off"
}
}
+12
View File
@@ -0,0 +1,12 @@
* text eol=lf
*.exe binary
*.png binary
*.jpg binary
*.jpeg binary
*.ico binary
*.icns binary
*.eot binary
*.otf binary
*.ttf binary
*.woff binary
*.woff2 binary
+1
View File
@@ -0,0 +1 @@
# These are supported funding model platforms
+44
View File
@@ -0,0 +1,44 @@
---
name: Bug report
about: You're having technical issues. 🐞
labels: 'bug'
---
## Expected Behavior
<!--- What should have happened? -->
## Current Behavior
<!--- What went wrong? -->
<!-- Add screenshots to help explain your problem -->
<!-- (Open the browser dev tools in the menu or using CTRL + SHIFT + I) -->
## Steps to Reproduce
<!-- Add relevant code and/or a live example -->
<!-- Add stack traces -->
1.
2.
3.
4.
## Possible Solution (Not obligatory)
<!--- Suggest a reason for the bug or how to fix it. -->
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
- Application version :
- Operating System and version :
- Node version (if developing locally) :
+9
View File
@@ -0,0 +1,9 @@
---
name: Question
about: Ask a question.❓
labels: 'question'
---
<!-- Question issues will be closed. -->
<!-- Ask questions in the discussions tab: Please use discussions https://github.com/jeffvli/feishin/discussions -->
<!-- Or join the Discord/Matrix servers: https://discord.gg/FVKpcMDy5f https://matrix.to/#/#sonixd:matrix.org -->
@@ -0,0 +1,9 @@
---
name: Feature request
about: Request a feature to be added to Feishin 🎉
labels: 'enhancement'
---
## What do you want to be added?
## Additional context
+6
View File
@@ -0,0 +1,6 @@
requiredHeaders:
- Prerequisites
- Expected Behavior
- Current Behavior
- Possible Solution
- Your Environment
+17
View File
@@ -0,0 +1,17 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- discussion
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
+39
View File
@@ -0,0 +1,39 @@
name: Publish Linux (Manual)
on: workflow_dispatch
jobs:
publish:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
steps:
- name: Checkout git repo
uses: actions/checkout@v1
- name: Install Node and NPM
uses: actions/setup-node@v1
with:
node-version: 16
cache: npm
- name: Install dependencies
run: |
npm install --legacy-peer-deps
- name: Publish releases
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
npm run postinstall
npm run build
npm exec electron-builder -- --publish always --linux
on_retry_command: npm cache clean --force
+39
View File
@@ -0,0 +1,39 @@
name: Publish Windows and macOS (Manual)
on: workflow_dispatch
jobs:
publish:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest]
steps:
- name: Checkout git repo
uses: actions/checkout@v1
- name: Install Node and NPM
uses: actions/setup-node@v1
with:
node-version: 16
cache: npm
- name: Install dependencies
run: |
npm install --legacy-peer-deps
- name: Publish releases
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
npm run postinstall
npm run build
npm exec electron-builder -- --publish always --win --mac
on_retry_command: npm cache clean --force
+54
View File
@@ -0,0 +1,54 @@
name: Comment on pull request
on:
workflow_run:
workflows: ['Publish (PR)']
types: [completed]
jobs:
pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
# This snippet is public-domain, taken from
# https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml
script: |
async function upsertComment(owner, repo, issue_number, purpose, body) {
const {data: comments} = await github.rest.issues.listComments(
{owner, repo, issue_number});
const marker = `<!-- bot: ${purpose} -->`;
body = marker + "\n" + body;
const existing = comments.filter((c) => c.body.includes(marker));
if (existing.length > 0) {
const last = existing[existing.length - 1];
core.info(`Updating comment ${last.id}`);
await github.rest.issues.updateComment({
owner, repo,
body,
comment_id: last.id,
});
} else {
core.info(`Creating a comment in issue / PR #${issue_number}`);
await github.rest.issues.createComment({issue_number, body, owner, repo});
}
}
const {owner, repo} = context.repo;
const run_id = ${{github.event.workflow_run.id}};
const pull_requests = ${{ toJSON(github.event.workflow_run.pull_requests) }};
if (!pull_requests.length) {
return core.error("This workflow doesn't match any pull requests!");
}
const artifacts = await github.paginate(
github.rest.actions.listWorkflowRunArtifacts, {owner, repo, run_id});
if (!artifacts.length) {
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n`;
for (const art of artifacts) {
body += `\n* [${art.name}.zip](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
}
core.info("Review thread message body:", body);
for (const pr of pull_requests) {
await upsertComment(owner, repo, pr.number,
"nightly-link", body);
}
+60
View File
@@ -0,0 +1,60 @@
name: Publish (PR)
on:
pull_request:
branches:
- development
jobs:
publish:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest]
steps:
- name: Checkout git repo
uses: actions/checkout@v3
- name: Install Node and NPM
uses: actions/setup-node@v3
with:
node-version: 16
cache: npm
- name: Install dependencies
run: |
npm install --legacy-peer-deps
- name: Build releases
uses: nick-invision/retry@v2.8.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
npm run postinstall
npm run build
npm run package:pr
on_retry_command: npm cache clean --force
- uses: actions/upload-artifact@v3
with:
name: windows-binaries
path: |
release/build/*.exe
- uses: actions/upload-artifact@v3
with:
name: linux-binaries
path: |
release/build/*.AppImage
release/build/*.deb
release/build/*.rpm
- uses: actions/upload-artifact@v3
with:
name: macos-binaries
path: |
release/build/*.dmg
+34
View File
@@ -0,0 +1,34 @@
name: Test
on: [push, pull_request]
jobs:
release:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
steps:
- name: Check out Git repository
uses: actions/checkout@v1
- name: Install Node.js and NPM
uses: actions/setup-node@v2
with:
node-version: 16
cache: npm
- name: npm install
run: |
npm install --legacy-peer-deps
- name: npm test
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npm run package
npm run lint
npm exec tsc
npm test
+21 -19
View File
@@ -1,29 +1,31 @@
# Logs # Logs
logs logs
*.log *.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Runtime data
pids
*.pid
*.seed
# Coverage directory used by tools like istanbul
coverage
.eslintcache
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules node_modules
dist
dist-ssr
*.local
# Editor directories and files # OSX
.idea
.DS_Store .DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
release/app/dist release/app/dist
release/build release/build
.vscode/.debug.env .erb/dll
./package-lock.json
pnpm-lock.yaml .idea
yarn.lock npm-debug.log.*
*.css.d.ts
*.sass.d.ts
*.scss.d.ts
.env*
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
+14 -6
View File
@@ -1,12 +1,20 @@
{ {
"printWidth": 120, "printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true, "semi": true,
"singleQuote": true, "singleQuote": true,
"trailingComma": "es5", "overrides": [
{
"files": ["**/*.css", "**/*.scss", "**/*.html"],
"options": {
"singleQuote": false
}
}
],
"trailingComma": "all",
"bracketSpacing": true, "bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always", "arrowParens": "always",
"proseWrap": "preserve" "proseWrap": "never",
"htmlWhitespaceSensitivity": "strict",
"endOfLine": "lf",
"singleAttributePerLine": true
} }
+31
View File
@@ -0,0 +1,31 @@
{
"processors": ["stylelint-processor-styled-components"],
"customSyntax": "postcss-scss",
"extends": [
"stylelint-config-standard-scss",
"stylelint-config-styled-components",
"stylelint-config-rational-order"
],
"rules": {
"color-function-notation": ["legacy"],
"declaration-empty-line-before": null,
"order/properties-order": [],
"plugin/rational-order": [
true,
{
"border-in-box-model": false,
"empty-line-between-groups": false
}
],
"string-quotes": "single",
"declaration-block-no-redundant-longhand-properties": null,
"selector-class-pattern": null,
"selector-type-case": ["lower", { "ignoreTypes": ["/^\\$\\w+/"] }],
"selector-type-no-unknown": [
true,
{ "ignoreTypes": ["/-styled-mixin/", "/^\\$\\w+/"] }
],
"value-keyword-case": ["lower", { "ignoreKeywords": ["dummyValue"] }],
"declaration-colon-newline-after": null
}
}
-24
View File
@@ -1,24 +0,0 @@
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import { createRequire } from 'module'
import { spawn } from 'child_process'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const require = createRequire(import.meta.url)
const pkg = require('../package.json')
// write .debug.env
const envContent = Object.entries(pkg.debug.env).map(([key, val]) => `${key}=${val}`)
fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n'))
// bootstrap
spawn(
// TODO: terminate `npm run dev` when Debug exits.
process.platform === 'win32' ? 'npm.cmd' : 'npm',
['run', 'dev'],
{
stdio: 'inherit',
env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }),
},
)
+4 -6
View File
@@ -1,10 +1,8 @@
{ {
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [ "recommendations": [
"editorconfig.editorconfig", "dbaeumer.vscode-eslint",
"mrmlnc.vscode-json5", "EditorConfig.EditorConfig",
"rbbit.typescript-hero", "stylelint.vscode-stylelint",
"syler.sass-indented", "esbenp.prettier-vscode"
] ]
} }
+18 -35
View File
@@ -1,47 +1,30 @@
{ {
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"compounds": [
{
"name": "Debug App",
"preLaunchTask": "start .debug.script.mjs",
"configurations": [
"Debug Main Process",
"Debug Renderer Process"
],
"presentation": {
"hidden": false,
"group": "",
"order": 1
},
"stopAll": true
}
],
"configurations": [ "configurations": [
{ {
"name": "Debug Main Process", "name": "Electron: Main",
"type": "pwa-node", "type": "node",
"request": "launch", "request": "launch",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", "protocol": "inspector",
"windows": { "runtimeExecutable": "npm",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
},
"runtimeArgs": [ "runtimeArgs": [
"--no-sandbox", "run start:main --inspect=5858 --remote-debugging-port=9223"
"--remote-debugging-port=9229",
"."
], ],
"envFile": "${workspaceFolder}/.vscode/.debug.env", "preLaunchTask": "Start Webpack Dev"
"console": "integratedTerminal"
}, },
{ {
"name": "Debug Renderer Process", "name": "Electron: Renderer",
"port": 9229, "type": "chrome",
"request": "attach", "request": "attach",
"type": "pwa-chrome", "port": 9223,
"timeout": 60000 "webRoot": "${workspaceFolder}",
}, "timeout": 15000
}
],
"compounds": [
{
"name": "Electron: All",
"configurations": ["Electron: Main", "Electron: Renderer"]
}
] ]
} }
+43
View File
@@ -0,0 +1,43 @@
{
"files.associations": {
".eslintrc": "jsonc",
".prettierrc": "jsonc",
".eslintignore": "ignore"
},
"eslint.validate": ["typescript"],
"eslint.workingDirectories": [
{ "directory": "./", "changeProcessCWD": true },
{ "directory": "./server", "changeProcessCWD": true }
],
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
"editor.tabSize": 2,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": false
},
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"javascript.validate.enable": false,
"javascript.format.enable": false,
"typescript.format.enable": false,
"search.exclude": {
".git": true,
".eslintcache": true,
".erb/dll": true,
"release/{build,app/dist}": true,
"node_modules": true,
"npm-debug.log.*": true,
"test/**/__snapshots__": true,
"package-lock.json": true,
"*.{css,sass,scss}.d.ts": true
},
"i18n-ally.localesPaths": ["src/i18n", "src/i18n/locales"],
"typescript.tsdk": "node_modules\\typescript\\lib",
"typescript.preferences.importModuleSpecifier": "non-relative",
"stylelint.validate": ["css", "less", "postcss", "typescript", "typescriptreact", "scss"],
"typescript.updateImportsOnFileMove.enabled": "always",
"[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
"[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
"typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true
}
+10 -19
View File
@@ -1,34 +1,25 @@
{ {
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"label": "start .debug.script.mjs", "type": "npm",
"type": "shell", "label": "Start Webpack Dev",
"command": "node .vscode/.debug.script.mjs", "script": "start:renderer",
"options": {
"cwd": "${workspaceFolder}"
},
"isBackground": true, "isBackground": true,
"problemMatcher": { "problemMatcher": {
"owner": "typescript", "owner": "custom",
"fileLocation": "relative",
"pattern": { "pattern": {
// TODO: correct "regexp" "regexp": "____________"
"regexp": "^([a-zA-Z]\\:\/?([\\w\\-]\/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$",
"file": 1,
"line": 3,
"column": 4,
"code": 5,
"message": 6
}, },
"background": { "background": {
"activeOnStart": true, "activeOnStart": true,
"endsPattern": "^.*[startup] Electron App.*$", "beginsPattern": "Compiling\\.\\.\\.$",
"endsPattern": "(Compiled successfully|Failed to compile)\\.$"
} }
} }
} }
] ]
} }
// https://code.visualstudio.com/docs/editor/tasks#_operating-system-specific-properties
// https://code.visualstudio.com/docs/editor/tasks#_background-watching-tasks
// https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
+5
View File
@@ -0,0 +1,5 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+164 -164
View File
@@ -1,190 +1,190 @@
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007 Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
Preamble Preamble
The GNU General Public License is a free, copyleft license for The GNU General Public License is a free, copyleft license for
software and other kinds of works. software and other kinds of works.
The licenses for most software and other practical works are designed The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast, to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to any other work released this way by its authors. You can apply it to
your programs, too. your programs, too.
When we speak of free software, we are referring to freedom, not When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things. free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others. you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they or can get the source code. And you must show them these terms so they
know their rights. know their rights.
Developers that use the GNU GPL protect your rights with two steps: Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License (1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it. giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to changed, so that their problems will not be attributed erroneously to
authors of previous versions. authors of previous versions.
Some devices are designed to deny users access to install or run Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users. of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents. Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free. patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and The precise terms and conditions for copying, distribution and
modification follow. modification follow.
TERMS AND CONDITIONS TERMS AND CONDITIONS
0. Definitions. 0. Definitions.
"This License" refers to version 3 of the GNU General Public License. "This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks. works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this "The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations. "recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work. earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based A "covered work" means either the unmodified Program or a work based
on the Program. on the Program.
To "propagate" a work means to do anything with it that, without To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying, computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the distribution (with or without modification), making available to the
public, and in some countries other activities as well. public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying. a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices" An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2) feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion. menu, a prominent item in the list meets this criterion.
1. Source Code. 1. Source Code.
The "source code" for a work means the preferred form of the work The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source for making modifications to it. "Object code" means any non-source
form of a work. form of a work.
A "Standard Interface" means an interface that either is an official A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that interfaces specified for a particular programming language, one that
is widely used among developers working in that language. is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component "Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system (kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to (if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it. produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require, linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those such as by intimate data communication or control flow between those
subprograms and other parts of the work. subprograms and other parts of the work.
The Corresponding Source need not include anything that users The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding can regenerate automatically from other parts of the Corresponding
Source. Source.
The Corresponding Source for a work in source code form is that The Corresponding Source for a work in source code form is that
same work. same work.
2. Basic Permissions. 2. Basic Permissions.
All rights granted under this License are granted for the term of All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law. rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you. your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10 the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary. makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law. 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or 11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such similar laws prohibiting or restricting circumvention of such
measures. measures.
When you convey a covered work, you waive any legal power to forbid When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or the covered work, and you disclaim any intention to limit operation or
@@ -192,9 +192,9 @@ modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of users, your or third parties' legal rights to forbid circumvention of
technological measures. technological measures.
4. Conveying Verbatim Copies. 4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice; appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any keep intact all notices stating that this License and any
@@ -202,12 +202,12 @@ non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program. recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey, You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee. and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions. 5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions: terms of section 4, provided that you also meet all of these conditions:
@@ -232,19 +232,19 @@ terms of section 4, provided that you also meet all of these conditions:
interfaces that do not display Appropriate Legal Notices, your interfaces that do not display Appropriate Legal Notices, your
work need not make them do so. work need not make them do so.
A compilation of a covered work with other separate and independent A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work, works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program, and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not "aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other in an aggregate does not cause this License to apply to the other
parts of the aggregate. parts of the aggregate.
6. Conveying Non-Source Forms. 6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License, machine-readable Corresponding Source under the terms of this License,
in one of these ways: in one of these ways:
@@ -290,75 +290,75 @@ in one of these ways:
Source of the work are being offered to the general public at no Source of the work are being offered to the general public at no
charge under subsection 6d. charge under subsection 6d.
A separable portion of the object code, whose source code is excluded A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be from the Corresponding Source as a System Library, need not be
included in conveying the object code work. included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family, tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product, into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product. the only significant mode of use of the product.
"Installation Information" for a User Product means any methods, "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because code is in no case prevented or interfered with solely because
modification has been made. modification has been made.
If you convey an object code work under this section in, or with, or If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has modified object code on the User Product (for example, the work has
been installed in ROM). been installed in ROM).
The requirement to provide Installation Information does not include a The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and adversely affects the operation of the network or violates the rules and
protocols for communication across the network. protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided, Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in documented (and with an implementation available to the public in
source code form), and must require no special password or key for source code form), and must require no special password or key for
unpacking, reading or copying. unpacking, reading or copying.
7. Additional Terms. 7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions. License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions. this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work, additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission. for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms: that material) supplement the terms of this License with terms:
@@ -385,74 +385,74 @@ that material) supplement the terms of this License with terms:
any liability that these contractual assumptions directly impose on any liability that these contractual assumptions directly impose on
those licensors and authors. those licensors and authors.
All other non-permissive additional terms are considered "further All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does of that license document, provided that the further restriction does
not survive such relicensing or conveying. not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating additional terms that apply to those files, or a notice indicating
where to find the applicable terms. where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions; form of a separately written license, or stated as exceptions;
the above requirements apply either way. the above requirements apply either way.
8. Termination. 8. Termination.
You may not propagate or modify a covered work except as expressly You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third this License (including any patent licenses granted under the third
paragraph of section 11). paragraph of section 11).
However, if you cease all violation of this License, then your However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a) license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation. prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice. your receipt of the notice.
Termination of your rights under this section does not terminate the Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same reinstated, you do not qualify to receive new licenses for the same
material under section 10. material under section 10.
9. Acceptance Not Required for Having Copies. 9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However, to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so. covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients. 10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License. for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could licenses to the work the party's predecessor in interest had or could
@@ -460,43 +460,43 @@ give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts. the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that (including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it. sale, or importing the Program or any portion of it.
11. Patents. 11. Patents.
A "contributor" is a copyright holder who authorizes use under this A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version". work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version, by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of patent sublicenses in a manner consistent with the requirements of
this License. this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version. propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to (such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a party means to make such an agreement or commitment not to enforce a
patent against the party. patent against the party.
If you convey a covered work, knowingly relying on a patent license, If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means, publicly available network server or other readily accessible means,
@@ -504,13 +504,13 @@ then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid. country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify receiving the covered work authorizing them to use, propagate, modify
@@ -518,10 +518,10 @@ or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered you grant is automatically extended to all recipients of the covered
work and works based on it. work and works based on it.
A patent license is "discriminatory" if it does not include within A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying to the third party based on the extent of your activity of conveying
@@ -533,73 +533,73 @@ for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement, contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007. or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law. otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom. 12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program. License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License. 13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work, License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License, but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the section 13, concerning interaction through a network will apply to the
combination as such. combination as such.
14. Revised Versions of this License. 14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to be similar in spirit to the present version, but may differ in detail to
address new problems or concerns. address new problems or concerns.
Each version is given a distinguishing version number. If the Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published GNU General Public License, you may choose any version ever published
by the Free Software Foundation. by the Free Software Foundation.
If the Program specifies that a proxy can decide which future If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you public statement of acceptance of a version permanently authorizes you
to choose that version for the Program. to choose that version for the Program.
Later license versions may give you additional or different Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a author or copyright holder as a result of your choosing to follow a
later version. later version.
15. Disclaimer of Warranty. 15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability. 16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
@@ -609,9 +609,9 @@ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES. SUCH DAMAGES.
17. Interpretation of Sections 15 and 16. 17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms, above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the an absolute waiver of all civil liability in connection with the
@@ -622,11 +622,11 @@ copy of the Program in return for a fee.
How to Apply These Terms to Your New Programs How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms. free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found. the "copyright" line and a pointer to where the full notice is found.
@@ -649,7 +649,7 @@ the "copyright" line and a pointer to where the full notice is found.
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode: notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author> <program> Copyright (C) <year> <name of author>
@@ -658,17 +658,17 @@ notice like this when it starts in an interactive mode:
under certain conditions; type `show c' for details. under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box". might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school, You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary. if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>. <http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>. <http://www.gnu.org/philosophy/why-not-lgpl.html>.
+59 -7
View File
@@ -1,15 +1,67 @@
# Sonixd (rewrite) # Feishin
Repository for the rewrite of [Sonixd](https://github.com/jeffvli/sonixd). <p align="center">
<a href="https://github.com/jeffvli/feishin/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/jeffvli/feishin?style=flat-square&color=brightgreen"
alt="License">
</a>
<a href="https://github.com/jeffvli/feishin/releases">
<img src="https://img.shields.io/github/v/release/jeffvli/feishin?style=flat-square&color=blue"
alt="Release">
</a>
<a href="https://github.com/jeffvli/feishin/releases">
<img src="https://img.shields.io/github/downloads/jeffvli/feishin/total?style=flat-square&color=orange"
alt="Downloads">
</a>
</p>
<p align="center">
<a href="https://discord.gg/FVKpcMDy5f">
<img src="https://img.shields.io/discord/922656312888811530?color=black&label=discord&logo=discord&logoColor=white"
alt="Discord">
</a>
<a href="https://matrix.to/#/#sonixd:matrix.org">
<img src="https://img.shields.io/matrix/sonixd:matrix.org?color=black&label=matrix&logo=matrix&logoColor=white"
alt="Matrix">
</a>
</p>
Rewrite of [Sonixd](https://github.com/jeffvli/sonixd).
## Features
- [x] MPV player backend
- [x] Web player backend
- [x] Modern UI
- [x] Scrobble playback to your server
- [x] Smart playlist editor (Navidrome)
- [ ] [Request a feature](https://github.com/jeffvli/feishin/issues) or [view taskboard](https://github.com/users/jeffvli/projects/5/views/1)
## Screenshots
<a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_home.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_home.png" width="49.5%"/></a> <a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_artist_detail.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_artist_detail.png" width="49.5%"/></a> <a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_detail.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_detail.png" width="49.5%"/></a> <a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_smart_playlist.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_smart_playlist.png" width="49.5%"/></a>
## Getting Started
Download the [latest desktop client](https://github.com/jeffvli/feishin/releases).
## FAQ
### What music servers does Feishin support?
Feishin supports any music server that implements a [Navidrome](https://www.navidrome.org/) or [Jellyfin](https://jellyfin.org/) API. **Subsonic API is not currently supported**. This will likely be added in [later when the new Subsonic API is decided on](https://support.symfonium.app/t/subsonic-servers-participation/1233).
- [Navidrome](https://github.com/navidrome/navidrome)
- [Jellyfin](https://github.com/jellyfin/jellyfin)
- ~~[Gonic](https://github.com/sentriz/gonic)~~
- ~~[Astiga](https://asti.ga/)~~
- ~~[Supysonic](https://github.com/spl0k/supysonic)~~
## Development ## Development
TBD Built and tested using Node `v16.15.0`.
### Developing with Docker Compose This project is built off of [electron-react-boilerplate](https://github.com/electron-react-boilerplate/electron-react-boilerplate) v4.6.0.
TBD
## License ## License
[GNU General Public License v3.0 ©](https://github.com/jeffvli/sonixd-rewrite/blob/dev/LICENSE) [GNU General Public License v3.0 ©](https://github.com/jeffvli/feishin/blob/dev/LICENSE)
+31
View File
@@ -0,0 +1,31 @@
type Styles = Record<string, string>;
declare module '*.svg' {
const content: string;
export default content;
}
declare module '*.png' {
const content: string;
export default content;
}
declare module '*.jpg' {
const content: string;
export default content;
}
declare module '*.scss' {
const content: Styles;
export default content;
}
declare module '*.sass' {
const content: Styles;
export default content;
}
declare module '*.css' {
const content: Styles;
export default content;
}
+10
View File
@@ -0,0 +1,10 @@
<?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-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+1
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

-59
View File
@@ -1,59 +0,0 @@
{
"appId": "TEST",
"productName": "TEST",
"copyright": "Copyright © 2022 ${author}",
"directories": {
"app": "release/app",
"output": "release/build",
"buildResources": "electron/resources"
},
"extends": null,
"asar": true,
"asarUnpack": ["**\\*.{node,dll}", "prisma"],
"files": [
"dist",
"node_modules",
"package.json",
"prisma/**/*",
"resources/**/*",
"!**/node_modules/@prisma/engines/introspection-engine*",
"!**/node_modules/@prisma/engines/migration-engine*",
"!**/node_modules/@prisma/engines/prisma-fmt*",
"!**/node_modules/@prisma/engines/query_engine-*",
"!**/node_modules/@prisma/engines/libquery_engine*",
"!**/node_modules/prisma/query_engine*",
"!**/node_modules/prisma/libquery_engine*",
"!**/node_modules/prisma/**/*.mjs"
],
"win": {
"target": [
{
"target": "nsis",
"arch": ["x64"]
}
],
"artifactName": "${productName}-Windows-${version}-Setup.${ext}"
},
"nsis": {
"oneClick": false,
"perMachine": false,
"allowToChangeInstallationDirectory": true,
"deleteAppDataOnUninstall": false
},
"mac": {
"target": ["dmg"],
"artifactName": "${productName}-Mac-${version}-Installer.${ext}"
},
"linux": {
"icon": "electron/resources/iconset",
"target": ["AppImage", "deb"],
"artifactName": "${productName}-Linux-${version}.${ext}"
},
"extraResources": [
"./assets/**",
"prisma/**/*",
"node_modules/@prisma/engines/migration-engine*",
"node_modules/@prisma/engines/query*",
"node_modules/@prisma/engines/libquery*"
]
}
-11
View File
@@ -1,11 +0,0 @@
/// <reference types="vite-electron-plugin/electron-env" />
declare namespace NodeJS {
interface ProcessEnv {
DIST: string;
DIST_ELECTRON: string;
/** /dist/ or /public/ */
PUBLIC: string;
VSCODE_DEBUG?: 'true';
}
}
-126
View File
@@ -1,126 +0,0 @@
import fs from 'fs';
import path from 'path';
import { PrismaClient } from '@prisma/client';
import { app, ipcMain } from 'electron';
import isDev from 'electron-is-dev';
import './server';
const dbPath = isDev
? path.join(__dirname, '../../../../prisma/dev.db')
: path.join(app.getPath('userData'), 'database.db');
if (!isDev) {
try {
// database file does not exist, need to create
fs.copyFileSync(path.join(process.resourcesPath, 'prisma/dev.db'), dbPath, fs.constants.COPYFILE_EXCL);
console.log(`DB does not exist. Create new DB from ${path.join(process.resourcesPath, 'prisma/dev.db')}`);
} catch (err) {
if (err && 'code' in (err as { code: string }) && (err as { code: string }).code !== 'EEXIST') {
console.error(`DB creation faild. Reason:`, err);
} else {
throw err;
}
}
}
function getPlatformName(): string {
const isDarwin = process.platform === 'darwin';
if (isDarwin && process.arch === 'arm64') {
return `${process.platform}Arm64`;
}
return process.platform;
}
const platformToExecutables: Record<string, any> = {
darwin: {
migrationEngine: 'node_modules/@prisma/engines/migration-engine-darwin',
queryEngine: 'node_modules/@prisma/engines/libquery_engine-darwin.dylib.node',
},
darwinArm64: {
migrationEngine: 'node_modules/@prisma/engines/migration-engine-darwin-arm64',
queryEngine: 'node_modules/@prisma/engines/libquery_engine-darwin-arm64.dylib.node',
},
linux: {
migrationEngine: 'node_modules/@prisma/engines/migration-engine-debian-openssl-1.1.x',
queryEngine: 'node_modules/@prisma/engines/libquery_engine-debian-openssl-1.1.x.so.node',
},
win32: {
migrationEngine: 'node_modules/@prisma/engines/migration-engine-windows.exe',
queryEngine: 'node_modules/@prisma/engines/query_engine-windows.dll.node',
},
};
const extraResourcesPath = app.getAppPath().replace('app.asar', ''); // impacted by extraResources setting in electron-builder.yml
const platformName = getPlatformName();
const mePath = path.join(extraResourcesPath, platformToExecutables[platformName].migrationEngine);
const qePath = path.join(extraResourcesPath, platformToExecutables[platformName].queryEngine);
ipcMain.on('config:get-app-path', (event) => {
event.returnValue = app.getAppPath();
});
ipcMain.on('config:get-platform-name', (event) => {
const isDarwin = process.platform === 'darwin';
event.returnValue =
isDarwin && process.arch === 'arm64' ? `${process.platform}Arm64` : (event.returnValue = process.platform);
});
ipcMain.on('config:get-prisma-db-path', (event) => {
event.returnValue = dbPath;
});
ipcMain.on('config:get-prisma-me-path', (event) => {
event.returnValue = mePath;
});
ipcMain.on('config:get-prisma-qe-path', (event) => {
event.returnValue = qePath;
});
export const prisma = new PrismaClient({
datasources: {
db: {
url: `file:${dbPath}`,
},
},
errorFormat: 'minimal',
// see https://github.com/prisma/prisma/discussions/5200
// __internal: {
// engine: {
// binaryPath: qePath,
// },
// },
});
prisma.server.findMany({
where: {},
});
export const exclude = <T, Key extends keyof T>(resultSet: T, ...keys: Key[]): Omit<T, Key> => {
// eslint-disable-next-line no-restricted-syntax
for (const key of keys) {
delete resultSet[key];
}
return resultSet;
};
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
prisma.$use(async (params, next) => {
const maxRetries = 5;
let retries = 0;
do {
try {
const result = await next(params);
return result;
} catch (err) {
retries += 1;
return sleep(500);
}
} while (retries < maxRetries);
});
@@ -1,12 +0,0 @@
import { ipcMain } from 'electron';
import { prisma } from '..';
export enum ServerApi {
GET_SERVER = 'api:server:get-server',
GET_SERVERS = 'api:server:get-servers',
}
ipcMain.handle(ServerApi.GET_SERVERS, async () => {
const result = await prisma.server.findMany();
return result;
});
-2
View File
@@ -1,2 +0,0 @@
import './mpv-player';
import './api';
@@ -1,134 +0,0 @@
import { ipcMain } from 'electron';
import MpvAPI from 'node-mpv';
import { getWindow } from '../../..';
const mpv = new MpvAPI(
{
audio_only: true,
auto_restart: true,
binary: 'C:/ProgramData/chocolatey/lib/mpv.install/tools/mpv.exe',
time_update: 1,
},
['--gapless-audio=yes', '--prefetch-playlist']
);
mpv.start().catch((error: any) => {
console.log('error', error);
});
mpv.on('status', (status: any) => {
if (status.property === 'playlist-pos') {
if (status.value !== 0) {
getWindow()?.webContents.send('renderer-player-auto-next');
}
}
});
// Automatically updates the play button when the player is playing
mpv.on('started', () => {
getWindow()?.webContents.send('renderer-player-play');
});
// Automatically updates the play button when the player is stopped
mpv.on('stopped', () => {
getWindow()?.webContents.send('renderer-player-stop');
});
// Automatically updates the play button when the player is paused
mpv.on('paused', () => {
getWindow()?.webContents.send('renderer-player-pause');
});
mpv.on('quit', () => {
console.log('mpv quit');
});
// Event output every interval set by time_update, used to update the current time
mpv.on('timeposition', (time: number) => {
getWindow()?.webContents.send('renderer-player-current-time', time);
});
mpv.on('seek', () => {
console.log('mpv seek');
});
// Starts the player
ipcMain.on('player-play', async () => {
await mpv.play();
});
// Pauses the player
ipcMain.on('player-pause', async () => {
await mpv.pause();
});
// Stops the player
ipcMain.on('player-stop', async () => {
await mpv.stop();
});
// Stops the player
ipcMain.on('player-next', async () => {
await mpv.next();
});
// Stops the player
ipcMain.on('player-previous', async () => {
await mpv.prev();
});
// Seeks forward or backward by the given amount of seconds
ipcMain.on('player-seek', async (_event, time: number) => {
await mpv.seek(time);
});
// Seeks to the given time in seconds
ipcMain.on('player-seek-to', async (_event, time: number) => {
await mpv.goToPosition(time);
});
// Sets the queue in position 0 and 1 to the given data. Used when manually starting a song or using the next/prev buttons
ipcMain.on('player-set-queue', async (_event, data: any) => {
if (data.queue.current) {
await mpv.load(data.queue.current.streamUrl, 'replace');
}
if (data.queue.next) {
await mpv.load(data.queue.next.streamUrl, 'append');
}
});
// Replaces the queue in position 1 to the given data
ipcMain.on('player-set-queue-next', async (_event, data: any) => {
const size = await mpv.getPlaylistSize();
if (size > 1) {
await mpv.playlistRemove(1);
}
if (data.queue.next) {
await mpv.load(data.queue.next.streamUrl, 'append');
}
});
// Sets the next song in the queue when reaching the end of the queue
ipcMain.on('player-auto-next', async (_event, data: any) => {
// Always keep the current song as position 0 in the mpv queue
// This allows us to easily set update the next song in the queue without
// disturbing the currently playing song
await mpv.playlistRemove(0);
if (data.queue.next) {
await mpv.load(data.queue.next.streamUrl, 'append');
}
});
// Sets the volume to the given value (0-100)
ipcMain.on('player-volume', async (_event, value: number) => {
mpv.volume(value);
});
// Toggles the mute status
ipcMain.on('player-mute', async () => {
mpv.mute();
});
-3
View File
@@ -1,3 +0,0 @@
import './core';
require(`./${process.platform}`);
-109
View File
@@ -1,109 +0,0 @@
// The built directory structure
//
// ├─┬ dist-electron
// │ ├─┬ main
// │ │ └── index.js > Electron-Main
// │ └─┬ preload
// │ └── index.js > Preload-Scripts
// ├─┬ dist
// │ └── index.html > Electron-Renderer
//
process.env.DIST_ELECTRON = join(__dirname, '..');
process.env.DIST = join(process.env.DIST_ELECTRON, '../dist');
process.env.PUBLIC = app.isPackaged ? process.env.DIST : join(process.env.DIST_ELECTRON, '../public');
import { release } from 'os';
import { join } from 'path';
import { app, BrowserWindow, shell, ipcMain } from 'electron';
import './features';
// Disable GPU Acceleration for Windows 7
if (release().startsWith('6.1')) app.disableHardwareAcceleration();
// Set application name for Windows 10+ notifications
if (process.platform === 'win32') app.setAppUserModelId(app.getName());
if (!app.requestSingleInstanceLock()) {
app.quit();
process.exit(0);
}
let win: BrowserWindow | null = null;
// Here, you can also use other preload
const preload = join(__dirname, '../preload/index.js');
const url = process.env.VITE_DEV_SERVER_URL as string;
const indexHtml = join(process.env.DIST, 'index.html');
async function createWindow() {
win = new BrowserWindow({
icon: join(process.env.PUBLIC as string, 'favicon.svg'),
title: 'Main window',
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
preload,
},
});
if (app.isPackaged) {
win.loadFile(indexHtml);
} else {
win.loadURL(url);
// win.webContents.openDevTools()
}
// Test actively push message to the Electron-Renderer
win.webContents.on('did-finish-load', () => {
win?.webContents.send('main-process-message', new Date().toLocaleString());
});
// Make all links open with the browser, not with the application
win.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith('https:')) shell.openExternal(url);
return { action: 'deny' };
});
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
win = null;
if (process.platform !== 'darwin') app.quit();
});
app.on('second-instance', () => {
if (win) {
// Focus on the main window if the user tried to open another
if (win.isMinimized()) win.restore();
win.focus();
}
});
app.on('activate', () => {
const allWindows = BrowserWindow.getAllWindows();
if (allWindows.length) {
allWindows[0].focus();
} else {
createWindow();
}
});
// new window example arg: new windows url
ipcMain.handle('open-win', (event, arg) => {
const childWindow = new BrowserWindow({
webPreferences: {
preload,
},
});
if (app.isPackaged) {
childWindow.loadFile(indexHtml, { hash: arg });
} else {
childWindow.loadURL(`${url}/#${arg}`);
// childWindow.webContents.openDevTools({ mode: "undocked", activate: true })
}
});
export const getWindow = () => {
return win;
};
-107
View File
@@ -1,107 +0,0 @@
import { contextBridge, ipcRenderer } from 'electron';
function domReady(condition: DocumentReadyState[] = ['complete', 'interactive']) {
return new Promise((resolve) => {
if (condition.includes(document.readyState)) {
resolve(true);
} else {
document.addEventListener('readystatechange', () => {
if (condition.includes(document.readyState)) {
resolve(true);
}
});
}
});
}
const safeDOM = {
append(parent: HTMLElement, child: HTMLElement) {
if (!Array.from(parent.children).find((e) => e === child)) {
return parent.appendChild(child);
}
},
remove(parent: HTMLElement, child: HTMLElement) {
if (Array.from(parent.children).find((e) => e === child)) {
return parent.removeChild(child);
}
},
};
/**
* https://tobiasahlin.com/spinkit
* https://connoratherton.com/loaders
* https://projects.lukehaas.me/css-loaders
* https://matejkustec.github.io/SpinThatShit
*/
function useLoading() {
const className = `loaders-css__square-spin`;
const styleContent = `
@keyframes square-spin {
25% { transform: perspective(100px) rotateX(180deg) rotateY(0); }
50% { transform: perspective(100px) rotateX(180deg) rotateY(180deg); }
75% { transform: perspective(100px) rotateX(0) rotateY(180deg); }
100% { transform: perspective(100px) rotateX(0) rotateY(0); }
}
.${className} > div {
animation-fill-mode: both;
width: 50px;
height: 50px;
background: #fff;
animation: square-spin 3s 0s cubic-bezier(0.09, 0.57, 0.49, 0.9) infinite;
}
.app-loading-wrap {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #282c34;
z-index: 9;
}
`;
const oStyle = document.createElement('style');
const oDiv = document.createElement('div');
oStyle.id = 'app-loading-style';
oStyle.innerHTML = styleContent;
oDiv.className = 'app-loading-wrap';
oDiv.innerHTML = `<div class="${className}"><div></div></div>`;
return {
appendLoading() {
safeDOM.append(document.head, oStyle);
safeDOM.append(document.body, oDiv);
},
removeLoading() {
safeDOM.remove(document.head, oStyle);
safeDOM.remove(document.body, oDiv);
},
};
}
// ----------------------------------------------------------------------
const { appendLoading, removeLoading } = useLoading();
domReady().then(appendLoading);
window.onmessage = (ev) => {
ev.data.payload === 'removeLoading' && removeLoading();
};
setTimeout(removeLoading, 4999);
const serverApi = {
getServer: () => ipcRenderer.invoke('api:server:get-server'), // ServerApi.GET_SERVER
getServers: () => ipcRenderer.invoke('api:server:get-servers'), // ServerApi.GET_SERVERS
};
const api = {
prisma: {
server: serverApi,
},
};
contextBridge.exposeInMainWorld('electron', api);
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

-14
View File
@@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/assets/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<title>Vite App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 644 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

+29507 -6664
View File
File diff suppressed because it is too large Load Diff
+306 -74
View File
@@ -1,86 +1,318 @@
{ {
"name": "sonixd-rewrite", "name": "feishin",
"productName": "sonixd-rewrite", "productName": "Feishin",
"private": true, "description": "Feishin music server",
"version": "0.0.0", "version": "0.0.1-alpha6",
"description": "",
"author": "jeffvli",
"license": "GPL-3.0",
"main": "release/app/dist/main/index.js",
"scripts": { "scripts": {
"dev": "vite", "build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
"build": "tsc && vite build && electron-builder", "build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts",
"postinstall": "node post-install.js && electron-builder install-app-deps", "build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts",
"prisma:init": "npx prisma migrate dev", "rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app",
"prisma:dev": "npx prisma db push", "lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx",
"prisma:migrate": "" "lint:styles": "npx stylelint **/*.tsx",
"package": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never",
"package:pr": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never --win --mac --linux",
"package:dev": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never --dir",
"postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts",
"start": "ts-node ./.erb/scripts/check-port-in-use.js && npm run start:renderer",
"start:main": "cross-env NODE_ENV=development electron -r ts-node/register/transpile-only ./src/main/main.ts",
"start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts",
"start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts",
"start:web": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.web.ts",
"test": "jest",
"prepare": "husky install",
"i18next": "i18next -c src/renderer/i18n/i18next-parser.config.js",
"prod:buildserver": "pwsh -c \"./scripts/server-build.ps1\"",
"prod:publishserver": "pwsh -c \"./scripts/server-publish.ps1\""
}, },
"engines": { "lint-staged": {
"node": "^14.18.0 || >=16.0.0" "*.{js,jsx,ts,tsx}": [
"cross-env NODE_ENV=development eslint --cache"
],
"*.json,.{eslintrc,prettierrc}": [
"prettier --ignore-path .eslintignore --parser json --write"
],
"*.{css,scss}": [
"prettier --ignore-path .eslintignore --single-quote --write"
],
"*.{html,md,yml}": [
"prettier --ignore-path .eslintignore --single-quote --write"
]
}, },
"dependencies": { "build": {
"@prisma/client": "^4.4.0", "productName": "Feishin",
"node-mpv": "^2.0.0-beta.2" "appId": "org.jeffvli.feishin",
"artifactName": "${productName}-${version}-${os}-${arch}.${ext}",
"asar": true,
"asarUnpack": "**\\*.{node,dll}",
"files": [
"dist",
"node_modules",
"package.json"
],
"afterSign": ".erb/scripts/notarize.js",
"electronVersion": "22.3.1",
"mac": {
"target": {
"target": "default",
"arch": [
"arm64",
"x64"
]
},
"type": "distribution",
"hardenedRuntime": true,
"entitlements": "assets/entitlements.mac.plist",
"entitlementsInherit": "assets/entitlements.mac.plist",
"gatekeeperAssess": false
},
"dmg": {
"contents": [
{
"x": 130,
"y": 220
},
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
}
]
},
"win": {
"target": [
"nsis",
"zip"
]
},
"linux": {
"target": [
"AppImage",
"tar.xz"
],
"icon": "assets/icons/placeholder.png",
"category": "Development"
},
"directories": {
"app": "release/app",
"buildResources": "assets",
"output": "release/build"
},
"extraResources": [
"./assets/**"
],
"publish": {
"provider": "github",
"owner": "jeffvli",
"repo": "feishin"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/jeffvli/feishin.git"
},
"author": {
"name": "jeffvli",
"url": "https://github.com/jeffvli/"
},
"contributors": [],
"license": "GPL-3.0",
"bugs": {
"url": "https://github.com/jeffvli/feishin/issues"
},
"keywords": [
"subsonic",
"navidrome",
"airsonic",
"jellyfin",
"react",
"electron"
],
"homepage": "https://github.com/jeffvli/feishin",
"jest": {
"testURL": "http://localhost/",
"testEnvironment": "jsdom",
"transform": {
"\\.(ts|tsx|js|jsx)$": "ts-jest"
},
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/.erb/mocks/fileMock.js",
"\\.(css|less|sass|scss)$": "identity-obj-proxy"
},
"moduleFileExtensions": [
"js",
"jsx",
"ts",
"tsx",
"json"
],
"moduleDirectories": [
"node_modules",
"release/app/node_modules"
],
"testPathIgnorePatterns": [
"release/app/dist"
],
"setupFiles": [
"./.erb/scripts/check-build-exists.ts"
]
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.21", "@electron/rebuild": "^3.2.10",
"@types/react-dom": "^18.0.6", "@pmmmwh/react-refresh-webpack-plugin": "0.5.5",
"@vitejs/plugin-react": "^2.1.0", "@stylelint/postcss-css-in-js": "^0.38.0",
"electron": "^21.1.0", "@teamsupercell/typings-for-css-modules-loader": "^2.5.1",
"electron-builder": "^23.3.3", "@testing-library/jest-dom": "^5.16.4",
"react": "^18.2.0", "@testing-library/react": "^13.0.0",
"react-dom": "^18.2.0", "@types/electron-localshortcut": "^3.1.0",
"sass": "^1.55.0", "@types/jest": "^27.4.1",
"typescript": "^4.8.4", "@types/lodash": "^4.14.188",
"vite": "^3.1.4", "@types/md5": "^2.3.2",
"vite-electron-plugin": "^0.4.4", "@types/node": "^17.0.23",
"@emotion/react": "^11.10.4", "@types/react": "^18.0.25",
"@emotion/styled": "^11.10.4", "@types/react-dom": "^18.0.8",
"@mantine/carousel": "^5.5.4", "@types/react-test-renderer": "^17.0.1",
"@mantine/core": "^5.5.4", "@types/react-virtualized-auto-sizer": "^1.0.1",
"@mantine/dates": "^5.5.4", "@types/react-window": "^1.8.5",
"@mantine/form": "^5.5.4", "@types/react-window-infinite-loader": "^1.0.6",
"@mantine/hooks": "^5.5.4", "@types/styled-components": "^5.1.26",
"@mantine/modals": "^5.5.4", "@types/terser-webpack-plugin": "^5.0.4",
"@mantine/notifications": "^5.5.4", "@types/webpack-bundle-analyzer": "^4.4.1",
"@mantine/spotlight": "^5.5.4", "@types/webpack-env": "^1.16.3",
"@tanstack/react-query": "^4.10.1", "@typescript-eslint/eslint-plugin": "^5.47.0",
"@typescript-eslint/eslint-plugin": "^5.39.0", "@typescript-eslint/parser": "^5.47.0",
"@typescript-eslint/parser": "^5.39.0", "browserslist-config-erb": "^0.0.3",
"ag-grid-community": "^28.2.0", "chalk": "^4.1.2",
"ag-grid-react": "^28.2.0", "concurrently": "^7.1.0",
"axios": "^1.0.0", "core-js": "^3.21.1",
"dayjs": "^1.11.5", "cross-env": "^7.0.3",
"electron-is-dev": "^2.0.0", "css-loader": "^6.7.1",
"electron-rebuild": "^3.2.9", "css-minimizer-webpack-plugin": "^3.4.1",
"embla-carousel-react": "^7.0.3", "detect-port": "^1.3.0",
"eslint": "^8.24.0", "electron": "^22.3.1",
"eslint-config-prettier": "^8.5.0", "electron-builder": "^24.0.0-alpha.13",
"eslint-import-resolver-typescript": "^3.5.1", "electron-devtools-installer": "^3.2.0",
"electron-notarize": "^1.2.1",
"electronmon": "^2.0.2",
"eslint": "^8.30.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-erb": "^4.0.3",
"eslint-import-resolver-typescript": "^2.7.1",
"eslint-import-resolver-webpack": "^0.13.2",
"eslint-plugin-compat": "^4.0.2",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-jest": "^26.1.3",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-promise": "^6.0.1", "eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.31.8", "eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.4.0",
"eslint-plugin-sort-keys-fix": "^1.1.2", "eslint-plugin-sort-keys-fix": "^1.1.2",
"eslint-plugin-typescript-sort-keys": "^2.1.0", "eslint-plugin-typescript-sort-keys": "^2.1.0",
"immer": "^9.0.15", "file-loader": "^6.2.0",
"nanoid": "^4.0.0", "html-webpack-plugin": "^5.5.0",
"prettier": "^2.7.1", "husky": "^7.0.4",
"prisma": "^4.4.0", "i18next-parser": "^6.3.0",
"react-virtualized-auto-sizer": "^1.0.7", "identity-obj-proxy": "^3.0.0",
"react-window": "^1.8.7", "jest": "^27.5.1",
"react-window-infinite-loader": "^1.0.8", "lint-staged": "^12.3.7",
"replace-in-file": "^6.3.5", "mini-css-extract-plugin": "^2.6.0",
"ts-node": "^10.9.1", "postcss-scss": "^4.0.4",
"vite-plugin-electron": "^0.9.2", "postcss-syntax": "^0.36.2",
"vite-plugin-electron-renderer": "^0.9.3", "prettier": "^2.6.2",
"zustand": "^4.1.1" "react-refresh": "^0.12.0",
"react-refresh-typescript": "^2.0.4",
"react-test-renderer": "^18.0.0",
"rimraf": "^3.0.2",
"sass": "^1.49.11",
"sass-loader": "^12.6.0",
"style-loader": "^3.3.1",
"stylelint": "^14.9.1",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-standard-scss": "^4.0.0",
"stylelint-config-styled-components": "^0.1.1",
"stylelint-order": "^5.0.0",
"stylelint-processor-styled-components": "^1.10.0",
"terser-webpack-plugin": "^5.3.1",
"ts-jest": "^27.1.4",
"ts-loader": "^9.2.8",
"ts-node": "^10.7.0",
"tsconfig-paths-webpack-plugin": "^4.0.0",
"typescript": "^4.8.4",
"typescript-plugin-styled-components": "^2.0.0",
"url-loader": "^4.1.1",
"webpack": "^5.71.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.8.0",
"webpack-merge": "^5.8.0"
}, },
"debug": { "dependencies": {
"env": { "@ag-grid-community/client-side-row-model": "^28.2.1",
"VITE_DEV_SERVER_URL": "http://127.0.0.1:7777" "@ag-grid-community/core": "^28.2.1",
} "@ag-grid-community/infinite-row-model": "^28.2.1",
"@ag-grid-community/react": "^28.2.1",
"@ag-grid-community/styles": "^28.2.1",
"@emotion/react": "^11.10.4",
"@mantine/core": "^6.0.8",
"@mantine/dates": "^6.0.8",
"@mantine/dropzone": "^6.0.8",
"@mantine/form": "^6.0.8",
"@mantine/hooks": "^6.0.8",
"@mantine/modals": "^6.0.8",
"@mantine/notifications": "^6.0.8",
"@mantine/utils": "^6.0.8",
"@tanstack/react-query": "^4.24.4",
"@tanstack/react-query-devtools": "^4.24.4",
"@ts-rest/core": "^3.19.2",
"axios": "^1.3.6",
"dayjs": "^1.11.6",
"electron-debug": "^3.2.0",
"electron-localshortcut": "^3.2.1",
"electron-log": "^4.4.6",
"electron-store": "^8.1.0",
"electron-updater": "^4.6.5",
"fast-average-color": "^9.2.0",
"format-duration": "^2.0.0",
"framer-motion": "^8.1.3",
"history": "^5.3.0",
"i18next": "^21.6.16",
"immer": "^9.0.15",
"is-electron": "^2.2.1",
"lodash": "^4.17.21",
"md5": "^2.3.0",
"memoize-one": "^6.0.0",
"nanoid": "^3.3.3",
"net": "^1.0.2",
"node-mpv": "^2.0.0-beta.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^3.1.4",
"react-i18next": "^11.16.7",
"react-icons": "^4.7.1",
"react-player": "^2.11.0",
"react-router": "^6.5.0",
"react-router-dom": "^6.5.0",
"react-simple-img": "^3.0.0",
"react-virtualized-auto-sizer": "^1.0.6",
"react-window": "^1.8.8",
"react-window-infinite-loader": "^1.0.8",
"styled-components": "^5.3.6",
"zod": "^3.19.1",
"zustand": "^4.1.4"
},
"resolutions": {
"styled-components": "^5"
},
"devEngines": {
"node": ">=14.x",
"npm": ">=7.x"
},
"browserslist": [],
"electronmon": {
"patterns": [
"!server",
"!src/renderer"
]
} }
} }
-14
View File
@@ -1,14 +0,0 @@
const path = require('path');
const replace = require('replace-in-file');
// fix long prisma loading times caused by scanning from process.cwd(), which returns "/" when run in electron
// (thus it scans all files on the computer.) See https://github.com/prisma/prisma/issues/8484
// solution: we get the app path from main process via sync IPC
const options = {
files: path.join(__dirname, '../release/app/node_modules/', '@prisma', 'client', 'index.js'),
from: 'findSync(process.cwd()',
to: `findSync(require("electron").ipcRenderer.sendSync('config:get-app-path')`,
};
const results = replace.sync(options);
console.log('build script: prisma fix', results);
-272
View File
@@ -1,272 +0,0 @@
generator client {
provider = "prisma-client-js"
engineType = "library"
binaryTargets = ["native", "windows"]
// output = "../release/app/node_modules/.prisma/client"
}
datasource db {
provider = "sqlite"
url = "file:../release/app/prisma/dev.db"
}
model User {
id Int @id @default(autoincrement())
name String
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
favorites Favorite[]
albumArtistRatings AlbumArtistRating[]
artistRatings ArtistRating[]
albumRatings AlbumRating[]
songRatings SongRating[]
}
model Server {
id Int @id @default(autoincrement())
nickname String @unique
url String @unique
remoteId String @map("remote_id")
authUsername String @map("auth_username")
authCredential String @map("auth_credential")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
serverType ServerType @relation(fields: [serverTypeId], references: [id])
serverTypeId Int
serverFolders ServerFolder[]
songs Song[]
albums Album[]
artists Artist[]
albumArtists AlbumArtist[]
// @@map("server")
}
model ServerType {
id Int @id @default(autoincrement())
name String @unique
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
Server Server[]
// @@map("server_type")
}
model ServerFolder {
id Int @id @default(autoincrement())
name String
remoteId String @map("remote_id")
enabled Boolean @default(true)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
server Server @relation(fields: [serverId], references: [id])
serverId Int
// @@map("server_folder")
}
model Genre {
id Int @id @default(autoincrement())
name String @unique
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
artists Artist[]
albumArtists AlbumArtist[]
albums Album[]
songs Song[]
// @@map("genre")
}
model Favorite {
id Int @id @default(autoincrement())
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
albumArtists AlbumArtist[]
artists Artist[]
albums Album[]
songs Song[]
User User? @relation(fields: [userId], references: [id])
userId Int?
// @@map("favorite")
}
model AlbumArtistRating {
id Int @id @default(autoincrement())
value Float
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id])
userId Int
albumArtist AlbumArtist? @relation(fields: [albumArtistId], references: [id])
albumArtistId Int
// @@map("album_artist_rating")
@@unique(fields: [albumArtistId, userId], name: "uniqueAlbumArtistRating", map: "unique_album_artist_rating")
}
model ArtistRating {
id Int @id @default(autoincrement())
value Float
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id])
userId Int
artist Artist? @relation(fields: [artistId], references: [id])
artistId Int
// @@map("artist_rating")
@@unique(fields: [artistId, userId], name: "uniqueArtistRating", map: "unique_artist_rating")
}
model AlbumRating {
id Int @id @default(autoincrement())
value Float
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id])
userId Int
album Album? @relation(fields: [albumId], references: [id])
albumId Int
// @@map("album_rating")
@@unique(fields: [albumId, userId], name: "uniqueAlbumRating", map: "unique_album_rating")
}
model SongRating {
id Int @id @default(autoincrement())
value Float
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id])
userId Int
song Song? @relation(fields: [songId], references: [id])
songId Int
// @@map("song_rating")
@@unique(fields: [songId, userId], name: "uniqueSongRating", map: "unique_song_rating")
}
model AlbumArtist {
id Int @id @default(autoincrement())
name String
image String?
image_remote String? @map("image_remote")
sortName String @map("sort_name")
biography String?
remoteId String @map("remote_id")
remoteCreatedAt DateTime? @map("remote_created_at")
deleted Boolean @default(false)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
genres Genre[]
albums Album[]
songs Song[]
favorites Favorite[]
ratings AlbumArtistRating[]
server Server @relation(fields: [serverId], references: [id])
serverId Int
// @@map("album_artist")
@@unique(fields: [serverId, remoteId], name: "uniqueAlbumArtistId", map: "unique_album_artist_id")
}
model Artist {
id Int @id @default(autoincrement())
name String
image String?
image_remote String? @map("image_remote")
biography String?
remoteId String @map("remote_id")
remoteCreatedAt DateTime? @map("remote_created_at")
deleted Boolean @default(false)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
genres Genre[]
favorites Favorite[]
ratings ArtistRating[]
server Server @relation(fields: [serverId], references: [id])
serverId Int
// @@map("artist")
@@unique(fields: [serverId, remoteId], name: "uniqueArtistId", map: "unique_artist_id")
}
model Album {
id Int @id @default(autoincrement())
name String
image String?
image_remote String? @map("image_remote")
releaseDate DateTime? @map("release_date")
releaseYear Int? @map("release_year")
remoteId String
remoteCreatedAt DateTime?
deleted Boolean @default(false)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
genres Genre[]
albumArtists AlbumArtist[]
favorites Favorite[]
ratings AlbumRating[]
server Server @relation(fields: [serverId], references: [id])
serverId Int @map("server_id")
// @@map("album")
@@unique(fields: [serverId, remoteId], name: "uniqueAlbumId", map: "unique_album_id")
}
model Song {
id Int @id @default(autoincrement())
name String
image String?
remote_image String? @map("remote_image")
releaseDate DateTime? @map("release_date")
releaseYear Int? @map("release_year")
duration Float?
lyric String?
bitRate Int @map("bit_rate")
container String
size String?
channels Int?
discIndex Int @default(1) @map("disc_index")
trackIndex Int? @map("track_index")
artistName String? @map("artist_name")
remoteId String @map("remote_id")
remoteCreatedAt DateTime? @map("remote_created_at")
deleted Boolean @default(false)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
genres Genre[]
albumArtists AlbumArtist[]
favorites Favorite[]
ratings SongRating[]
server Server @relation(fields: [serverId], references: [id])
serverId Int @map("server_id")
// @@map("song")
@@unique(fields: [serverId, remoteId], name: "uniqueSongId", map: "unique_song_id")
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

-15
View File
@@ -1,15 +0,0 @@
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
<defs>
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
<stop stop-color="#41D1FF"/>
<stop offset="1" stop-color="#BD34FE"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA83"/>
<stop offset="0.0833333" stop-color="#FFDD35"/>
<stop offset="1" stop-color="#FFA800"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

-7
View File
@@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

-15
View File
@@ -1,15 +0,0 @@
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
<defs>
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
<stop stop-color="#41D1FF"/>
<stop offset="1" stop-color="#BD34FE"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA83"/>
<stop offset="0.0833333" stop-color="#FFDD35"/>
<stop offset="1" stop-color="#FFA800"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

+1962 -61
View File
File diff suppressed because it is too large Load Diff
+13 -10
View File
@@ -1,19 +1,22 @@
{ {
"name": "sonixd-rewrite", "name": "feishin",
"version": "0.0.0", "version": "0.0.1-alpha6",
"description": "A foundation for scalable desktop apps", "description": "",
"main": "./dist/main/main.js", "main": "./dist/main/main.js",
"author": { "author": {
"name": "Electron React Boilerplate Maintainers", "name": "jeffvli",
"email": "electronreactboilerplate@gmail.com", "url": "https://github.com/jeffvli/"
"url": "https://github.com/electron-react-boilerplate" },
"scripts": {
"electron-rebuild": "node -r ts-node/register ../../.erb/scripts/electron-rebuild.js",
"link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.ts",
"postinstall": "npm run electron-rebuild && npm run link-modules"
}, },
"scripts": {},
"dependencies": { "dependencies": {
"@prisma/client": "4.4.0" "mpris-service": "^2.1.2"
}, },
"devDependencies": { "devDependencies": {
"prisma": "4.4.0" "electron": "22.3.1"
}, },
"license": "MIT" "license": "GPL-3.0"
} }
Binary file not shown.
-73
View File
@@ -1,73 +0,0 @@
import { useState } from "react";
import electron from "/electron.png";
import react from "/react.svg";
import vite from "/vite.svg";
import styles from "styles/app.module.scss";
const App: React.FC = () => {
const [count, setCount] = useState(0);
window.electron.doThing();
return (
<div className={styles.app}>
<header className={styles.appHeader}>
<div className={styles.logos}>
<div className={styles.imgBox}>
<img
src={electron}
style={{ height: "24vw" }}
className={styles.appLogo}
alt="electron"
/>
</div>
<div className={styles.imgBox}>
<img src={vite} style={{ height: "19vw" }} alt="vite" />
</div>
<div className={styles.imgBox}>
<img
src={react}
style={{ maxWidth: "100%" }}
className={styles.appLogo}
alt="logo"
/>
</div>
</div>
<p>Hello Electron + Vite + React!</p>
<p>
<button onClick={() => setCount((count) => count + 1)}>
count is: {count}
</button>
</p>
<p>
Edit <code>App.tsx</code> and save to test HMR updates.
</p>
<div>
<a
className={styles.appLink}
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
{" | "}
<a
className={styles.appLink}
href="https://vitejs.dev/guide/features.html"
target="_blank"
rel="noopener noreferrer"
>
Vite Docs
</a>
<div className={styles.staticPublic}>
Place static files into the <code>/public</code> folder
<img style={{ width: 77 }} src="./node.png" />
</div>
</div>
</header>
</div>
);
};
export default App;
+9
View File
@@ -0,0 +1,9 @@
import '@testing-library/jest-dom';
import { render } from '@testing-library/react';
import { App } from '../renderer/app';
describe('App', () => {
it('should render', () => {
expect(render(<App />)).toBeTruthy();
});
});
-65
View File
@@ -1,65 +0,0 @@
.app {
text-align: center;
.appHeader {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
.logos {
display: flex;
box-sizing: border-box;
align-items: center;
padding: 0 5vw;
width: 100%;
.imgBox {
width: 33.33%;
.appLogo {
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.appLogo {
animation: App-logo-spin infinite 20s linear;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
}
}
}
button {
font-size: calc(10px + 2vmin);
}
.appLink {
color: #61dafb;
}
.staticPublic {
display: flex;
align-items: center;
code {
padding: 4px 7px;
margin: 0 4px;
border-radius: 4px;
background-color: rgb(30, 30, 30, .7);
font-size: 13px;
}
}
}
}
-13
View File
@@ -1,13 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
-15
View File
@@ -1,15 +0,0 @@
import { Prisma } from '@prisma/client';
type ApiSurface = {
prisma: {
server: {
getServer: () => Promise<Prisma.ServerSelect>;
};
};
};
declare global {
interface Window {
electron: ApiSurface;
}
}
+32
View File
@@ -0,0 +1,32 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
const en = require('./locales/en.json');
const resources = {
en: { translation: en },
};
export const Languages = [
{
label: 'English',
value: 'en',
},
];
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.init({
fallbackLng: 'en',
// language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
// if you're using a language detector, do not define the lng option
interpolation: {
escapeValue: false, // react already safes from xss
},
lng: 'en',
resources,
});
export default i18n;

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