aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--package.json4
-rw-r--r--pnpm-lock.yaml235
m---------recipes0
-rw-r--r--src/components/services/tabs/TabItem.tsx8
-rw-r--r--src/components/settings/settings/EditSettingsForm.tsx125
-rw-r--r--src/config.ts1
-rw-r--r--src/containers/settings/EditSettingsScreen.tsx244
-rw-r--r--src/electron/ipc-api/dnd.ts1
-rw-r--r--src/electron/ipc-api/download.ts10
-rw-r--r--src/features/workspaces/components/WorkspaceDrawerItem.tsx8
-rw-r--r--src/i18n/locales/de.json21
-rw-r--r--src/i18n/locales/en-US.json9
-rw-r--r--src/i18n/locales/ru.json13
-rw-r--r--src/index.ts27
-rw-r--r--src/jsUtils.ts21
-rw-r--r--src/lib/Menu.ts17
-rw-r--r--src/models/Service.ts13
-rw-r--r--src/stores/ServicesStore.ts3
-rw-r--r--src/stores/UserStore.ts2
-rw-r--r--src/webview/contextMenuBuilder.ts59
-rw-r--r--src/webview/notifications.ts75
-rw-r--r--src/webview/recipe.ts2
-rw-r--r--test/jsUtils.test.ts46
23 files changed, 624 insertions, 320 deletions
diff --git a/package.json b/package.json
index 2df26d41f..0256815f0 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
3 "productName": "Ferdium", 3 "productName": "Ferdium",
4 "desktopName": "ferdium.desktop", 4 "desktopName": "ferdium.desktop",
5 "appId": "org.ferdium.ferdium-app", 5 "appId": "org.ferdium.ferdium-app",
6 "version": "6.7.4-nightly.3", 6 "version": "6.7.5-nightly.5",
7 "description": "Messaging app for WhatsApp, Slack, Telegram, Hangouts and many many more.", 7 "description": "Messaging app for WhatsApp, Slack, Telegram, Hangouts and many many more.",
8 "author": "Ferdium Contributors <hello@ferdium.org> (https://ferdium.org/)", 8 "author": "Ferdium Contributors <hello@ferdium.org> (https://ferdium.org/)",
9 "license": "Apache-2.0", 9 "license": "Apache-2.0",
@@ -163,7 +163,7 @@
163 "chalk": "5.3.0", 163 "chalk": "5.3.0",
164 "concurrently": "8.2.2", 164 "concurrently": "8.2.2",
165 "cross-env": "7.0.3", 165 "cross-env": "7.0.3",
166 "electron": "30.0.2", 166 "electron": "30.0.8",
167 "electron-builder": "24.13.3", 167 "electron-builder": "24.13.3",
168 "esbuild": "0.16.17", 168 "esbuild": "0.16.17",
169 "esbuild-plugin-copy": "2.1.1", 169 "esbuild-plugin-copy": "2.1.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0f0499ed6..03ee0a63e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -40,7 +40,7 @@ dependencies:
40 version: 5.1.0 40 version: 5.1.0
41 '@electron/remote': 41 '@electron/remote':
42 specifier: 2.1.2 42 specifier: 2.1.2
43 version: 2.1.2(electron@30.0.2) 43 version: 2.1.2(electron@30.0.8)
44 '@emotion/react': 44 '@emotion/react':
45 specifier: 11.11.4 45 specifier: 11.11.4
46 version: 11.11.4(@types/react@18.2.77)(react@18.2.0) 46 version: 11.11.4(@types/react@18.2.77)(react@18.2.0)
@@ -70,7 +70,7 @@ dependencies:
70 version: 7.4.0(history@5.3.0)(mobx@6.12.3)(path-to-regexp@6.2.2) 70 version: 7.4.0(history@5.3.0)(mobx@6.12.3)(path-to-regexp@6.2.2)
71 '@syed_umair/electron-process-manager': 71 '@syed_umair/electron-process-manager':
72 specifier: 1.1.0 72 specifier: 1.1.0
73 version: 1.1.0(electron@30.0.2) 73 version: 1.1.0(electron@30.0.8)
74 auto-launch: 74 auto-launch:
75 specifier: 5.0.6 75 specifier: 5.0.6
76 version: 5.0.6 76 version: 5.0.6
@@ -202,7 +202,7 @@ dependencies:
202 version: 10.10.0(react@18.2.0) 202 version: 10.10.0(react@18.2.0)
203 react-loader-spinner: 203 react-loader-spinner:
204 specifier: 5.4.5 204 specifier: 5.4.5
205 version: 5.4.5(@babel/core@7.24.5)(react-dom@18.2.0)(react@18.2.0) 205 version: 5.4.5(@babel/core@7.24.6)(react-dom@18.2.0)(react@18.2.0)
206 react-modal: 206 react-modal:
207 specifier: 3.16.1 207 specifier: 3.16.1
208 version: 3.16.1(react-dom@18.2.0)(react@18.2.0) 208 version: 3.16.1(react-dom@18.2.0)(react@18.2.0)
@@ -352,8 +352,8 @@ devDependencies:
352 specifier: 7.0.3 352 specifier: 7.0.3
353 version: 7.0.3 353 version: 7.0.3
354 electron: 354 electron:
355 specifier: 30.0.2 355 specifier: 30.0.8
356 version: 30.0.2 356 version: 30.0.8
357 electron-builder: 357 electron-builder:
358 specifier: 24.13.3 358 specifier: 24.13.3
359 version: 24.13.3(electron-builder-squirrel-windows@24.13.3) 359 version: 24.13.3(electron-builder-squirrel-windows@24.13.3)
@@ -401,7 +401,7 @@ devDependencies:
401 version: 4.6.0(eslint@8.57.0) 401 version: 4.6.0(eslint@8.57.0)
402 eslint-plugin-sonar: 402 eslint-plugin-sonar:
403 specifier: 0.13.2 403 specifier: 0.13.2
404 version: 0.13.2(@babel/core@7.24.5)(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5) 404 version: 0.13.2(@babel/core@7.24.6)(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5)
405 eslint-plugin-unicorn: 405 eslint-plugin-unicorn:
406 specifier: 52.0.0 406 specifier: 52.0.0
407 version: 52.0.0(eslint@8.57.0) 407 version: 52.0.0(eslint@8.57.0)
@@ -641,20 +641,20 @@ packages:
641 '@babel/highlight': 7.23.4 641 '@babel/highlight': 7.23.4
642 chalk: 2.4.2 642 chalk: 2.4.2
643 643
644 /@babel/code-frame@7.24.2: 644 /@babel/code-frame@7.24.6:
645 resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} 645 resolution: {integrity: sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==}
646 engines: {node: '>=6.9.0'} 646 engines: {node: '>=6.9.0'}
647 dependencies: 647 dependencies:
648 '@babel/highlight': 7.24.5 648 '@babel/highlight': 7.24.6
649 picocolors: 1.0.0 649 picocolors: 1.0.1
650 650
651 /@babel/compat-data@7.22.20: 651 /@babel/compat-data@7.22.20:
652 resolution: {integrity: sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==} 652 resolution: {integrity: sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==}
653 engines: {node: '>=6.9.0'} 653 engines: {node: '>=6.9.0'}
654 dev: true 654 dev: true
655 655
656 /@babel/compat-data@7.24.4: 656 /@babel/compat-data@7.24.6:
657 resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==} 657 resolution: {integrity: sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==}
658 engines: {node: '>=6.9.0'} 658 engines: {node: '>=6.9.0'}
659 659
660 /@babel/core@7.23.5: 660 /@babel/core@7.23.5:
@@ -680,20 +680,20 @@ packages:
680 - supports-color 680 - supports-color
681 dev: true 681 dev: true
682 682
683 /@babel/core@7.24.5: 683 /@babel/core@7.24.6:
684 resolution: {integrity: sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==} 684 resolution: {integrity: sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==}
685 engines: {node: '>=6.9.0'} 685 engines: {node: '>=6.9.0'}
686 dependencies: 686 dependencies:
687 '@ampproject/remapping': 2.3.0 687 '@ampproject/remapping': 2.3.0
688 '@babel/code-frame': 7.24.2 688 '@babel/code-frame': 7.24.6
689 '@babel/generator': 7.24.5 689 '@babel/generator': 7.24.6
690 '@babel/helper-compilation-targets': 7.23.6 690 '@babel/helper-compilation-targets': 7.24.6
691 '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) 691 '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6)
692 '@babel/helpers': 7.24.5 692 '@babel/helpers': 7.24.6
693 '@babel/parser': 7.24.5 693 '@babel/parser': 7.24.6
694 '@babel/template': 7.24.0 694 '@babel/template': 7.24.6
695 '@babel/traverse': 7.24.5 695 '@babel/traverse': 7.24.6
696 '@babel/types': 7.24.5 696 '@babel/types': 7.24.6
697 convert-source-map: 2.0.0 697 convert-source-map: 2.0.0
698 debug: 4.3.4(supports-color@5.5.0) 698 debug: 4.3.4(supports-color@5.5.0)
699 gensync: 1.0.0-beta.2 699 gensync: 1.0.0-beta.2
@@ -702,14 +702,14 @@ packages:
702 transitivePeerDependencies: 702 transitivePeerDependencies:
703 - supports-color 703 - supports-color
704 704
705 /@babel/eslint-parser@7.23.10(@babel/core@7.24.5)(eslint@8.57.0): 705 /@babel/eslint-parser@7.23.10(@babel/core@7.24.6)(eslint@8.57.0):
706 resolution: {integrity: sha512-3wSYDPZVnhseRnxRJH6ZVTNknBz76AEnyC+AYYhasjP3Yy23qz0ERR7Fcd2SHmYuSFJ2kY9gaaDd3vyqU09eSw==} 706 resolution: {integrity: sha512-3wSYDPZVnhseRnxRJH6ZVTNknBz76AEnyC+AYYhasjP3Yy23qz0ERR7Fcd2SHmYuSFJ2kY9gaaDd3vyqU09eSw==}
707 engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} 707 engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0}
708 peerDependencies: 708 peerDependencies:
709 '@babel/core': ^7.11.0 709 '@babel/core': ^7.11.0
710 eslint: ^7.5.0 || ^8.0.0 710 eslint: ^7.5.0 || ^8.0.0
711 dependencies: 711 dependencies:
712 '@babel/core': 7.24.5 712 '@babel/core': 7.24.6
713 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 713 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1
714 eslint: 8.57.0 714 eslint: 8.57.0
715 eslint-visitor-keys: 2.1.0 715 eslint-visitor-keys: 2.1.0
@@ -725,11 +725,11 @@ packages:
725 '@jridgewell/trace-mapping': 0.3.20 725 '@jridgewell/trace-mapping': 0.3.20
726 jsesc: 2.5.2 726 jsesc: 2.5.2
727 727
728 /@babel/generator@7.24.5: 728 /@babel/generator@7.24.6:
729 resolution: {integrity: sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==} 729 resolution: {integrity: sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==}
730 engines: {node: '>=6.9.0'} 730 engines: {node: '>=6.9.0'}
731 dependencies: 731 dependencies:
732 '@babel/types': 7.24.5 732 '@babel/types': 7.24.6
733 '@jridgewell/gen-mapping': 0.3.5 733 '@jridgewell/gen-mapping': 0.3.5
734 '@jridgewell/trace-mapping': 0.3.25 734 '@jridgewell/trace-mapping': 0.3.25
735 jsesc: 2.5.2 735 jsesc: 2.5.2
@@ -752,12 +752,12 @@ packages:
752 semver: 6.3.1 752 semver: 6.3.1
753 dev: true 753 dev: true
754 754
755 /@babel/helper-compilation-targets@7.23.6: 755 /@babel/helper-compilation-targets@7.24.6:
756 resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} 756 resolution: {integrity: sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==}
757 engines: {node: '>=6.9.0'} 757 engines: {node: '>=6.9.0'}
758 dependencies: 758 dependencies:
759 '@babel/compat-data': 7.24.4 759 '@babel/compat-data': 7.24.6
760 '@babel/helper-validator-option': 7.23.5 760 '@babel/helper-validator-option': 7.24.6
761 browserslist: 4.23.0 761 browserslist: 4.23.0
762 lru-cache: 5.1.1 762 lru-cache: 5.1.1
763 semver: 6.3.1 763 semver: 6.3.1
@@ -766,6 +766,10 @@ packages:
766 resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} 766 resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
767 engines: {node: '>=6.9.0'} 767 engines: {node: '>=6.9.0'}
768 768
769 /@babel/helper-environment-visitor@7.24.6:
770 resolution: {integrity: sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==}
771 engines: {node: '>=6.9.0'}
772
769 /@babel/helper-function-name@7.23.0: 773 /@babel/helper-function-name@7.23.0:
770 resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} 774 resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
771 engines: {node: '>=6.9.0'} 775 engines: {node: '>=6.9.0'}
@@ -773,23 +777,36 @@ packages:
773 '@babel/template': 7.22.15 777 '@babel/template': 7.22.15
774 '@babel/types': 7.23.5 778 '@babel/types': 7.23.5
775 779
780 /@babel/helper-function-name@7.24.6:
781 resolution: {integrity: sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==}
782 engines: {node: '>=6.9.0'}
783 dependencies:
784 '@babel/template': 7.24.6
785 '@babel/types': 7.24.6
786
776 /@babel/helper-hoist-variables@7.22.5: 787 /@babel/helper-hoist-variables@7.22.5:
777 resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} 788 resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
778 engines: {node: '>=6.9.0'} 789 engines: {node: '>=6.9.0'}
779 dependencies: 790 dependencies:
780 '@babel/types': 7.23.5 791 '@babel/types': 7.23.5
781 792
793 /@babel/helper-hoist-variables@7.24.6:
794 resolution: {integrity: sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==}
795 engines: {node: '>=6.9.0'}
796 dependencies:
797 '@babel/types': 7.24.6
798
782 /@babel/helper-module-imports@7.22.15: 799 /@babel/helper-module-imports@7.22.15:
783 resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} 800 resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
784 engines: {node: '>=6.9.0'} 801 engines: {node: '>=6.9.0'}
785 dependencies: 802 dependencies:
786 '@babel/types': 7.23.5 803 '@babel/types': 7.23.5
787 804
788 /@babel/helper-module-imports@7.24.3: 805 /@babel/helper-module-imports@7.24.6:
789 resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==} 806 resolution: {integrity: sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==}
790 engines: {node: '>=6.9.0'} 807 engines: {node: '>=6.9.0'}
791 dependencies: 808 dependencies:
792 '@babel/types': 7.24.5 809 '@babel/types': 7.24.6
793 810
794 /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5): 811 /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5):
795 resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} 812 resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
@@ -805,18 +822,18 @@ packages:
805 '@babel/helper-validator-identifier': 7.22.20 822 '@babel/helper-validator-identifier': 7.22.20
806 dev: true 823 dev: true
807 824
808 /@babel/helper-module-transforms@7.24.5(@babel/core@7.24.5): 825 /@babel/helper-module-transforms@7.24.6(@babel/core@7.24.6):
809 resolution: {integrity: sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==} 826 resolution: {integrity: sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==}
810 engines: {node: '>=6.9.0'} 827 engines: {node: '>=6.9.0'}
811 peerDependencies: 828 peerDependencies:
812 '@babel/core': ^7.0.0 829 '@babel/core': ^7.0.0
813 dependencies: 830 dependencies:
814 '@babel/core': 7.24.5 831 '@babel/core': 7.24.6
815 '@babel/helper-environment-visitor': 7.22.20 832 '@babel/helper-environment-visitor': 7.24.6
816 '@babel/helper-module-imports': 7.24.3 833 '@babel/helper-module-imports': 7.24.6
817 '@babel/helper-simple-access': 7.24.5 834 '@babel/helper-simple-access': 7.24.6
818 '@babel/helper-split-export-declaration': 7.24.5 835 '@babel/helper-split-export-declaration': 7.24.6
819 '@babel/helper-validator-identifier': 7.24.5 836 '@babel/helper-validator-identifier': 7.24.6
820 837
821 /@babel/helper-plugin-utils@7.22.5: 838 /@babel/helper-plugin-utils@7.22.5:
822 resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} 839 resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==}
@@ -829,11 +846,11 @@ packages:
829 '@babel/types': 7.23.5 846 '@babel/types': 7.23.5
830 dev: true 847 dev: true
831 848
832 /@babel/helper-simple-access@7.24.5: 849 /@babel/helper-simple-access@7.24.6:
833 resolution: {integrity: sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==} 850 resolution: {integrity: sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==}
834 engines: {node: '>=6.9.0'} 851 engines: {node: '>=6.9.0'}
835 dependencies: 852 dependencies:
836 '@babel/types': 7.24.5 853 '@babel/types': 7.24.6
837 854
838 /@babel/helper-split-export-declaration@7.22.6: 855 /@babel/helper-split-export-declaration@7.22.6:
839 resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} 856 resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
@@ -841,26 +858,26 @@ packages:
841 dependencies: 858 dependencies:
842 '@babel/types': 7.23.5 859 '@babel/types': 7.23.5
843 860
844 /@babel/helper-split-export-declaration@7.24.5: 861 /@babel/helper-split-export-declaration@7.24.6:
845 resolution: {integrity: sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==} 862 resolution: {integrity: sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==}
846 engines: {node: '>=6.9.0'} 863 engines: {node: '>=6.9.0'}
847 dependencies: 864 dependencies:
848 '@babel/types': 7.24.5 865 '@babel/types': 7.24.6
849 866
850 /@babel/helper-string-parser@7.23.4: 867 /@babel/helper-string-parser@7.23.4:
851 resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} 868 resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
852 engines: {node: '>=6.9.0'} 869 engines: {node: '>=6.9.0'}
853 870
854 /@babel/helper-string-parser@7.24.1: 871 /@babel/helper-string-parser@7.24.6:
855 resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} 872 resolution: {integrity: sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==}
856 engines: {node: '>=6.9.0'} 873 engines: {node: '>=6.9.0'}
857 874
858 /@babel/helper-validator-identifier@7.22.20: 875 /@babel/helper-validator-identifier@7.22.20:
859 resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} 876 resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
860 engines: {node: '>=6.9.0'} 877 engines: {node: '>=6.9.0'}
861 878
862 /@babel/helper-validator-identifier@7.24.5: 879 /@babel/helper-validator-identifier@7.24.6:
863 resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==} 880 resolution: {integrity: sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==}
864 engines: {node: '>=6.9.0'} 881 engines: {node: '>=6.9.0'}
865 882
866 /@babel/helper-validator-option@7.22.15: 883 /@babel/helper-validator-option@7.22.15:
@@ -868,8 +885,8 @@ packages:
868 engines: {node: '>=6.9.0'} 885 engines: {node: '>=6.9.0'}
869 dev: true 886 dev: true
870 887
871 /@babel/helper-validator-option@7.23.5: 888 /@babel/helper-validator-option@7.24.6:
872 resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} 889 resolution: {integrity: sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==}
873 engines: {node: '>=6.9.0'} 890 engines: {node: '>=6.9.0'}
874 891
875 /@babel/helpers@7.23.5: 892 /@babel/helpers@7.23.5:
@@ -883,15 +900,12 @@ packages:
883 - supports-color 900 - supports-color
884 dev: true 901 dev: true
885 902
886 /@babel/helpers@7.24.5: 903 /@babel/helpers@7.24.6:
887 resolution: {integrity: sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==} 904 resolution: {integrity: sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==}
888 engines: {node: '>=6.9.0'} 905 engines: {node: '>=6.9.0'}
889 dependencies: 906 dependencies:
890 '@babel/template': 7.24.0 907 '@babel/template': 7.24.6
891 '@babel/traverse': 7.24.5 908 '@babel/types': 7.24.6
892 '@babel/types': 7.24.5
893 transitivePeerDependencies:
894 - supports-color
895 909
896 /@babel/highlight@7.23.4: 910 /@babel/highlight@7.23.4:
897 resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} 911 resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
@@ -901,14 +915,14 @@ packages:
901 chalk: 2.4.2 915 chalk: 2.4.2
902 js-tokens: 4.0.0 916 js-tokens: 4.0.0
903 917
904 /@babel/highlight@7.24.5: 918 /@babel/highlight@7.24.6:
905 resolution: {integrity: sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==} 919 resolution: {integrity: sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==}
906 engines: {node: '>=6.9.0'} 920 engines: {node: '>=6.9.0'}
907 dependencies: 921 dependencies:
908 '@babel/helper-validator-identifier': 7.24.5 922 '@babel/helper-validator-identifier': 7.24.6
909 chalk: 2.4.2 923 chalk: 2.4.2
910 js-tokens: 4.0.0 924 js-tokens: 4.0.0
911 picocolors: 1.0.0 925 picocolors: 1.0.1
912 926
913 /@babel/parser@7.23.5: 927 /@babel/parser@7.23.5:
914 resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==} 928 resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==}
@@ -917,12 +931,12 @@ packages:
917 dependencies: 931 dependencies:
918 '@babel/types': 7.23.5 932 '@babel/types': 7.23.5
919 933
920 /@babel/parser@7.24.5: 934 /@babel/parser@7.24.6:
921 resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} 935 resolution: {integrity: sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==}
922 engines: {node: '>=6.0.0'} 936 engines: {node: '>=6.0.0'}
923 hasBin: true 937 hasBin: true
924 dependencies: 938 dependencies:
925 '@babel/types': 7.24.5 939 '@babel/types': 7.24.6
926 940
927 /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.5): 941 /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.5):
928 resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} 942 resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
@@ -979,13 +993,13 @@ packages:
979 '@babel/helper-plugin-utils': 7.22.5 993 '@babel/helper-plugin-utils': 7.22.5
980 dev: true 994 dev: true
981 995
982 /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.24.5): 996 /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.24.6):
983 resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} 997 resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==}
984 engines: {node: '>=6.9.0'} 998 engines: {node: '>=6.9.0'}
985 peerDependencies: 999 peerDependencies:
986 '@babel/core': ^7.0.0-0 1000 '@babel/core': ^7.0.0-0
987 dependencies: 1001 dependencies:
988 '@babel/core': 7.24.5 1002 '@babel/core': 7.24.6
989 '@babel/helper-plugin-utils': 7.22.5 1003 '@babel/helper-plugin-utils': 7.22.5
990 dev: false 1004 dev: false
991 1005
@@ -1090,13 +1104,13 @@ packages:
1090 '@babel/parser': 7.23.5 1104 '@babel/parser': 7.23.5
1091 '@babel/types': 7.23.5 1105 '@babel/types': 7.23.5
1092 1106
1093 /@babel/template@7.24.0: 1107 /@babel/template@7.24.6:
1094 resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} 1108 resolution: {integrity: sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==}
1095 engines: {node: '>=6.9.0'} 1109 engines: {node: '>=6.9.0'}
1096 dependencies: 1110 dependencies:
1097 '@babel/code-frame': 7.24.2 1111 '@babel/code-frame': 7.24.6
1098 '@babel/parser': 7.24.5 1112 '@babel/parser': 7.24.6
1099 '@babel/types': 7.24.5 1113 '@babel/types': 7.24.6
1100 1114
1101 /@babel/traverse@7.23.5(supports-color@5.5.0): 1115 /@babel/traverse@7.23.5(supports-color@5.5.0):
1102 resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==} 1116 resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==}
@@ -1115,18 +1129,18 @@ packages:
1115 transitivePeerDependencies: 1129 transitivePeerDependencies:
1116 - supports-color 1130 - supports-color
1117 1131
1118 /@babel/traverse@7.24.5: 1132 /@babel/traverse@7.24.6:
1119 resolution: {integrity: sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==} 1133 resolution: {integrity: sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==}
1120 engines: {node: '>=6.9.0'} 1134 engines: {node: '>=6.9.0'}
1121 dependencies: 1135 dependencies:
1122 '@babel/code-frame': 7.24.2 1136 '@babel/code-frame': 7.24.6
1123 '@babel/generator': 7.24.5 1137 '@babel/generator': 7.24.6
1124 '@babel/helper-environment-visitor': 7.22.20 1138 '@babel/helper-environment-visitor': 7.24.6
1125 '@babel/helper-function-name': 7.23.0 1139 '@babel/helper-function-name': 7.24.6
1126 '@babel/helper-hoist-variables': 7.22.5 1140 '@babel/helper-hoist-variables': 7.24.6
1127 '@babel/helper-split-export-declaration': 7.24.5 1141 '@babel/helper-split-export-declaration': 7.24.6
1128 '@babel/parser': 7.24.5 1142 '@babel/parser': 7.24.6
1129 '@babel/types': 7.24.5 1143 '@babel/types': 7.24.6
1130 debug: 4.3.4(supports-color@5.5.0) 1144 debug: 4.3.4(supports-color@5.5.0)
1131 globals: 11.12.0 1145 globals: 11.12.0
1132 transitivePeerDependencies: 1146 transitivePeerDependencies:
@@ -1140,12 +1154,12 @@ packages:
1140 '@babel/helper-validator-identifier': 7.22.20 1154 '@babel/helper-validator-identifier': 7.22.20
1141 to-fast-properties: 2.0.0 1155 to-fast-properties: 2.0.0
1142 1156
1143 /@babel/types@7.24.5: 1157 /@babel/types@7.24.6:
1144 resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==} 1158 resolution: {integrity: sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==}
1145 engines: {node: '>=6.9.0'} 1159 engines: {node: '>=6.9.0'}
1146 dependencies: 1160 dependencies:
1147 '@babel/helper-string-parser': 7.24.1 1161 '@babel/helper-string-parser': 7.24.6
1148 '@babel/helper-validator-identifier': 7.24.5 1162 '@babel/helper-validator-identifier': 7.24.6
1149 to-fast-properties: 2.0.0 1163 to-fast-properties: 2.0.0
1150 1164
1151 /@bcoe/v8-coverage@0.2.3: 1165 /@bcoe/v8-coverage@0.2.3:
@@ -1494,12 +1508,12 @@ packages:
1494 - supports-color 1508 - supports-color
1495 dev: true 1509 dev: true
1496 1510
1497 /@electron/remote@2.1.2(electron@30.0.2): 1511 /@electron/remote@2.1.2(electron@30.0.8):
1498 resolution: {integrity: sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==} 1512 resolution: {integrity: sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==}
1499 peerDependencies: 1513 peerDependencies:
1500 electron: '>= 13.0.0' 1514 electron: '>= 13.0.0'
1501 dependencies: 1515 dependencies:
1502 electron: 30.0.2 1516 electron: 30.0.8
1503 dev: false 1517 dev: false
1504 1518
1505 /@electron/universal@1.5.1: 1519 /@electron/universal@1.5.1:
@@ -3024,12 +3038,12 @@ packages:
3024 path-to-regexp: 6.2.2 3038 path-to-regexp: 6.2.2
3025 dev: false 3039 dev: false
3026 3040
3027 /@syed_umair/electron-process-manager@1.1.0(electron@30.0.2): 3041 /@syed_umair/electron-process-manager@1.1.0(electron@30.0.8):
3028 resolution: {integrity: sha512-rbDJ1GQ51DcsP+354WpI/3xWOra1HH4cT1jA0maK8VJogn7s5BiMO2XkSBaCoeMCvTIIwx7Y6dgUHpfnL2M6wg==} 3042 resolution: {integrity: sha512-rbDJ1GQ51DcsP+354WpI/3xWOra1HH4cT1jA0maK8VJogn7s5BiMO2XkSBaCoeMCvTIIwx7Y6dgUHpfnL2M6wg==}
3029 peerDependencies: 3043 peerDependencies:
3030 electron: '>=21' 3044 electron: '>=21'
3031 dependencies: 3045 dependencies:
3032 electron: 30.0.2 3046 electron: 30.0.8
3033 electron-process-reporter: 1.4.0 3047 electron-process-reporter: 1.4.0
3034 dev: false 3048 dev: false
3035 3049
@@ -4535,17 +4549,17 @@ packages:
4535 resolve: 1.22.8 4549 resolve: 1.22.8
4536 dev: false 4550 dev: false
4537 4551
4538 /babel-plugin-styled-components@2.1.4(@babel/core@7.24.5)(styled-components@5.3.11): 4552 /babel-plugin-styled-components@2.1.4(@babel/core@7.24.6)(styled-components@5.3.11):
4539 resolution: {integrity: sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==} 4553 resolution: {integrity: sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==}
4540 peerDependencies: 4554 peerDependencies:
4541 styled-components: '>= 2' 4555 styled-components: '>= 2'
4542 dependencies: 4556 dependencies:
4543 '@babel/helper-annotate-as-pure': 7.22.5 4557 '@babel/helper-annotate-as-pure': 7.22.5
4544 '@babel/helper-module-imports': 7.22.15 4558 '@babel/helper-module-imports': 7.22.15
4545 '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.24.5) 4559 '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.24.6)
4546 lodash: 4.17.21 4560 lodash: 4.17.21
4547 picomatch: 2.3.1 4561 picomatch: 2.3.1
4548 styled-components: 5.3.11(@babel/core@7.24.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) 4562 styled-components: 5.3.11(@babel/core@7.24.6)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
4549 transitivePeerDependencies: 4563 transitivePeerDependencies:
4550 - '@babel/core' 4564 - '@babel/core'
4551 dev: false 4565 dev: false
@@ -4722,7 +4736,7 @@ packages:
4722 engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 4736 engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
4723 hasBin: true 4737 hasBin: true
4724 dependencies: 4738 dependencies:
4725 caniuse-lite: 1.0.30001616 4739 caniuse-lite: 1.0.30001621
4726 electron-to-chromium: 1.4.710 4740 electron-to-chromium: 1.4.710
4727 node-releases: 2.0.14 4741 node-releases: 2.0.14
4728 update-browserslist-db: 1.0.13(browserslist@4.23.0) 4742 update-browserslist-db: 1.0.13(browserslist@4.23.0)
@@ -4973,8 +4987,8 @@ packages:
4973 resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} 4987 resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
4974 dev: false 4988 dev: false
4975 4989
4976 /caniuse-lite@1.0.30001616: 4990 /caniuse-lite@1.0.30001621:
4977 resolution: {integrity: sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==} 4991 resolution: {integrity: sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==}
4978 4992
4979 /caseless@0.12.0: 4993 /caseless@0.12.0:
4980 resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} 4994 resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
@@ -6217,8 +6231,8 @@ packages:
6217 mkdirp: 0.5.6 6231 mkdirp: 0.5.6
6218 dev: false 6232 dev: false
6219 6233
6220 /electron@30.0.2: 6234 /electron@30.0.8:
6221 resolution: {integrity: sha512-zv7T+GG89J/hyWVkQsLH4Y/rVEfqJG5M/wOBIGNaDdqd8UV9/YZPdS7CuFeaIj0H9LhCt95xkIQNpYB/3svOkQ==} 6235 resolution: {integrity: sha512-ivzXJJ/9gdb4oOw+5SDuaZpSInz8C+Z021dKZfFLMltKbDa4sSqt5cRBiUg7J36Z2kdus+Jai0bdHWutYE9wAA==}
6222 engines: {node: '>= 12.20.55'} 6236 engines: {node: '>= 12.20.55'}
6223 hasBin: true 6237 hasBin: true
6224 requiresBuild: true 6238 requiresBuild: true
@@ -6970,7 +6984,7 @@ packages:
6970 string.prototype.matchall: 4.0.11 6984 string.prototype.matchall: 4.0.11
6971 dev: true 6985 dev: true
6972 6986
6973 /eslint-plugin-sonar@0.13.2(@babel/core@7.24.5)(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5): 6987 /eslint-plugin-sonar@0.13.2(@babel/core@7.24.6)(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5):
6974 resolution: {integrity: sha512-7mZkk4/E2tGtIi3OCNCTmEWthweWzoAj9YcsxxIDI38r7q4RDusWDpsdGHPZXp/+QU8ZKkvmE11G4DtJW+k1kg==} 6988 resolution: {integrity: sha512-7mZkk4/E2tGtIi3OCNCTmEWthweWzoAj9YcsxxIDI38r7q4RDusWDpsdGHPZXp/+QU8ZKkvmE11G4DtJW+k1kg==}
6975 engines: {node: '>=14'} 6989 engines: {node: '>=14'}
6976 peerDependencies: 6990 peerDependencies:
@@ -6979,8 +6993,8 @@ packages:
6979 eslint: ^8.0.0 6993 eslint: ^8.0.0
6980 typescript: ^4.0.0 || ^5.0.0 6994 typescript: ^4.0.0 || ^5.0.0
6981 dependencies: 6995 dependencies:
6982 '@babel/core': 7.24.5 6996 '@babel/core': 7.24.6
6983 '@babel/eslint-parser': 7.23.10(@babel/core@7.24.5)(eslint@8.57.0) 6997 '@babel/eslint-parser': 7.23.10(@babel/core@7.24.6)(eslint@8.57.0)
6984 '@eslint-community/regexpp': 4.10.0 6998 '@eslint-community/regexpp': 4.10.0
6985 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5) 6999 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5)
6986 '@typescript-eslint/parser': 7.6.0(eslint@8.57.0)(typescript@5.4.5) 7000 '@typescript-eslint/parser': 7.6.0(eslint@8.57.0)(typescript@5.4.5)
@@ -11322,6 +11336,9 @@ packages:
11322 /picocolors@1.0.0: 11336 /picocolors@1.0.0:
11323 resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 11337 resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
11324 11338
11339 /picocolors@1.0.1:
11340 resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
11341
11325 /picomatch@2.3.1: 11342 /picomatch@2.3.1:
11326 resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 11343 resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
11327 engines: {node: '>=8.6'} 11344 engines: {node: '>=8.6'}
@@ -11958,7 +11975,7 @@ packages:
11958 resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} 11975 resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==}
11959 dev: false 11976 dev: false
11960 11977
11961 /react-loader-spinner@5.4.5(@babel/core@7.24.5)(react-dom@18.2.0)(react@18.2.0): 11978 /react-loader-spinner@5.4.5(@babel/core@7.24.6)(react-dom@18.2.0)(react@18.2.0):
11962 resolution: {integrity: sha512-32f+sb/v2tnNfyvnCCOS4fpyVHsGXjSyNo6QLniHcaj1XjKLxx14L2z0h6szRugOL8IEJ+53GPwNAdbkDqmy4g==} 11979 resolution: {integrity: sha512-32f+sb/v2tnNfyvnCCOS4fpyVHsGXjSyNo6QLniHcaj1XjKLxx14L2z0h6szRugOL8IEJ+53GPwNAdbkDqmy4g==}
11963 peerDependencies: 11980 peerDependencies:
11964 react: ^16.0.0 || ^17.0.0 || ^18.0.0 11981 react: ^16.0.0 || ^17.0.0 || ^18.0.0
@@ -11967,7 +11984,7 @@ packages:
11967 react: 18.2.0 11984 react: 18.2.0
11968 react-dom: 18.2.0(react@18.2.0) 11985 react-dom: 18.2.0(react@18.2.0)
11969 react-is: 18.2.0 11986 react-is: 18.2.0
11970 styled-components: 5.3.11(@babel/core@7.24.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) 11987 styled-components: 5.3.11(@babel/core@7.24.6)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
11971 styled-tools: 1.7.2 11988 styled-tools: 1.7.2
11972 transitivePeerDependencies: 11989 transitivePeerDependencies:
11973 - '@babel/core' 11990 - '@babel/core'
@@ -13402,7 +13419,7 @@ packages:
13402 engines: {node: '>=8'} 13419 engines: {node: '>=8'}
13403 dev: true 13420 dev: true
13404 13421
13405 /styled-components@5.3.11(@babel/core@7.24.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0): 13422 /styled-components@5.3.11(@babel/core@7.24.6)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0):
13406 resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==} 13423 resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==}
13407 engines: {node: '>=10'} 13424 engines: {node: '>=10'}
13408 peerDependencies: 13425 peerDependencies:
@@ -13415,7 +13432,7 @@ packages:
13415 '@emotion/is-prop-valid': 1.2.1 13432 '@emotion/is-prop-valid': 1.2.1
13416 '@emotion/stylis': 0.8.5 13433 '@emotion/stylis': 0.8.5
13417 '@emotion/unitless': 0.7.5 13434 '@emotion/unitless': 0.7.5
13418 babel-plugin-styled-components: 2.1.4(@babel/core@7.24.5)(styled-components@5.3.11) 13435 babel-plugin-styled-components: 2.1.4(@babel/core@7.24.6)(styled-components@5.3.11)
13419 css-to-react-native: 3.2.0 13436 css-to-react-native: 3.2.0
13420 hoist-non-react-statics: 3.3.2 13437 hoist-non-react-statics: 3.3.2
13421 react: 18.2.0 13438 react: 18.2.0
diff --git a/recipes b/recipes
Subproject 43943652e94de5d8e1d31539dbd95e125d8769b Subproject 200233bb81b678fa6c51eaa67f9476b3f1649ec
diff --git a/src/components/services/tabs/TabItem.tsx b/src/components/services/tabs/TabItem.tsx
index 0e999506a..94c0b7e7f 100644
--- a/src/components/services/tabs/TabItem.tsx
+++ b/src/components/services/tabs/TabItem.tsx
@@ -364,10 +364,10 @@ class TabItem extends Component<IProps, IState> {
364 role="presentation" 364 role="presentation"
365 onContextMenu={() => menu.popup()} 365 onContextMenu={() => menu.popup()}
366 data-tooltip-id="tooltip-sidebar-button" 366 data-tooltip-id="tooltip-sidebar-button"
367 data-tooltip-content={`${service.name} ${acceleratorString( 367 data-tooltip-content={`${service.name} ${acceleratorString({
368 shortcutIndex, 368 index: shortcutIndex,
369 cmdOrCtrlShortcutKey(false), 369 keyCombo: cmdOrCtrlShortcutKey(false),
370 )}`} 370 })}`}
371 > 371 >
372 <img src={service.icon} className="tab-item__icon" alt="" /> 372 <img src={service.icon} className="tab-item__icon" alt="" />
373 {showServiceNameSetting && ( 373 {showServiceNameSetting && (
diff --git a/src/components/settings/settings/EditSettingsForm.tsx b/src/components/settings/settings/EditSettingsForm.tsx
index c3e8d46a0..2900aa2af 100644
--- a/src/components/settings/settings/EditSettingsForm.tsx
+++ b/src/components/settings/settings/EditSettingsForm.tsx
@@ -1,5 +1,6 @@
1import { systemPreferences } from '@electron/remote'; 1import { systemPreferences } from '@electron/remote';
2import { mdiGithub, mdiOpenInNew, mdiPowerPlug } from '@mdi/js'; 2import { mdiGithub, mdiOpenInNew, mdiPowerPlug } from '@mdi/js';
3import { ipcRenderer } from 'electron';
3import { noop } from 'lodash'; 4import { noop } from 'lodash';
4import { observer } from 'mobx-react'; 5import { observer } from 'mobx-react';
5import prettyBytes from 'pretty-bytes'; 6import prettyBytes from 'pretty-bytes';
@@ -191,6 +192,14 @@ const messages = defineMessages({
191 id: 'settings.app.subheadlineCache', 192 id: 'settings.app.subheadlineCache',
192 defaultMessage: 'Cache', 193 defaultMessage: 'Cache',
193 }, 194 },
195 subheadlineUserAgent: {
196 id: 'settings.app.subheadlineUserAgent',
197 defaultMessage: 'User Agent',
198 },
199 subheadlineDownloads: {
200 id: 'settings.app.subheadlineDownloads',
201 defaultMessage: 'Downloads',
202 },
194 cacheInfo: { 203 cacheInfo: {
195 id: 'settings.app.cacheInfo', 204 id: 'settings.app.cacheInfo',
196 defaultMessage: 'Ferdium cache is currently using {size} of disk space.', 205 defaultMessage: 'Ferdium cache is currently using {size} of disk space.',
@@ -281,6 +290,10 @@ const messages = defineMessages({
281 id: 'settings.app.buttonOpenFerdiumCertsFolder', 290 id: 'settings.app.buttonOpenFerdiumCertsFolder',
282 defaultMessage: 'Open certificates folder', 291 defaultMessage: 'Open certificates folder',
283 }, 292 },
293 buttonOpenFolderSelector: {
294 id: 'settings.app.buttonOpenFolderSelector',
295 defaultMessage: 'Open folder selector',
296 },
284}); 297});
285 298
286const Hr = (): ReactElement => ( 299const Hr = (): ReactElement => (
@@ -1034,49 +1047,87 @@ class EditSettingsForm extends Component<IProps, IState> {
1034 1047
1035 <Hr /> 1048 <Hr />
1036 1049
1037 <Input 1050 <div className="settings__settings-group">
1038 placeholder="User Agent" 1051 <H3>{intl.formatMessage(messages.subheadlineDownloads)}</H3>
1039 onChange={e => this.submit(e)} 1052
1040 {...form.$('userAgentPref').bind()} 1053 <Input
1041 /> 1054 placeholder="Default download folder"
1042 <p className="settings__help"> 1055 onChange={e => {
1043 {intl.formatMessage(globalMessages.userAgentHelp)} 1056 this.submit(e);
1044 </p> 1057 }}
1045 <p className="settings__help"> 1058 {...form.$('downloadFolderPath').bind()}
1046 {intl.formatMessage(messages.appRestartRequired)} 1059 />
1047 </p> 1060
1061 <Button
1062 buttonType="secondary"
1063 label={intl.formatMessage(
1064 messages.buttonOpenFolderSelector,
1065 )}
1066 className="settings__open-settings-cache-button"
1067 onClick={e => {
1068 ipcRenderer
1069 .invoke('download-folder-select')
1070 .then(path => {
1071 if (path) {
1072 form.$('downloadFolderPath').set(path);
1073 this.submit(e);
1074 }
1075 })
1076 .catch(console.error);
1077 }}
1078 disabled={isClearingAllCache}
1079 loaded={!isClearingAllCache}
1080 />
1081 </div>
1048 1082
1049 <Hr /> 1083 <Hr />
1050 1084
1051 <div className="settings__settings-group"> 1085 <div className="settings__settings-group">
1052 <H3>{intl.formatMessage(messages.subheadlineCache)}</H3> 1086 <Input
1053 <p> 1087 placeholder="User Agent"
1054 {intl.formatMessage(messages.cacheInfo, { 1088 onChange={e => this.submit(e)}
1055 size: cacheSize, 1089 {...form.$('userAgentPref').bind()}
1056 })} 1090 />
1091
1092 <p className="settings__help">
1093 {intl.formatMessage(globalMessages.userAgentHelp)}
1057 </p> 1094 </p>
1058 {notCleared && ( 1095 <p className="settings__help">
1059 <p>{intl.formatMessage(messages.cacheNotCleared)}</p> 1096 {intl.formatMessage(messages.appRestartRequired)}
1060 )} 1097 </p>
1098
1099 <Hr />
1100
1061 <div className="settings__settings-group"> 1101 <div className="settings__settings-group">
1062 <div className="settings__open-settings-cache-container"> 1102 <H3>{intl.formatMessage(messages.subheadlineCache)}</H3>
1063 <Button 1103 <p>
1064 buttonType="secondary" 1104 {intl.formatMessage(messages.cacheInfo, {
1065 label={intl.formatMessage(globalMessages.clearCache)} 1105 size: cacheSize,
1066 className="settings__open-settings-cache-button" 1106 })}
1067 onClick={() => { 1107 </p>
1068 onClearAllCache(); 1108 {notCleared && (
1069 this.onClearCacheClicked(); 1109 <p>{intl.formatMessage(messages.cacheNotCleared)}</p>
1070 }} 1110 )}
1071 disabled={isClearingAllCache} 1111 <div className="settings__settings-group">
1072 loaded={!isClearingAllCache} 1112 <div className="settings__open-settings-cache-container">
1073 /> 1113 <Button
1074 <Button 1114 buttonType="secondary"
1075 buttonType="secondary" 1115 label={intl.formatMessage(globalMessages.clearCache)}
1076 label="Open Process Manager" 1116 className="settings__open-settings-cache-button"
1077 className="settings__open-settings-cache-button" 1117 onClick={() => {
1078 onClick={openProcessManager} 1118 onClearAllCache();
1079 /> 1119 this.onClearCacheClicked();
1120 }}
1121 disabled={isClearingAllCache}
1122 loaded={!isClearingAllCache}
1123 />
1124 <Button
1125 buttonType="secondary"
1126 label="Open Process Manager"
1127 className="settings__open-settings-cache-button"
1128 onClick={openProcessManager}
1129 />
1130 </div>
1080 </div> 1131 </div>
1081 </div> 1132 </div>
1082 </div> 1133 </div>
diff --git a/src/config.ts b/src/config.ts
index 348b4b85d..0034b00a6 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -401,6 +401,7 @@ export const DEFAULT_APP_SETTINGS = {
401 automaticUpdates: true, 401 automaticUpdates: true,
402 universalDarkMode: true, 402 universalDarkMode: true,
403 userAgentPref: '', 403 userAgentPref: '',
404 downloadFolderPath: '',
404 adaptableDarkMode: true, 405 adaptableDarkMode: true,
405 accentColor: DEFAULT_ACCENT_COLOR, 406 accentColor: DEFAULT_ACCENT_COLOR,
406 progressbarAccentColor: DEFAULT_ACCENT_COLOR, 407 progressbarAccentColor: DEFAULT_ACCENT_COLOR,
diff --git a/src/containers/settings/EditSettingsScreen.tsx b/src/containers/settings/EditSettingsScreen.tsx
index fdd9bd8bc..31e8d6484 100644
--- a/src/containers/settings/EditSettingsScreen.tsx
+++ b/src/containers/settings/EditSettingsScreen.tsx
@@ -341,6 +341,32 @@ const messages = defineMessages({
341 id: 'settings.app.form.keepAllWorkspacesLoaded', 341 id: 'settings.app.form.keepAllWorkspacesLoaded',
342 defaultMessage: 'Keep all workspaces loaded', 342 defaultMessage: 'Keep all workspaces loaded',
343 }, 343 },
344 downloadFolderPath: {
345 id: 'settings.app.form.downloadFolderPath',
346 defaultMessage:
347 'Default download folder (leave blank to be prompted for each download)',
348 },
349 restartDialogTitle: {
350 id: 'settings.app.restart.restartDialogTitle',
351 defaultMessage: 'Ferdium - Relaunch Application',
352 },
353 restartNow: {
354 id: 'settings.app.restart.restartNow',
355 defaultMessage: 'Restart now',
356 },
357 restartLater: {
358 id: 'settings.app.restart.restartLater',
359 defaultMessage: 'Restart later',
360 },
361 restartDialogMessage: {
362 id: 'settings.app.restart.restartDialogMessage',
363 defaultMessage: 'Do you want to relaunch Ferdium?',
364 },
365 restartDialogDetail: {
366 id: 'settings.app.restart.restartDialogDetail',
367 defaultMessage:
368 'You made a change that requires a restart. This will close Ferdium and restart it.',
369 },
344}); 370});
345 371
346interface EditSettingsScreenProps extends StoresProps, WrappedComponentProps {} 372interface EditSettingsScreenProps extends StoresProps, WrappedComponentProps {}
@@ -364,6 +390,7 @@ class EditSettingsScreen extends Component<
364 } 390 }
365 391
366 onSubmit(settingsData) { 392 onSubmit(settingsData) {
393 const { intl } = this.props;
367 const { todos, workspaces } = this.props.stores; 394 const { todos, workspaces } = this.props.stores;
368 const { 395 const {
369 app, 396 app,
@@ -386,97 +413,130 @@ class EditSettingsScreen extends Component<
386 413
387 debug(`Updating settings store with data: ${settingsData}`); 414 debug(`Updating settings store with data: ${settingsData}`);
388 415
416 const { app: currentSettings } = this.props.stores.settings.all;
417
418 const newSettings = {
419 runInBackground: Boolean(settingsData.runInBackground),
420 enableSystemTray: Boolean(settingsData.enableSystemTray),
421 reloadAfterResume: Boolean(settingsData.reloadAfterResume),
422 reloadAfterResumeTime: Number(settingsData.reloadAfterResumeTime),
423 startMinimized: Boolean(settingsData.startMinimized),
424 confirmOnQuit: Boolean(settingsData.confirmOnQuit),
425 minimizeToSystemTray: Boolean(settingsData.minimizeToSystemTray),
426 closeToSystemTray: Boolean(settingsData.closeToSystemTray),
427 privateNotifications: Boolean(settingsData.privateNotifications),
428 clipboardNotifications: Boolean(settingsData.clipboardNotifications),
429 notifyTaskBarOnMessage: Boolean(settingsData.notifyTaskBarOnMessage),
430 isTwoFactorAutoCatcherEnabled: Boolean(
431 settingsData.isTwoFactorAutoCatcherEnabled,
432 ),
433 twoFactorAutoCatcherMatcher: settingsData.twoFactorAutoCatcherMatcher,
434 navigationBarBehaviour: settingsData.navigationBarBehaviour,
435 webRTCIPHandlingPolicy: settingsData.webRTCIPHandlingPolicy,
436 searchEngine: settingsData.searchEngine,
437 translatorEngine: settingsData.translatorEngine,
438 translatorLanguage: settingsData.translatorLanguage,
439 sentry: Boolean(settingsData.sentry),
440 hibernateOnStartup: Boolean(settingsData.hibernateOnStartup),
441 hibernationStrategy: Number(settingsData.hibernationStrategy),
442 wakeUpStrategy: Number(settingsData.wakeUpStrategy),
443 wakeUpHibernationStrategy: Number(settingsData.wakeUpHibernationStrategy),
444 wakeUpHibernationSplay: Boolean(settingsData.wakeUpHibernationSplay),
445 predefinedTodoServer: settingsData.predefinedTodoServer,
446 customTodoServer: settingsData.customTodoServer,
447 isLockingFeatureEnabled: Boolean(settingsData.isLockingFeatureEnabled),
448 lockedPassword: useOriginalPassword
449 ? this.props.stores.settings.all.app.lockedPassword
450 : hash(String(settingsData.lockedPassword)),
451 useTouchIdToUnlock: Boolean(settingsData.useTouchIdToUnlock),
452 inactivityLock: Number(settingsData.inactivityLock),
453 scheduledDNDEnabled: Boolean(settingsData.scheduledDNDEnabled),
454 scheduledDNDStart: settingsData.scheduledDNDStart,
455 scheduledDNDEnd: settingsData.scheduledDNDEnd,
456 enableGPUAcceleration: Boolean(settingsData.enableGPUAcceleration),
457 downloadFolderPath: String(settingsData.downloadFolderPath),
458 enableGlobalHideShortcut: Boolean(settingsData.enableGlobalHideShortcut),
459 showDisabledServices: Boolean(settingsData.showDisabledServices),
460 showServiceName: Boolean(settingsData.showServiceName),
461 darkMode: Boolean(settingsData.darkMode),
462 adaptableDarkMode: Boolean(settingsData.adaptableDarkMode),
463 universalDarkMode: Boolean(settingsData.universalDarkMode),
464 splitMode: Boolean(settingsData.splitMode),
465 splitColumns: Number(settingsData.splitColumns),
466 serviceRibbonWidth: Number(settingsData.serviceRibbonWidth),
467 sidebarServicesLocation: Number(settingsData.sidebarServicesLocation),
468 iconSize: Number(settingsData.iconSize),
469 enableLongPressServiceHint: Boolean(
470 settingsData.enableLongPressServiceHint,
471 ),
472 useHorizontalStyle: Boolean(settingsData.useHorizontalStyle),
473 hideCollapseButton: Boolean(settingsData.hideCollapseButton),
474 hideRecipesButton: Boolean(settingsData.hideRecipesButton),
475 hideSplitModeButton: Boolean(settingsData.hideSplitModeButton),
476 useGrayscaleServices: Boolean(settingsData.useGrayscaleServices),
477 grayscaleServicesDim: Number(settingsData.grayscaleServicesDim),
478 hideWorkspacesButton: Boolean(settingsData.hideWorkspacesButton),
479 hideNotificationsButton: Boolean(settingsData.hideNotificationsButton),
480 hideSettingsButton: Boolean(settingsData.hideSettingsButton),
481 hideDownloadButton: Boolean(settingsData.hideDownloadButton),
482 alwaysShowWorkspaces: Boolean(settingsData.alwaysShowWorkspaces),
483 hideAllServicesWorkspace: Boolean(settingsData.hideAllServicesWorkspace),
484 accentColor: settingsData.accentColor,
485 progressbarAccentColor: settingsData.progressbarAccentColor,
486 showMessageBadgeWhenMuted: Boolean(
487 settingsData.showMessageBadgeWhenMuted,
488 ),
489 showDragArea: Boolean(settingsData.showDragArea),
490 enableSpellchecking: Boolean(settingsData.enableSpellchecking),
491 enableTranslator: Boolean(settingsData.enableTranslator),
492 useSelfSignedCertificates: Boolean(
493 settingsData.useSelfSignedCertificates,
494 ),
495 spellcheckerLanguage: settingsData.spellcheckerLanguage,
496 userAgentPref: settingsData.userAgentPref,
497 beta: Boolean(settingsData.beta), // we need this info in the main process as well
498 automaticUpdates: Boolean(settingsData.automaticUpdates), // we need this info in the main process as well
499 locale: settingsData.locale, // we need this info in the main process as well
500 };
501
502 const requiredRestartKeys = [
503 'webRTCIPHandlingPolicy',
504 'sentry',
505 'searchEngine',
506 'enableSpellchecking',
507 'spellcheckerLanguage',
508 'enableGlobalHideShortcut',
509 // 'userAgentPref', // TODO: this is an input field, so it changes on every key stroke
510 ];
511
512 // Check if any of the keys that require a restart have changed
513 const requiresRestart = requiredRestartKeys.some(
514 key => newSettings[key] !== currentSettings[key],
515 );
516
517 if (requiresRestart) {
518 debug('Settings require restart');
519
520 const options: Electron.MessageBoxOptions = {
521 type: 'warning',
522 buttons: [
523 intl.formatMessage(messages.restartNow),
524 intl.formatMessage(messages.restartLater),
525 ],
526 defaultId: 0,
527 cancelId: 1,
528 title: intl.formatMessage(messages.restartDialogTitle),
529 message: intl.formatMessage(messages.restartDialogMessage),
530 detail: intl.formatMessage(messages.restartDialogDetail),
531 };
532
533 ipcRenderer.send('relaunch-app', options);
534 }
535
389 settings.update({ 536 settings.update({
390 type: 'app', 537 type: 'app',
391 // TODO: The conversions might not be necessary once we convert to typescript 538 // TODO: The conversions might not be necessary once we convert to typescript
392 data: { 539 data: newSettings,
393 runInBackground: Boolean(settingsData.runInBackground),
394 enableSystemTray: Boolean(settingsData.enableSystemTray),
395 reloadAfterResume: Boolean(settingsData.reloadAfterResume),
396 reloadAfterResumeTime: Number(settingsData.reloadAfterResumeTime),
397 startMinimized: Boolean(settingsData.startMinimized),
398 confirmOnQuit: Boolean(settingsData.confirmOnQuit),
399 minimizeToSystemTray: Boolean(settingsData.minimizeToSystemTray),
400 closeToSystemTray: Boolean(settingsData.closeToSystemTray),
401 privateNotifications: Boolean(settingsData.privateNotifications),
402 clipboardNotifications: Boolean(settingsData.clipboardNotifications),
403 notifyTaskBarOnMessage: Boolean(settingsData.notifyTaskBarOnMessage),
404 isTwoFactorAutoCatcherEnabled: Boolean(
405 settingsData.isTwoFactorAutoCatcherEnabled,
406 ),
407 twoFactorAutoCatcherMatcher: settingsData.twoFactorAutoCatcherMatcher,
408 navigationBarBehaviour: settingsData.navigationBarBehaviour,
409 webRTCIPHandlingPolicy: settingsData.webRTCIPHandlingPolicy,
410 searchEngine: settingsData.searchEngine,
411 translatorEngine: settingsData.translatorEngine,
412 translatorLanguage: settingsData.translatorLanguage,
413 sentry: Boolean(settingsData.sentry),
414 hibernateOnStartup: Boolean(settingsData.hibernateOnStartup),
415 hibernationStrategy: Number(settingsData.hibernationStrategy),
416 wakeUpStrategy: Number(settingsData.wakeUpStrategy),
417 wakeUpHibernationStrategy: Number(
418 settingsData.wakeUpHibernationStrategy,
419 ),
420 wakeUpHibernationSplay: Boolean(settingsData.wakeUpHibernationSplay),
421 predefinedTodoServer: settingsData.predefinedTodoServer,
422 customTodoServer: settingsData.customTodoServer,
423 isLockingFeatureEnabled: Boolean(settingsData.isLockingFeatureEnabled),
424 lockedPassword: useOriginalPassword
425 ? this.props.stores.settings.all.app.lockedPassword
426 : hash(String(settingsData.lockedPassword)),
427 useTouchIdToUnlock: Boolean(settingsData.useTouchIdToUnlock),
428 inactivityLock: Number(settingsData.inactivityLock),
429 scheduledDNDEnabled: Boolean(settingsData.scheduledDNDEnabled),
430 scheduledDNDStart: settingsData.scheduledDNDStart,
431 scheduledDNDEnd: settingsData.scheduledDNDEnd,
432 enableGPUAcceleration: Boolean(settingsData.enableGPUAcceleration),
433 enableGlobalHideShortcut: Boolean(
434 settingsData.enableGlobalHideShortcut,
435 ),
436 showDisabledServices: Boolean(settingsData.showDisabledServices),
437 showServiceName: Boolean(settingsData.showServiceName),
438 darkMode: Boolean(settingsData.darkMode),
439 adaptableDarkMode: Boolean(settingsData.adaptableDarkMode),
440 universalDarkMode: Boolean(settingsData.universalDarkMode),
441 splitMode: Boolean(settingsData.splitMode),
442 splitColumns: Number(settingsData.splitColumns),
443 serviceRibbonWidth: Number(settingsData.serviceRibbonWidth),
444 sidebarServicesLocation: Number(settingsData.sidebarServicesLocation),
445 iconSize: Number(settingsData.iconSize),
446 enableLongPressServiceHint: Boolean(
447 settingsData.enableLongPressServiceHint,
448 ),
449 useHorizontalStyle: Boolean(settingsData.useHorizontalStyle),
450 hideCollapseButton: Boolean(settingsData.hideCollapseButton),
451 hideRecipesButton: Boolean(settingsData.hideRecipesButton),
452 hideSplitModeButton: Boolean(settingsData.hideSplitModeButton),
453 useGrayscaleServices: Boolean(settingsData.useGrayscaleServices),
454 grayscaleServicesDim: Number(settingsData.grayscaleServicesDim),
455 hideWorkspacesButton: Boolean(settingsData.hideWorkspacesButton),
456 hideNotificationsButton: Boolean(settingsData.hideNotificationsButton),
457 hideSettingsButton: Boolean(settingsData.hideSettingsButton),
458 hideDownloadButton: Boolean(settingsData.hideDownloadButton),
459 alwaysShowWorkspaces: Boolean(settingsData.alwaysShowWorkspaces),
460 hideAllServicesWorkspace: Boolean(
461 settingsData.hideAllServicesWorkspace,
462 ),
463 accentColor: settingsData.accentColor,
464 progressbarAccentColor: settingsData.progressbarAccentColor,
465 showMessageBadgeWhenMuted: Boolean(
466 settingsData.showMessageBadgeWhenMuted,
467 ),
468 showDragArea: Boolean(settingsData.showDragArea),
469 enableSpellchecking: Boolean(settingsData.enableSpellchecking),
470 enableTranslator: Boolean(settingsData.enableTranslator),
471 useSelfSignedCertificates: Boolean(
472 settingsData.useSelfSignedCertificates,
473 ),
474 spellcheckerLanguage: settingsData.spellcheckerLanguage,
475 userAgentPref: settingsData.userAgentPref,
476 beta: Boolean(settingsData.beta), // we need this info in the main process as well
477 automaticUpdates: Boolean(settingsData.automaticUpdates), // we need this info in the main process as well
478 locale: settingsData.locale, // we need this info in the main process as well
479 },
480 }); 540 });
481 541
482 user.update({ 542 user.update({
@@ -982,6 +1042,14 @@ class EditSettingsScreen extends Component<
982 default: DEFAULT_APP_SETTINGS.userAgentPref, 1042 default: DEFAULT_APP_SETTINGS.userAgentPref,
983 placeholder: defaultUserAgent(), 1043 placeholder: defaultUserAgent(),
984 }, 1044 },
1045 downloadFolderPath: {
1046 label: intl.formatMessage(messages.downloadFolderPath),
1047 value: ifUndefined<string>(
1048 settings.all.app.downloadFolderPath,
1049 DEFAULT_APP_SETTINGS.downloadFolderPath,
1050 ),
1051 default: DEFAULT_APP_SETTINGS.userAgentPref,
1052 },
985 darkMode: { 1053 darkMode: {
986 label: intl.formatMessage(messages.darkMode), 1054 label: intl.formatMessage(messages.darkMode),
987 value: ifUndefined<boolean>( 1055 value: ifUndefined<boolean>(
diff --git a/src/electron/ipc-api/dnd.ts b/src/electron/ipc-api/dnd.ts
index 30ace7c84..280521a9f 100644
--- a/src/electron/ipc-api/dnd.ts
+++ b/src/electron/ipc-api/dnd.ts
@@ -8,7 +8,6 @@ export default async () => {
8 if (!isMac) { 8 if (!isMac) {
9 return false; 9 return false;
10 } 10 }
11
12 const { getDoNotDisturb } = await import('macos-notification-state'); 11 const { getDoNotDisturb } = await import('macos-notification-state');
13 12
14 if (!getDoNotDisturb) { 13 if (!getDoNotDisturb) {
diff --git a/src/electron/ipc-api/download.ts b/src/electron/ipc-api/download.ts
index d749cb889..a306ba68d 100644
--- a/src/electron/ipc-api/download.ts
+++ b/src/electron/ipc-api/download.ts
@@ -52,4 +52,14 @@ export default (params: { mainWindow: BrowserWindow }) => {
52 } 52 }
53 }, 53 },
54 ); 54 );
55
56 ipcMain.handle('download-folder-select', async () => {
57 const result = await dialog.showOpenDialog(params.mainWindow, {
58 properties: ['openDirectory'],
59 });
60
61 if (result.canceled) return null;
62
63 return result.filePaths[0];
64 });
55}; 65};
diff --git a/src/features/workspaces/components/WorkspaceDrawerItem.tsx b/src/features/workspaces/components/WorkspaceDrawerItem.tsx
index 01a18ffb0..23851edf6 100644
--- a/src/features/workspaces/components/WorkspaceDrawerItem.tsx
+++ b/src/features/workspaces/components/WorkspaceDrawerItem.tsx
@@ -125,10 +125,10 @@ class WorkspaceDrawerItem extends Component<IProps> {
125 }} 125 }}
126 onKeyDown={noop} 126 onKeyDown={noop}
127 data-tooltip-id="tooltip-workspaces-drawer" 127 data-tooltip-id="tooltip-workspaces-drawer"
128 data-tooltip-content={acceleratorString( 128 data-tooltip-content={acceleratorString({
129 shortcutIndex, 129 index: shortcutIndex,
130 `${cmdOrCtrlShortcutKey(false)}+${altKey(false)}`, 130 keyCombo: `${cmdOrCtrlShortcutKey(false)}+${altKey(false)}`,
131 )} 131 })}
132 > 132 >
133 <span 133 <span
134 className={classnames([ 134 className={classnames([
diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json
index d2d317586..50ab8d4ce 100644
--- a/src/i18n/locales/de.json
+++ b/src/i18n/locales/de.json
@@ -16,6 +16,7 @@
16 "feature.publishDebugInfo.info": "Die Veröffentlichung deiner Debug-Informationen hilft uns, Probleme und Fehler in Ferdium zu finden. Indem du deine Debug-Informationen veröffentlichst, akzeptierst du die Datenschutzbestimmungen und Nutzungsbedingungen vom Ferdium Debugger", 16 "feature.publishDebugInfo.info": "Die Veröffentlichung deiner Debug-Informationen hilft uns, Probleme und Fehler in Ferdium zu finden. Indem du deine Debug-Informationen veröffentlichst, akzeptierst du die Datenschutzbestimmungen und Nutzungsbedingungen vom Ferdium Debugger",
17 "feature.publishDebugInfo.privacy": "Datenschutzrichtlinien", 17 "feature.publishDebugInfo.privacy": "Datenschutzrichtlinien",
18 "feature.publishDebugInfo.publish": "Akzeptieren und Veröffentlichen", 18 "feature.publishDebugInfo.publish": "Akzeptieren und Veröffentlichen",
19 "feature.publishDebugInfo.published": "Ihr Debug-Protokoll wurde veröffentlicht und ist nun verfügbar unter",
19 "feature.publishDebugInfo.terms": "Nutzungsbedingungen", 20 "feature.publishDebugInfo.terms": "Nutzungsbedingungen",
20 "feature.publishDebugInfo.title": "Debug-Informationen veröffentlichen", 21 "feature.publishDebugInfo.title": "Debug-Informationen veröffentlichen",
21 "feature.quickSwitch.info": "Wähle einen Dienst mit TAB, ↑ und ↓. Um einen Dienst zu öffnen, drücke ENTER.", 22 "feature.quickSwitch.info": "Wähle einen Dienst mit TAB, ↑ und ↓. Um einen Dienst zu öffnen, drücke ENTER.",
@@ -24,6 +25,7 @@
24 "global.api.unhealthy": "Verbindung zu {serverNameParse} Online-Diensten kann nicht hergestellt werden", 25 "global.api.unhealthy": "Verbindung zu {serverNameParse} Online-Diensten kann nicht hergestellt werden",
25 "global.cancel": "Abbrechen", 26 "global.cancel": "Abbrechen",
26 "global.clearCache": "Cache leeren", 27 "global.clearCache": "Cache leeren",
28 "global.downloads": "Downloads",
27 "global.edit": "Bearbeiten", 29 "global.edit": "Bearbeiten",
28 "global.no": "Nein", 30 "global.no": "Nein",
29 "global.notConnectedToTheInternet": "Du bist nicht mit dem Internet verbunden.", 31 "global.notConnectedToTheInternet": "Du bist nicht mit dem Internet verbunden.",
@@ -122,6 +124,7 @@
122 "menu.view.back": "Zurück", 124 "menu.view.back": "Zurück",
123 "menu.view.forward": "Vorwärts", 125 "menu.view.forward": "Vorwärts",
124 "menu.view.lockFerdium": "Ferdium sperren", 126 "menu.view.lockFerdium": "Ferdium sperren",
127 "menu.view.openProcessManager": "Prozessverwaltung öffnen",
125 "menu.view.openQuickSwitch": "Quick Switch öffnen", 128 "menu.view.openQuickSwitch": "Quick Switch öffnen",
126 "menu.view.reloadFerdium": "Ferdium neu laden", 129 "menu.view.reloadFerdium": "Ferdium neu laden",
127 "menu.view.reloadService": "Dienst neu laden", 130 "menu.view.reloadService": "Dienst neu laden",
@@ -161,6 +164,8 @@
161 "service.errorHandler.headline": "Oh nein!", 164 "service.errorHandler.headline": "Oh nein!",
162 "service.errorHandler.message": "Fehler", 165 "service.errorHandler.message": "Fehler",
163 "service.errorHandler.text": "{name} konnte nicht geladen werden.", 166 "service.errorHandler.text": "{name} konnte nicht geladen werden.",
167 "service.screenshare.cancel": "Abbrechen",
168 "service.screenshare.loading": "Bildschirme und Fenster werden geladen",
164 "service.webviewLoader.loading": "{service} wird geladen", 169 "service.webviewLoader.loading": "{service} wird geladen",
165 "services.getStarted": "Los geht's", 170 "services.getStarted": "Los geht's",
166 "services.login": "Bitte anmelden, um Ferdium nutzen zu können.", 171 "services.login": "Bitte anmelden, um Ferdium nutzen zu können.",
@@ -189,6 +194,7 @@
189 "settings.app.buttonOpenFerdiumCertsFolder": "Ordner „Zertifikate” öffnen", 194 "settings.app.buttonOpenFerdiumCertsFolder": "Ordner „Zertifikate” öffnen",
190 "settings.app.buttonOpenFerdiumProfileFolder": "Profilordner anzeigen", 195 "settings.app.buttonOpenFerdiumProfileFolder": "Profilordner anzeigen",
191 "settings.app.buttonOpenFerdiumServiceRecipesFolder": "Rezeptordner öffnen", 196 "settings.app.buttonOpenFerdiumServiceRecipesFolder": "Rezeptordner öffnen",
197 "settings.app.buttonOpenFolderSelector": "Ordnerauswahl öffnen",
192 "settings.app.buttonOpenImportExport": "Importieren/Exportieren", 198 "settings.app.buttonOpenImportExport": "Importieren/Exportieren",
193 "settings.app.buttonSearchForUpdate": "Nach Aktualisierungen suchen", 199 "settings.app.buttonSearchForUpdate": "Nach Aktualisierungen suchen",
194 "settings.app.buttonShowChangelog": "Änderungen zeigen", 200 "settings.app.buttonShowChangelog": "Änderungen zeigen",
@@ -208,6 +214,7 @@
208 "settings.app.form.confirmOnQuit": "Beim Beenden von Ferdium bestätigen", 214 "settings.app.form.confirmOnQuit": "Beim Beenden von Ferdium bestätigen",
209 "settings.app.form.customTodoServer": "Eigener Todo Server", 215 "settings.app.form.customTodoServer": "Eigener Todo Server",
210 "settings.app.form.darkMode": "Dark Mode aktivieren", 216 "settings.app.form.darkMode": "Dark Mode aktivieren",
217 "settings.app.form.downloadFolderPath": "Standard-Download-Ordner (leer lassen, um bei jedem Download gefragt zu werden)",
211 "settings.app.form.enableGPUAcceleration": "Hardwarebeschleunigung aktivieren", 218 "settings.app.form.enableGPUAcceleration": "Hardwarebeschleunigung aktivieren",
212 "settings.app.form.enableGlobalHideShortcut": "Allgemeine Verknüpfung zum Ausblenden von Ferdium aktivieren", 219 "settings.app.form.enableGlobalHideShortcut": "Allgemeine Verknüpfung zum Ausblenden von Ferdium aktivieren",
213 "settings.app.form.enableLock": "Passwort-Sperre aktivieren", 220 "settings.app.form.enableLock": "Passwort-Sperre aktivieren",
@@ -220,7 +227,9 @@
220 "settings.app.form.grayscaleServicesDim": "Graustufen Dimmwert", 227 "settings.app.form.grayscaleServicesDim": "Graustufen Dimmwert",
221 "settings.app.form.hibernateOnStartup": "Dienste beim Start in den Ruhezustand versetzen", 228 "settings.app.form.hibernateOnStartup": "Dienste beim Start in den Ruhezustand versetzen",
222 "settings.app.form.hibernationStrategy": "\"Service Hibernation\"-Strategie", 229 "settings.app.form.hibernationStrategy": "\"Service Hibernation\"-Strategie",
230 "settings.app.form.hideAllServicesWorkspace": "Arbeitsbereich „Alle Dienste“ ausblenden",
223 "settings.app.form.hideCollapseButton": "Schaltfläche „Ausklappen” ausblenden", 231 "settings.app.form.hideCollapseButton": "Schaltfläche „Ausklappen” ausblenden",
232 "settings.app.form.hideDownloadButton": "Schaltfläche „Downloads“ ausblenden",
224 "settings.app.form.hideNotificationsButton": "Schaltfläche „Benachrichtigungen und Töne” ausblenden", 233 "settings.app.form.hideNotificationsButton": "Schaltfläche „Benachrichtigungen und Töne” ausblenden",
225 "settings.app.form.hideRecipesButton": "Schaltfläche „Neuen Dienst hinzufügen” ausblenden", 234 "settings.app.form.hideRecipesButton": "Schaltfläche „Neuen Dienst hinzufügen” ausblenden",
226 "settings.app.form.hideSettingsButton": "Schaltfläche „Einstellungen” ausblenden", 235 "settings.app.form.hideSettingsButton": "Schaltfläche „Einstellungen” ausblenden",
@@ -278,10 +287,16 @@
278 "settings.app.hibernateInfo": "Standardmäßig öffnet Ferdium alle deine Dienste im Hintergrund, sodass diese bereit sind, wenn du sie verwenden möchtest. \"Service Hibernation\" wird deine Dienste nach einer bestimmten Zeitspanne schließen. Dies ist nützlich, um RAM zu sparen oder Dienste davon abzuhalten, deinen Computer zu verlangsamen.", 287 "settings.app.hibernateInfo": "Standardmäßig öffnet Ferdium alle deine Dienste im Hintergrund, sodass diese bereit sind, wenn du sie verwenden möchtest. \"Service Hibernation\" wird deine Dienste nach einer bestimmten Zeitspanne schließen. Dies ist nützlich, um RAM zu sparen oder Dienste davon abzuhalten, deinen Computer zu verlangsamen.",
279 "settings.app.inactivityLockInfo": "Minuten der Inaktivität, nach denen Ferdium automatisch sperren soll. Verwenden Sie 0, um dies zu deaktivieren", 288 "settings.app.inactivityLockInfo": "Minuten der Inaktivität, nach denen Ferdium automatisch sperren soll. Verwenden Sie 0, um dies zu deaktivieren",
280 "settings.app.infoOpenCertificatesFolder": "Um ein Zertifikat zu installieren, klicken Sie auf die Schaltfläche unten, um den Zertifikatsordner zu öffnen, und kopieren Sie es in den Ordner. Danach können Sie den Dienst aktualisieren (STRG/CMD + R). Zum Entfernen/Deinstallieren löschen Sie einfach die Zertifikatsdatei und starten Ferdium neu.", 289 "settings.app.infoOpenCertificatesFolder": "Um ein Zertifikat zu installieren, klicken Sie auf die Schaltfläche unten, um den Zertifikatsordner zu öffnen, und kopieren Sie es in den Ordner. Danach können Sie den Dienst aktualisieren (STRG/CMD + R). Zum Entfernen/Deinstallieren löschen Sie einfach die Zertifikatsdatei und starten Ferdium neu.",
290 "settings.app.lockInfo": "Die Passwortsperre ermöglicht es Ihnen, Ihre Nachrichten zu schützen.\nWenn Sie die Passwortsperre verwenden, werden Sie bei jedem Start von Ferdium aufgefordert, Ihr Passwort einzugeben, oder Sie sperren Ferdium selbst, indem Sie das Schlosssymbol in der unteren linken Ecke oder das Tastaturkürzel {lockShortcut} verwenden.",
281 "settings.app.lockedPassword": "Passwort", 291 "settings.app.lockedPassword": "Passwort",
282 "settings.app.lockedPasswordInfo": "Bitte stelle sicher, dass du ein Passwort setzt, an welches du dich erinnern kannst.\nSolltest du dieses Passwort vergessen, musst du Ferdium neu installieren.", 292 "settings.app.lockedPasswordInfo": "Bitte stelle sicher, dass du ein Passwort setzt, an welches du dich erinnern kannst.\nSolltest du dieses Passwort vergessen, musst du Ferdium neu installieren.",
283 "settings.app.overallTheme": "Allgemeines Farbschema", 293 "settings.app.overallTheme": "Allgemeines Farbschema",
284 "settings.app.progressbarTheme": "Farbschema der Fortschrittsanzeige", 294 "settings.app.progressbarTheme": "Farbschema der Fortschrittsanzeige",
295 "settings.app.restart.restartDialogDetail": "Sie haben eine Änderung vorgenommen, die einen Neustart erfordert. Dadurch wird Ferdium geschlossen und neu gestartet.",
296 "settings.app.restart.restartDialogMessage": "Möchten Sie Ferdium erneut starten?",
297 "settings.app.restart.restartDialogTitle": "Ferdium - Anwendung neu starten",
298 "settings.app.restart.restartLater": "Später neu starten",
299 "settings.app.restart.restartNow": "Jetzt neu starten",
285 "settings.app.restartRequired": "Änderungen werden erst nach einem Neustart wirksam", 300 "settings.app.restartRequired": "Änderungen werden erst nach einem Neustart wirksam",
286 "settings.app.scheduledDNDInfo": "Die geplante \"Nicht-stören\"-Funktion erlaubt es dir eine Zeitspanne festzulegen, in der du keine Benachrichtigungen von Ferdium erhalten möchtest.", 301 "settings.app.scheduledDNDInfo": "Die geplante \"Nicht-stören\"-Funktion erlaubt es dir eine Zeitspanne festzulegen, in der du keine Benachrichtigungen von Ferdium erhalten möchtest.",
287 "settings.app.scheduledDNDTimeInfo": "Zeiten im 24-Stunden-Format (z.B. 18:00). Endzeit kann vor Beginn der Startzeit sein (z.B. 17:00 Uhr, Ende 09:00), um die Funktion über Nacht zu aktivieren.", 302 "settings.app.scheduledDNDTimeInfo": "Zeiten im 24-Stunden-Format (z.B. 18:00). Endzeit kann vor Beginn der Startzeit sein (z.B. 17:00 Uhr, Ende 09:00), um die Funktion über Nacht zu aktivieren.",
@@ -298,8 +313,11 @@
298 "settings.app.sentryInfo": "Das Senden von Telemetriedaten ermöglicht es uns, Fehler in Ferdium zu finden - es werden keine persönlichen Informationen wie Ihre Nachrichtendaten gesendet!", 313 "settings.app.sentryInfo": "Das Senden von Telemetriedaten ermöglicht es uns, Fehler in Ferdium zu finden - es werden keine persönlichen Informationen wie Ihre Nachrichtendaten gesendet!",
299 "settings.app.serverHelp": "Verbunden mit dem Server unter {serverURL}", 314 "settings.app.serverHelp": "Verbunden mit dem Server unter {serverURL}",
300 "settings.app.servicesUpdateStatusUpToDate": "Ihre Dienste sind auf dem neuesten Stand", 315 "settings.app.servicesUpdateStatusUpToDate": "Ihre Dienste sind auf dem neuesten Stand",
316 "settings.app.spellCheckerLanguageInfo": "Ferdium verwendet die in Ihrem Mac eingebaute Rechtschreibprüfung, um auf Tippfehler zu prüfen. Wenn Sie die Sprachen, die die Rechtschreibprüfung prüft, ändern möchten, können Sie dies in den Systemeinstellungen Ihres Macs tun.",
301 "settings.app.subheadlineCache": "Cache", 317 "settings.app.subheadlineCache": "Cache",
318 "settings.app.subheadlineDownloads": "Downloads",
302 "settings.app.subheadlineFerdiumProfile": "Ferdium Profil", 319 "settings.app.subheadlineFerdiumProfile": "Ferdium Profil",
320 "settings.app.subheadlineUserAgent": "Browserkennung",
303 "settings.app.todoServerInfo": "Dieser Server wird für die \"Ferdium Todo\"-Funktion verwendet.", 321 "settings.app.todoServerInfo": "Dieser Server wird für die \"Ferdium Todo\"-Funktion verwendet.",
304 "settings.app.translationHelp": "Hilf uns, Ferdium in Deine Sprache zu übersetzen.", 322 "settings.app.translationHelp": "Hilf uns, Ferdium in Deine Sprache zu übersetzen.",
305 "settings.app.universalDarkModeInfo": "Universeller Dark Mode versucht dynamisch Dienste abzudunkeln, die vom normalen Dark Mode noch nicht unterstützt werden.", 323 "settings.app.universalDarkModeInfo": "Universeller Dark Mode versucht dynamisch Dienste abzudunkeln, die vom normalen Dark Mode noch nicht unterstützt werden.",
@@ -329,6 +347,8 @@
329 "settings.recipes.missingService": "Fehlt ein Dienst?", 347 "settings.recipes.missingService": "Fehlt ein Dienst?",
330 "settings.recipes.nothingFound": "Leider hat kein Dienst zu deinem Suchbegriff gepasst - aber du kannst ihn wahrscheinlich trotzdem über die Option \"Eigene Website\" hinzufügen. Bitte beachte, dass die Website möglicherweise weitere Dienste anzeigt, die seit deiner aktuellen Version zu Ferdium hinzugefügt wurden. Um diese neuen Dienste zu erhalten, erwäge bitte ein Upgrade auf eine neuere Version von Ferdium.", 348 "settings.recipes.nothingFound": "Leider hat kein Dienst zu deinem Suchbegriff gepasst - aber du kannst ihn wahrscheinlich trotzdem über die Option \"Eigene Website\" hinzufügen. Bitte beachte, dass die Website möglicherweise weitere Dienste anzeigt, die seit deiner aktuellen Version zu Ferdium hinzugefügt wurden. Um diese neuen Dienste zu erhalten, erwäge bitte ein Upgrade auf eine neuere Version von Ferdium.",
331 "settings.recipes.servicesSuccessfulAddedInfo": "Dienst erfolgreich hinzugefügt", 349 "settings.recipes.servicesSuccessfulAddedInfo": "Dienst erfolgreich hinzugefügt",
350 "settings.releasenotes.connectionError": "Bei der Verbindung zu Github ist ein Fehler aufgetreten. Bitte versuchen Sie es später noch einmal.",
351 "settings.releasenotes.connectionErrorPageMissing": "Bei der Verbindung zu Github ist ein Fehler aufgetreten, die gesuchte Seite ist nicht vorhanden.",
332 "settings.releasenotes.headline": "Neuigkeiten", 352 "settings.releasenotes.headline": "Neuigkeiten",
333 "settings.searchService": "Dienst suchen", 353 "settings.searchService": "Dienst suchen",
334 "settings.service.error.goBack": "Zurück zu den Diensten", 354 "settings.service.error.goBack": "Zurück zu den Diensten",
@@ -385,6 +405,7 @@
385 "settings.service.form.tabOnPremise": "Selbst gehostet ⭐️", 405 "settings.service.form.tabOnPremise": "Selbst gehostet ⭐️",
386 "settings.service.form.team": "Team", 406 "settings.service.form.team": "Team",
387 "settings.service.form.trapLinkClicks": "URLs in Ferdium öffnen", 407 "settings.service.form.trapLinkClicks": "URLs in Ferdium öffnen",
408 "settings.service.form.useFavicon": "Favicon des Dienstes anstelle des Standard- oder benutzerdefinierten Symbols verwenden",
388 "settings.service.form.useHostedService": "Hosted {name} verwenden.", 409 "settings.service.form.useHostedService": "Hosted {name} verwenden.",
389 "settings.service.form.yourServices": "Deine Dienste", 410 "settings.service.form.yourServices": "Deine Dienste",
390 "settings.service.reloadRequired": "Änderungen werden erst nach einem Neuladen des Dienstes wirksam", 411 "settings.service.reloadRequired": "Änderungen werden erst nach einem Neuladen des Dienstes wirksam",
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 3947b2e5f..cb0f4369c 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -194,6 +194,7 @@
194 "settings.app.buttonOpenFerdiumCertsFolder": "Open certificates folder", 194 "settings.app.buttonOpenFerdiumCertsFolder": "Open certificates folder",
195 "settings.app.buttonOpenFerdiumProfileFolder": "Open Profile folder", 195 "settings.app.buttonOpenFerdiumProfileFolder": "Open Profile folder",
196 "settings.app.buttonOpenFerdiumServiceRecipesFolder": "Open Service Recipes folder", 196 "settings.app.buttonOpenFerdiumServiceRecipesFolder": "Open Service Recipes folder",
197 "settings.app.buttonOpenFolderSelector": "Open folder selector",
197 "settings.app.buttonOpenImportExport": "Import / Export", 198 "settings.app.buttonOpenImportExport": "Import / Export",
198 "settings.app.buttonSearchForUpdate": "Check for updates", 199 "settings.app.buttonSearchForUpdate": "Check for updates",
199 "settings.app.buttonShowChangelog": "Show changelog", 200 "settings.app.buttonShowChangelog": "Show changelog",
@@ -213,6 +214,7 @@
213 "settings.app.form.confirmOnQuit": "Confirm when quitting Ferdium", 214 "settings.app.form.confirmOnQuit": "Confirm when quitting Ferdium",
214 "settings.app.form.customTodoServer": "Custom Todo Server", 215 "settings.app.form.customTodoServer": "Custom Todo Server",
215 "settings.app.form.darkMode": "Enable Dark Mode", 216 "settings.app.form.darkMode": "Enable Dark Mode",
217 "settings.app.form.downloadFolderPath": "Default download folder (leave blank to be prompted for each download)",
216 "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", 218 "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration",
217 "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdium", 219 "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdium",
218 "settings.app.form.enableLock": "Enable Password Lock", 220 "settings.app.form.enableLock": "Enable Password Lock",
@@ -290,6 +292,11 @@
290 "settings.app.lockedPasswordInfo": "Please make sure to set a password you'll remember.\nIf you loose this password, you will have to reinstall Ferdium.", 292 "settings.app.lockedPasswordInfo": "Please make sure to set a password you'll remember.\nIf you loose this password, you will have to reinstall Ferdium.",
291 "settings.app.overallTheme": "Overall Theme", 293 "settings.app.overallTheme": "Overall Theme",
292 "settings.app.progressbarTheme": "Progressbar Theme", 294 "settings.app.progressbarTheme": "Progressbar Theme",
295 "settings.app.restart.restartDialogDetail": "You made a change that requires a restart. This will close Ferdium and restart it.",
296 "settings.app.restart.restartDialogMessage": "Do you want to relaunch Ferdium?",
297 "settings.app.restart.restartDialogTitle": "Ferdium - Relaunch Application",
298 "settings.app.restart.restartLater": "Restart later",
299 "settings.app.restart.restartNow": "Restart now",
293 "settings.app.restartRequired": "Changes require restart", 300 "settings.app.restartRequired": "Changes require restart",
294 "settings.app.scheduledDNDInfo": "Scheduled Do-not-Disturb allows you to define a period of time in which you do not want to get Notifications from Ferdium.", 301 "settings.app.scheduledDNDInfo": "Scheduled Do-not-Disturb allows you to define a period of time in which you do not want to get Notifications from Ferdium.",
295 "settings.app.scheduledDNDTimeInfo": "Times in 24-Hour-Format. End time can be before start time (e.g. start 17:00, end 09:00) to enable Do-not-Disturb overnight.", 302 "settings.app.scheduledDNDTimeInfo": "Times in 24-Hour-Format. End time can be before start time (e.g. start 17:00, end 09:00) to enable Do-not-Disturb overnight.",
@@ -308,7 +315,9 @@
308 "settings.app.servicesUpdateStatusUpToDate": "Your services are up-to-date", 315 "settings.app.servicesUpdateStatusUpToDate": "Your services are up-to-date",
309 "settings.app.spellCheckerLanguageInfo": "Ferdium uses your Mac's built-in spellchecker to check for typos. If you want to change the languages the spellchecker checks for, you can do so in your Mac's System Preferences.", 316 "settings.app.spellCheckerLanguageInfo": "Ferdium uses your Mac's built-in spellchecker to check for typos. If you want to change the languages the spellchecker checks for, you can do so in your Mac's System Preferences.",
310 "settings.app.subheadlineCache": "Cache", 317 "settings.app.subheadlineCache": "Cache",
318 "settings.app.subheadlineDownloads": "Downloads",
311 "settings.app.subheadlineFerdiumProfile": "Ferdium Profile", 319 "settings.app.subheadlineFerdiumProfile": "Ferdium Profile",
320 "settings.app.subheadlineUserAgent": "User Agent",
312 "settings.app.todoServerInfo": "This server will be used for the \"Ferdium Todo\" feature.", 321 "settings.app.todoServerInfo": "This server will be used for the \"Ferdium Todo\" feature.",
313 "settings.app.translationHelp": "Help us to translate Ferdium into your language.", 322 "settings.app.translationHelp": "Help us to translate Ferdium into your language.",
314 "settings.app.universalDarkModeInfo": "Universal Dark Mode tries to dynamically generate dark mode styles for services that are otherwise not currently supported.", 323 "settings.app.universalDarkModeInfo": "Universal Dark Mode tries to dynamically generate dark mode styles for services that are otherwise not currently supported.",
diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json
index e6ee0e35f..24ca894ee 100644
--- a/src/i18n/locales/ru.json
+++ b/src/i18n/locales/ru.json
@@ -159,6 +159,7 @@
159 "service.errorHandler.headline": "О, нет!", 159 "service.errorHandler.headline": "О, нет!",
160 "service.errorHandler.message": "Ошибка", 160 "service.errorHandler.message": "Ошибка",
161 "service.errorHandler.text": "{name} не смог загрузиться.", 161 "service.errorHandler.text": "{name} не смог загрузиться.",
162 "service.screenshare.cancel": "Отмена",
162 "service.webviewLoader.loading": "Загрузка {service}", 163 "service.webviewLoader.loading": "Загрузка {service}",
163 "services.getStarted": "Начать работу", 164 "services.getStarted": "Начать работу",
164 "services.login": "Пожалуйста, войдите, чтобы использовать Ferdium.", 165 "services.login": "Пожалуйста, войдите, чтобы использовать Ferdium.",
@@ -214,6 +215,7 @@
214 "settings.app.form.enableTodos": "Включить задания Ferdium", 215 "settings.app.form.enableTodos": "Включить задания Ferdium",
215 "settings.app.form.hibernateOnStartup": "Оставлять сервисы в состоянии глубокого сна во время запуска", 216 "settings.app.form.hibernateOnStartup": "Оставлять сервисы в состоянии глубокого сна во время запуска",
216 "settings.app.form.hibernationStrategy": "Стратегия гибернации", 217 "settings.app.form.hibernationStrategy": "Стратегия гибернации",
218 "settings.app.form.hideCollapseButton": "Скрыть кнопку \"Свернуть\"",
217 "settings.app.form.hideDownloadButton": "Скрыть кнопку \"Загрузки\"", 219 "settings.app.form.hideDownloadButton": "Скрыть кнопку \"Загрузки\"",
218 "settings.app.form.hideNotificationsButton": "Скрыть кнопки уведомлений и звука", 220 "settings.app.form.hideNotificationsButton": "Скрыть кнопки уведомлений и звука",
219 "settings.app.form.hideSettingsButton": "Скрыть кнопку настроек", 221 "settings.app.form.hideSettingsButton": "Скрыть кнопку настроек",
@@ -244,6 +246,7 @@
244 "settings.app.form.splitMode": "Включить раздельный режим просмотра", 246 "settings.app.form.splitMode": "Включить раздельный режим просмотра",
245 "settings.app.form.startMinimized": "Запускать свернутым", 247 "settings.app.form.startMinimized": "Запускать свернутым",
246 "settings.app.form.universalDarkMode": "Включить универсальный темный режим", 248 "settings.app.form.universalDarkMode": "Включить универсальный темный режим",
249 "settings.app.form.useSelfSignedCertificates": "Принимать самоподписные сертификаты",
247 "settings.app.form.useTouchIdToUnlock": "Разрешить разблокировку Ferdium по TouchID", 250 "settings.app.form.useTouchIdToUnlock": "Разрешить разблокировку Ferdium по TouchID",
248 "settings.app.form.wakeUpHibernationSplay": "Чередование циклов cна/пробуждения для уменьшения нагрузки", 251 "settings.app.form.wakeUpHibernationSplay": "Чередование циклов cна/пробуждения для уменьшения нагрузки",
249 "settings.app.form.wakeUpHibernationStrategy": "Действия после автоматического просыпания", 252 "settings.app.form.wakeUpHibernationStrategy": "Действия после автоматического просыпания",
@@ -261,10 +264,15 @@
261 "settings.app.lockedPasswordInfo": "Пожалуйста, убедитесь, что установлен пароль, который вы запомните.\nЕсли вы потеряете этот пароль, вам придется переустановить Ferdium.", 264 "settings.app.lockedPasswordInfo": "Пожалуйста, убедитесь, что установлен пароль, который вы запомните.\nЕсли вы потеряете этот пароль, вам придется переустановить Ferdium.",
262 "settings.app.overallTheme": "Общая тема", 265 "settings.app.overallTheme": "Общая тема",
263 "settings.app.progressbarTheme": "Тема Прогресса", 266 "settings.app.progressbarTheme": "Тема Прогресса",
267 "settings.app.restart.restartDialogMessage": "Вы хотите перезапустить Ferdium?",
268 "settings.app.restart.restartDialogTitle": "Ferdium - Перезапуск приложения",
269 "settings.app.restart.restartLater": "Перезапустить позже",
270 "settings.app.restart.restartNow": "Перезапустить сейчас",
264 "settings.app.restartRequired": "Изменения требуют перезагрузки приложения", 271 "settings.app.restartRequired": "Изменения требуют перезагрузки приложения",
265 "settings.app.scheduledDNDInfo": "Запланированный режим \"не беспокоить\" позволяет вам определить период времени, в течение которого вы не желаете получать уведомления от Ferdium.", 272 "settings.app.scheduledDNDInfo": "Запланированный режим \"не беспокоить\" позволяет вам определить период времени, в течение которого вы не желаете получать уведомления от Ferdium.",
266 "settings.app.scheduledDNDTimeInfo": "Время в 24-часовом формате. Время окончания может быть раньше времени начала (например, начало 17:00, конец 09:00), чтобы включить ночной режим \"не беспокоить\".", 273 "settings.app.scheduledDNDTimeInfo": "Время в 24-часовом формате. Время окончания может быть раньше времени начала (например, начало 17:00, конец 09:00), чтобы включить ночной режим \"не беспокоить\".",
267 "settings.app.sectionAdvanced": "Расширенные настройки", 274 "settings.app.sectionAdvanced": "Расширенные настройки",
275 "settings.app.sectionHibernation": "Гибернация",
268 "settings.app.sectionLanguage": "Настройка языка", 276 "settings.app.sectionLanguage": "Настройка языка",
269 "settings.app.sectionPrivacy": "Настройки конфиденциальности", 277 "settings.app.sectionPrivacy": "Настройки конфиденциальности",
270 "settings.app.sectionServiceIconsSettings": "Настройки значков сервиса", 278 "settings.app.sectionServiceIconsSettings": "Настройки значков сервиса",
@@ -274,6 +282,7 @@
274 "settings.app.serverHelp": "Подключен к серверу {serverURL}", 282 "settings.app.serverHelp": "Подключен к серверу {serverURL}",
275 "settings.app.servicesUpdateStatusUpToDate": "Ваши сервисы актуальны", 283 "settings.app.servicesUpdateStatusUpToDate": "Ваши сервисы актуальны",
276 "settings.app.subheadlineCache": "Кэш", 284 "settings.app.subheadlineCache": "Кэш",
285 "settings.app.subheadlineDownloads": "Загрузки",
277 "settings.app.subheadlineFerdiumProfile": "Профиль Ferdium", 286 "settings.app.subheadlineFerdiumProfile": "Профиль Ferdium",
278 "settings.app.todoServerInfo": "Этот сервер будет использоваться для функции задач \"Ferdium Todo\".", 287 "settings.app.todoServerInfo": "Этот сервер будет использоваться для функции задач \"Ferdium Todo\".",
279 "settings.app.translationHelp": "Помогите нам перевести Ferdium на ваш язык.", 288 "settings.app.translationHelp": "Помогите нам перевести Ferdium на ваш язык.",
@@ -303,6 +312,8 @@
303 "settings.recipes.missingService": "Не можете найти сервис?", 312 "settings.recipes.missingService": "Не можете найти сервис?",
304 "settings.recipes.nothingFound": "Извините, но сервис не соответствует вашему поисковому запросу - но вы все еще можете добавить его, используя опцию \"Пользовательский сайт\". Пожалуйста, обратите внимание, что на сайте может отображаться больше служб, которые могли быть добавлены в новых версиях Ferdium. Для получения новых сервисов, пожалуйста, обновите Ferdium до актуальной версии.", 313 "settings.recipes.nothingFound": "Извините, но сервис не соответствует вашему поисковому запросу - но вы все еще можете добавить его, используя опцию \"Пользовательский сайт\". Пожалуйста, обратите внимание, что на сайте может отображаться больше служб, которые могли быть добавлены в новых версиях Ferdium. Для получения новых сервисов, пожалуйста, обновите Ferdium до актуальной версии.",
305 "settings.recipes.servicesSuccessfulAddedInfo": "Сервис успешно добавлен", 314 "settings.recipes.servicesSuccessfulAddedInfo": "Сервис успешно добавлен",
315 "settings.releasenotes.connectionError": "Произошла ошибка при подключении к Github, пожалуйста, повторите попытку позже.",
316 "settings.releasenotes.connectionErrorPageMissing": "Произошла ошибка при подключении к Github, страница, которую вы ищете, отсутствует.",
306 "settings.releasenotes.headline": "Информация о выпуске", 317 "settings.releasenotes.headline": "Информация о выпуске",
307 "settings.searchService": "Найти сервис", 318 "settings.searchService": "Найти сервис",
308 "settings.service.error.goBack": "Вернуться к сервисам", 319 "settings.service.error.goBack": "Вернуться к сервисам",
@@ -324,6 +335,7 @@
324 "settings.service.form.enableNotification": "Включить уведомления", 335 "settings.service.form.enableNotification": "Включить уведомления",
325 "settings.service.form.enableService": "Включить сервис", 336 "settings.service.form.enableService": "Включить сервис",
326 "settings.service.form.enableWakeUp": "Включить пробуждение", 337 "settings.service.form.enableWakeUp": "Включить пробуждение",
338 "settings.service.form.headlineAppearance": "Оформление",
327 "settings.service.form.headlineBadges": "Значки непрочитанных сообщений", 339 "settings.service.form.headlineBadges": "Значки непрочитанных сообщений",
328 "settings.service.form.headlineDarkReaderSettings": "Настройки тёмного чтения", 340 "settings.service.form.headlineDarkReaderSettings": "Настройки тёмного чтения",
329 "settings.service.form.headlineGeneral": "Общие", 341 "settings.service.form.headlineGeneral": "Общие",
@@ -440,6 +452,7 @@
440 "validation.minLength": "Кількість символів в {field} повина бути не меньше {length}", 452 "validation.minLength": "Кількість символів в {field} повина бути не меньше {length}",
441 "validation.oneRequired": "Необходим как минимум один", 453 "validation.oneRequired": "Необходим как минимум один",
442 "validation.required": "{field} необходимо", 454 "validation.required": "{field} необходимо",
455 "validation.url": "{field} не является действительным URL",
443 "webControls.back": "Назад", 456 "webControls.back": "Назад",
444 "webControls.forward": "Переслать", 457 "webControls.forward": "Переслать",
445 "webControls.goHome": "Домой", 458 "webControls.goHome": "Домой",
diff --git a/src/index.ts b/src/index.ts
index cccf0ef66..a602c994e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -245,6 +245,18 @@ const createWindow = () => {
245 openExternalUrl(url); 245 openExternalUrl(url);
246 return { action: 'deny' }; 246 return { action: 'deny' };
247 }); 247 });
248
249 // Handle will download event from main process (prevent download dialog)
250 contents.session.on('will-download', (_e, item) => {
251 const downloadFolderPath = retrieveSettingValue(
252 'downloadFolderPath',
253 DEFAULT_APP_SETTINGS.downloadFolderPath,
254 ) as string;
255
256 if (downloadFolderPath !== '') {
257 item.setSavePath(join(downloadFolderPath, item.getFilename()));
258 }
259 });
248 } 260 }
249 }); 261 });
250 262
@@ -393,7 +405,8 @@ const createWindow = () => {
393 }); 405 });
394 406
395 if (isMac) { 407 if (isMac) {
396 import('./electron/macOSPermissions').then(macOSPermissions => { 408 // Note: Do not remove the extension. See https://github.com/ferdium/ferdium-app/issues/1755 for explanation
409 import('./electron/macOSPermissions.js').then(macOSPermissions => {
397 const { askFormacOSPermissions } = macOSPermissions; 410 const { askFormacOSPermissions } = macOSPermissions;
398 411
399 setTimeout(() => askFormacOSPermissions(mainWindow!), ms('30s')); 412 setTimeout(() => askFormacOSPermissions(mainWindow!), ms('30s'));
@@ -782,3 +795,15 @@ app.on(
782 callback(checkIfCertIsPresent(certificate.data)); 795 callback(checkIfCertIsPresent(certificate.data));
783 }, 796 },
784); 797);
798
799ipcMain.on('relaunch-app', async (_, options) => {
800 // Ask user to confirm
801 const result = await dialog.showMessageBox(mainWindow!, options);
802
803 if (result.response === options.cancelId) {
804 return;
805 }
806
807 app.relaunch();
808 app.quit();
809});
diff --git a/src/jsUtils.ts b/src/jsUtils.ts
index 0befb8d56..145875cfa 100644
--- a/src/jsUtils.ts
+++ b/src/jsUtils.ts
@@ -28,12 +28,21 @@ export const safeParseInt = (text?: string | number | null) => {
28 return Math.max(adjustedNumber, 0); 28 return Math.max(adjustedNumber, 0);
29}; 29};
30 30
31export const acceleratorString = ( 31interface IAcceleratorString {
32 index: number, 32 keyCombo: string;
33 keyCombo: string, 33 index: number;
34 prefix: string = '(', 34 prefix?: string;
35 suffix: string = ')', 35 suffix?: string;
36) => (index <= 10 ? `${prefix}${keyCombo}+${index % 10}${suffix}` : ''); 36 maxIndex?: number;
37}
38export const acceleratorString = ({
39 index,
40 keyCombo,
41 prefix = '(',
42 suffix = ')',
43 maxIndex = 9,
44}: IAcceleratorString) =>
45 index <= maxIndex ? `${prefix}${keyCombo}+${index % 10}${suffix}` : '';
37 46
38export const removeNewLines = (input: string): string => 47export const removeNewLines = (input: string): string =>
39 input.replaceAll(/\r?\n|\r/g, ''); 48 input.replaceAll(/\r?\n|\r/g, '');
diff --git a/src/lib/Menu.ts b/src/lib/Menu.ts
index bb8eead3a..8fd8e0a73 100644
--- a/src/lib/Menu.ts
+++ b/src/lib/Menu.ts
@@ -496,7 +496,15 @@ function titleBarTemplateFactory(
496 }, 496 },
497 { 497 {
498 label: intl.formatMessage(menuItems.zoomIn), 498 label: intl.formatMessage(menuItems.zoomIn),
499 accelerator: `${cmdOrCtrlShortcutKey()}+plus`, 499 // TODO: Modify this logic once https://github.com/electron/electron/issues/40674 is fixed
500 // This is a workaround for the issue where the zoom in shortcut is not working
501 // This makes sure the accelerator is not registered
502 accelerator: isWindows
503 ? `${cmdOrCtrlShortcutKey()}++`
504 : `${cmdOrCtrlShortcutKey()}+Plus`,
505 registerAccelerator: !!isMac,
506 acceleratorWorksWhenHidden: !!isMac,
507 // ---------------------------
500 click() { 508 click() {
501 const activeService = getActiveService(); 509 const activeService = getActiveService();
502 if (!activeService) { 510 if (!activeService) {
@@ -1134,7 +1142,12 @@ class FranzMenu implements StoresProps {
1134 for (const [i, service] of services.allDisplayed.entries()) { 1142 for (const [i, service] of services.allDisplayed.entries()) {
1135 menu.push({ 1143 menu.push({
1136 label: this._getServiceName(service), 1144 label: this._getServiceName(service),
1137 accelerator: acceleratorString(i + 1, cmdOrCtrlShortcutKey(), '', ''), 1145 accelerator: acceleratorString({
1146 index: i + 1,
1147 keyCombo: cmdOrCtrlShortcutKey(),
1148 prefix: '',
1149 suffix: '',
1150 }),
1138 type: 'radio', 1151 type: 'radio',
1139 checked: service.isActive, 1152 checked: service.isActive,
1140 click: () => { 1153 click: () => {
diff --git a/src/models/Service.ts b/src/models/Service.ts
index a61df5570..ae04b8b58 100644
--- a/src/models/Service.ts
+++ b/src/models/Service.ts
@@ -7,6 +7,7 @@ import type ElectronWebView from 'react-electron-web-view';
7import { v4 as uuidV4 } from 'uuid'; 7import { v4 as uuidV4 } from 'uuid';
8import { needsToken } from '../api/apiBase'; 8import { needsToken } from '../api/apiBase';
9import { DEFAULT_SERVICE_ORDER, DEFAULT_SERVICE_SETTINGS } from '../config'; 9import { DEFAULT_SERVICE_ORDER, DEFAULT_SERVICE_SETTINGS } from '../config';
10import { isMac } from '../environment';
10import { todosStore } from '../features/todos'; 11import { todosStore } from '../features/todos';
11import { getFaviconUrl } from '../helpers/favicon-helpers'; 12import { getFaviconUrl } from '../helpers/favicon-helpers';
12import { isValidExternalURL, normalizedUrl } from '../helpers/url-helpers'; 13import { isValidExternalURL, normalizedUrl } from '../helpers/url-helpers';
@@ -536,6 +537,18 @@ export default class Service {
536 }); 537 });
537 538
538 if (webviewWebContents) { 539 if (webviewWebContents) {
540 // TODO: Modify this logic once https://github.com/electron/electron/issues/40674 is fixed
541 // This is a workaround for the issue where the zoom in shortcut is not working
542 if (!isMac) {
543 webviewWebContents.on('before-input-event', (event, input) => {
544 if (input.control && input.key === '+' && input.type === 'keyDown') {
545 event.preventDefault();
546 const currentZoom = this.webview?.getZoomLevel();
547 this.webview?.setZoomLevel(currentZoom + 0.5);
548 }
549 });
550 }
551
539 webviewWebContents.session.on('will-download', (event, item) => { 552 webviewWebContents.session.on('will-download', (event, item) => {
540 event.preventDefault(); 553 event.preventDefault();
541 554
diff --git a/src/stores/ServicesStore.ts b/src/stores/ServicesStore.ts
index 9f2cfeca3..58c6b2a87 100644
--- a/src/stores/ServicesStore.ts
+++ b/src/stores/ServicesStore.ts
@@ -917,12 +917,13 @@ export default class ServicesStore extends TypedStore {
917 917
918 if (service.isNotificationEnabled) { 918 if (service.isNotificationEnabled) {
919 let title: string; 919 let title: string;
920 options.icon = service.iconUrl;
921 if (this.stores.settings.all.app.privateNotifications === true) { 920 if (this.stores.settings.all.app.privateNotifications === true) {
922 // Remove message data from notification in private mode 921 // Remove message data from notification in private mode
923 options.body = ''; 922 options.body = '';
923 options.icon = service.icon;
924 title = `Notification from ${service.name}`; 924 title = `Notification from ${service.name}`;
925 } else { 925 } else {
926 options.icon = options.icon || service.icon;
926 options.body = typeof options.body === 'string' ? options.body : ''; 927 options.body = typeof options.body === 'string' ? options.body : '';
927 title = 928 title =
928 typeof args[0].title === 'string' ? args[0].title : service.name; 929 typeof args[0].title === 'string' ? args[0].title : service.name;
diff --git a/src/stores/UserStore.ts b/src/stores/UserStore.ts
index ade68e4d4..adf144b74 100644
--- a/src/stores/UserStore.ts
+++ b/src/stores/UserStore.ts
@@ -408,7 +408,7 @@ export default class UserStore extends TypedStore {
408 const parsedUrl = new URL(url); 408 const parsedUrl = new URL(url);
409 const params = new URLSearchParams(parsedUrl.search.slice(1)); 409 const params = new URLSearchParams(parsedUrl.search.slice(1));
410 410
411 // TODO: Remove the neccesity for `as string` 411 // TODO: Remove the necessity for `as string`
412 params.append('authToken', this.authToken!); 412 params.append('authToken', this.authToken!);
413 413
414 return `${parsedUrl.origin}${parsedUrl.pathname}?${params.toString()}`; 414 return `${parsedUrl.origin}${parsedUrl.pathname}?${params.toString()}`;
diff --git a/src/webview/contextMenuBuilder.ts b/src/webview/contextMenuBuilder.ts
index 0a8b9c57b..d92e18aa9 100644
--- a/src/webview/contextMenuBuilder.ts
+++ b/src/webview/contextMenuBuilder.ts
@@ -130,8 +130,8 @@ interface ContextMenuStringTable {
130 translatorLanguage: string; 130 translatorLanguage: string;
131 }) => string; 131 }) => string;
132 openLinkUrl: () => string; 132 openLinkUrl: () => string;
133 openLinkInFerdiumUrl: () => string;
134 openInBrowser: () => string; 133 openInBrowser: () => string;
134 openInFerdium: () => string;
135 copyLinkUrl: () => string; 135 copyLinkUrl: () => string;
136 copyImageUrl: () => string; 136 copyImageUrl: () => string;
137 copyImage: () => string; 137 copyImage: () => string;
@@ -158,8 +158,8 @@ const contextMenuStringTable: ContextMenuStringTable = {
158 `Translate to ${translatorLanguage}`, 158 `Translate to ${translatorLanguage}`,
159 translateLanguage: ({ translatorLanguage }) => `${translatorLanguage}`, 159 translateLanguage: ({ translatorLanguage }) => `${translatorLanguage}`,
160 openLinkUrl: () => 'Open Link', 160 openLinkUrl: () => 'Open Link',
161 openLinkInFerdiumUrl: () => 'Open Link in Ferdium',
162 openInBrowser: () => 'Open in Browser', 161 openInBrowser: () => 'Open in Browser',
162 openInFerdium: () => 'Open in Ferdium',
163 copyLinkUrl: () => 'Copy Link', 163 copyLinkUrl: () => 'Copy Link',
164 copyImageUrl: () => 'Copy Image Address', 164 copyImageUrl: () => 'Copy Image Address',
165 copyImage: () => 'Copy Image', 165 copyImage: () => 'Copy Image',
@@ -299,10 +299,13 @@ export class ContextMenuBuilder {
299 this.addPastePlain(menu, menuInfo); 299 this.addPastePlain(menu, menuInfo);
300 this.addInspectElement(menu, menuInfo); 300 this.addInspectElement(menu, menuInfo);
301 this.processMenu(menu); 301 this.processMenu(menu);
302 302 this.addSeparator(menu);
303 this.copyPageUrl(menu, menuInfo); 303 this.copyPageUrl(menu, menuInfo);
304 this.goToHomePage(menu, menuInfo); 304 this.addSeparator(menu);
305 this.openInBrowser(menu, menuInfo); 305 this.openInBrowser(menu, menuInfo);
306 this.openInFerdium(menu, menuInfo);
307 this.addSeparator(menu);
308 this.goToHomePage(menu, menuInfo);
306 309
307 return menu; 310 return menu;
308 } 311 }
@@ -340,16 +343,8 @@ export class ContextMenuBuilder {
340 }, 343 },
341 }); 344 });
342 345
343 const openInFerdiumLink = new MenuItem({
344 label: this.stringTable.openLinkInFerdiumUrl(),
345 click: () => {
346 window.location.href = menuInfo.linkURL;
347 },
348 });
349
350 menu.append(copyLink); 346 menu.append(copyLink);
351 menu.append(openLink); 347 menu.append(openLink);
352 menu.append(openInFerdiumLink);
353 348
354 if (this.isSrcUrlValid(menuInfo)) { 349 if (this.isSrcUrlValid(menuInfo)) {
355 this.addSeparator(menu); 350 this.addSeparator(menu);
@@ -358,13 +353,15 @@ export class ContextMenuBuilder {
358 353
359 this.addInspectElement(menu, menuInfo); 354 this.addInspectElement(menu, menuInfo);
360 this.processMenu(menu); 355 this.processMenu(menu);
361
362 this.addSeparator(menu); 356 this.addSeparator(menu);
363 this.goBack(menu); 357 this.goBack(menu);
364 this.goForward(menu); 358 this.goForward(menu);
365 this.copyPageUrl(menu, menuInfo); 359 this.copyPageUrl(menu, menuInfo);
366 this.goToHomePage(menu, menuInfo); 360 this.addSeparator(menu);
367 this.openInBrowser(menu, menuInfo); 361 this.openInBrowser(menu, menuInfo);
362 this.openInFerdium(menu, menuInfo);
363 this.addSeparator(menu);
364 this.goToHomePage(menu, menuInfo);
368 365
369 return menu; 366 return menu;
370 } 367 }
@@ -384,16 +381,19 @@ export class ContextMenuBuilder {
384 this.addTranslateItems(menu, menuInfo); 381 this.addTranslateItems(menu, menuInfo);
385 } 382 }
386 this.addCopy(menu, menuInfo); 383 this.addCopy(menu, menuInfo);
387 this.addInspectElement(menu, menuInfo);
388 // @ts-expect-error Expected 1 arguments, but got 2.
389 this.processMenu(menu, menuInfo);
390 384
385 this.addInspectElement(menu, menuInfo);
386 this.processMenu(menu);
391 this.addSeparator(menu); 387 this.addSeparator(menu);
392 this.goBack(menu); 388 this.goBack(menu);
393 this.goForward(menu); 389 this.goForward(menu);
390 this.addSeparator(menu);
394 this.copyPageUrl(menu, menuInfo); 391 this.copyPageUrl(menu, menuInfo);
395 this.goToHomePage(menu, menuInfo); 392 this.addSeparator(menu);
396 this.openInBrowser(menu, menuInfo); 393 this.openInBrowser(menu, menuInfo);
394 this.openInFerdium(menu, menuInfo);
395 this.addSeparator(menu);
396 this.goToHomePage(menu, menuInfo);
397 397
398 return menu; 398 return menu;
399 } 399 }
@@ -412,8 +412,7 @@ export class ContextMenuBuilder {
412 this.addImageItems(menu, menuInfo); 412 this.addImageItems(menu, menuInfo);
413 } 413 }
414 this.addInspectElement(menu, menuInfo); 414 this.addInspectElement(menu, menuInfo);
415 // @ts-expect-error Expected 1 arguments, but got 2. 415 this.processMenu(menu);
416 this.processMenu(menu, menuInfo);
417 416
418 return menu; 417 return menu;
419 } 418 }
@@ -970,6 +969,26 @@ export class ContextMenuBuilder {
970 return menu; 969 return menu;
971 } 970 }
972 971
972 /**
973 * Adds the 'open in ferdium' menu item.
974 */
975 openInFerdium(
976 menu: Electron.CrossProcessExports.Menu,
977 menuInfo: IContextMenuParams,
978 ) {
979 menu.append(
980 new MenuItem({
981 label: this.stringTable.openInFerdium(),
982 enabled: true,
983 click: () => {
984 window.location.href = menuInfo.linkURL;
985 },
986 }),
987 );
988
989 return menu;
990 }
991
973 _sendNotificationOnClipboardEvent( 992 _sendNotificationOnClipboardEvent(
974 isDisabled: boolean, 993 isDisabled: boolean,
975 notificationText: () => string, 994 notificationText: () => string,
diff --git a/src/webview/notifications.ts b/src/webview/notifications.ts
index e4401ab6e..0da86fbba 100644
--- a/src/webview/notifications.ts
+++ b/src/webview/notifications.ts
@@ -31,52 +31,51 @@ export class NotificationsHandler {
31} 31}
32 32
33export const notificationsClassDefinition = `(() => { 33export const notificationsClassDefinition = `(() => {
34 class Notification { 34class Notification {
35 static permission = 'granted'; 35 static permission = 'granted';
36
37 constructor(title = '', options = {}) {
38 this.title = title;
39 this.options = options;
40 try {
41 window.ferdium.displayNotification(title, options)
42 .then(() => {
43 if (typeof (this.onClick) === 'function') {
44 this.onClick();
45 }
46 });
47 } catch(error) {
48 this.options.onClick = null;
49 window.ferdium.displayNotification(title, options)
50 .then(() => {
51 if (typeof (this.onClick) === 'function') {
52 this.onClick();
53 }
54 });
55 }
56 }
57 36
58 static requestPermission(cb = null) { 37 static _notification;
59 if (!cb) {
60 return new Promise((resolve) => {
61 resolve(Notification.permission);
62 });
63 }
64 38
65 if (typeof (cb) === 'function') { 39 constructor(title = '', options = {}) {
66 return cb(Notification.permission); 40 Notification._displayNotification(title, options);
67 } 41 }
68 42
69 return Notification.permission; 43 static _displayNotification(title, options) {
70 } 44 Notification._notification = window.ferdium
45 .displayNotification(title, options)
46 .then(() => {
47 // TODO: After several tries, we couldn't find a way to trigger the native notification onclick event.
48 // This was needed so that user could go to the specific context when clicking on the notification (it only goes to the service now).
49 // For now, we don't do anything here
50 });
51 }
71 52
72 onNotify(data) { 53 static requestPermission(cb) {
73 return data; 54 if (typeof cb === 'function') {
55 cb(Notification.permission);
74 } 56 }
75 57
76 onClick() {} 58 return Promise.resolve(Notification.permission);
59 }
60
61 onNotify(data) {
62 return data;
63 }
77 64
78 close() {} 65 close() {
66 if (Notification._notification) {
67 Notification._notification = null;
68 }
79 } 69 }
80 70
71 onclick() {}
72
73 onclose() {}
74
75 onerror() {}
76
77 onshow() {}
78}
79
81 window.Notification = Notification; 80 window.Notification = Notification;
82})();`; 81})();`;
diff --git a/src/webview/recipe.ts b/src/webview/recipe.ts
index a35a99699..ad2215ffd 100644
--- a/src/webview/recipe.ts
+++ b/src/webview/recipe.ts
@@ -121,7 +121,7 @@ contextBridge.exposeInMainWorld('ferdium', {
121 setDialogTitle: (title: string | null | undefined) => 121 setDialogTitle: (title: string | null | undefined) =>
122 dialogTitleHandler.setDialogTitle(title), 122 dialogTitleHandler.setDialogTitle(title),
123 displayNotification: (title: string, options: any) => { 123 displayNotification: (title: string, options: any) => {
124 notificationsHandler.displayNotification( 124 return notificationsHandler.displayNotification(
125 title, 125 title,
126 // The following line is needed so that a proper clone of the "options" object is made. 126 // The following line is needed so that a proper clone of the "options" object is made.
127 // This line was causing issues with some services. 127 // This line was causing issues with some services.
diff --git a/test/jsUtils.test.ts b/test/jsUtils.test.ts
index 508f5dd9f..b40b21b2a 100644
--- a/test/jsUtils.test.ts
+++ b/test/jsUtils.test.ts
@@ -180,23 +180,59 @@ describe('jsUtils', () => {
180 180
181 describe('acceleratorString', () => { 181 describe('acceleratorString', () => {
182 it('handles without prefix and suffix', () => { 182 it('handles without prefix and suffix', () => {
183 expect(jsUtils.acceleratorString(5, 'abc')).toEqual('(abc+5)'); 183 expect(
184 jsUtils.acceleratorString({
185 index: 5,
186 keyCombo: 'abc',
187 }),
188 ).toEqual('(abc+5)');
184 }); 189 });
185 190
186 it('handles index = 0', () => { 191 it('handles index = 0', () => {
187 expect(jsUtils.acceleratorString(0, 'abc')).toEqual('(abc+0)'); 192 expect(
193 jsUtils.acceleratorString({
194 index: 0,
195 keyCombo: 'abc',
196 }),
197 ).toEqual('(abc+0)');
188 }); 198 });
189 199
190 it('handles index = 1', () => { 200 it('handles index = 1', () => {
191 expect(jsUtils.acceleratorString(1, 'abc')).toEqual('(abc+1)'); 201 expect(
202 jsUtils.acceleratorString({
203 index: 1,
204 keyCombo: 'abc',
205 }),
206 ).toEqual('(abc+1)');
192 }); 207 });
193 208
194 it('handles index = 10', () => { 209 it('handles index = 10', () => {
195 expect(jsUtils.acceleratorString(10, 'abc')).toEqual('(abc+0)'); 210 expect(
211 jsUtils.acceleratorString({
212 index: 10,
213 keyCombo: 'abc',
214 maxIndex: 10,
215 }),
216 ).toEqual('(abc+0)');
196 }); 217 });
197 218
198 it('handles index = 11', () => { 219 it('handles index = 11', () => {
199 expect(jsUtils.acceleratorString(11, 'abc')).toEqual(''); 220 expect(
221 jsUtils.acceleratorString({
222 index: 11,
223 keyCombo: 'abc',
224 }),
225 ).toEqual('');
226 });
227
228 it('handles index = 9, maxIndex = 8', () => {
229 expect(
230 jsUtils.acceleratorString({
231 index: 9,
232 maxIndex: 8,
233 keyCombo: 'abc',
234 }),
235 ).toEqual('');
200 }); 236 });
201 }); 237 });
202 238