From cfa99f0e386213a2ad9960a15f11bf064f8121df Mon Sep 17 00:00:00 2001 From: Aris Sidiropoulos Date: Wed, 17 Sep 2025 21:13:17 +0300 Subject: [PATCH 01/32] fix(Intro): make all text lowercase to match the rest --- src/routes/Intro/Intro.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 48acadcd4..e85a65d97 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -387,7 +387,7 @@ const Intro = ({ queryParams }) => { { state.form === SIGNUP_FORM ? : null @@ -395,7 +395,7 @@ const Intro = ({ queryParams }) => { { state.form === LOGIN_FORM ? : null @@ -403,7 +403,7 @@ const Intro = ({ queryParams }) => { { state.form === SIGNUP_FORM ? : null From 010f2e0390377006e9e89009f26c06891aae1ce9 Mon Sep 17 00:00:00 2001 From: Aris Sidiropoulos Date: Fri, 19 Sep 2025 11:43:36 +0300 Subject: [PATCH 02/32] fix(Intro): follow up commit, best practice solution --- src/routes/Intro/Intro.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index e85a65d97..6de7835d5 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -387,7 +387,7 @@ const Intro = ({ queryParams }) => { { state.form === SIGNUP_FORM ? : null @@ -395,7 +395,7 @@ const Intro = ({ queryParams }) => { { state.form === LOGIN_FORM ? : null @@ -403,7 +403,7 @@ const Intro = ({ queryParams }) => { { state.form === SIGNUP_FORM ? : null From 5bea8a83c68a0db968f6adb2fea7b1ef82d17271 Mon Sep 17 00:00:00 2001 From: Aris Sidiropoulos Date: Fri, 19 Sep 2025 16:45:25 +0300 Subject: [PATCH 03/32] fix(Intro): clean up unused css class --- src/routes/Intro/styles.less | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/routes/Intro/styles.less b/src/routes/Intro/styles.less index 32fc73d79..7aedeae48 100644 --- a/src/routes/Intro/styles.less +++ b/src/routes/Intro/styles.less @@ -102,9 +102,6 @@ text-align: center; } - .uppercase { - text-transform: uppercase; - } } .submit-button, .guest-login-button, .signup-form-button, .login-form-button { From 10a98fcecf8ab807e4e1ac1c36887ab94435071e Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Tue, 23 Sep 2025 23:45:57 +0300 Subject: [PATCH 04/32] chore: code styles --- src/routes/Intro/styles.less | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes/Intro/styles.less b/src/routes/Intro/styles.less index 7aedeae48..31a09d54c 100644 --- a/src/routes/Intro/styles.less +++ b/src/routes/Intro/styles.less @@ -101,7 +101,6 @@ color: var(--primary-foreground-color); text-align: center; } - } .submit-button, .guest-login-button, .signup-form-button, .login-form-button { From 67f5446030c0317dfb3941df84911edcfcd79253 Mon Sep 17 00:00:00 2001 From: actuallylost Date: Sat, 27 Sep 2025 16:23:06 +0300 Subject: [PATCH 05/32] chore: fix all typos and misspellings --- .github/workflows/build.yml | 4 ++-- CODE_OF_CONDUCT.md | 4 ++-- src/App/styles.less | 4 ++-- src/common/FileDrop/FileDrop.tsx | 2 +- src/common/useNotifications.d.ts | 4 ++-- src/components/BottomSheet/BottomSheet.less | 2 +- src/components/ColorInput/ColorPicker/ColorPicker.js | 2 +- src/components/ColorInput/ColorPicker/styles.less | 2 +- src/components/ContinueWatchingItem/index.js | 4 ++-- src/routes/Player/SubtitlesMenu/SubtitlesMenu.js | 2 +- .../Settings/Streaming/URLsManager/AddItem/AddItem.tsx | 6 +++--- src/types/models/Player.d.ts | 2 +- webpack.config.js | 4 ++-- 13 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b41ff7317..95c70f581 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ on: branches: - development tags-ignore: - - '**' + - "**" pull_request: branches: - development @@ -33,7 +33,7 @@ jobs: run: npm test - name: Lint run: npm run lint - # Create recursivelly the destiantion dir with + # Create recursively the destination dir with # "--parrents where no error if existing, make parent directories as needed." - run: mkdir -p ./build/${{ github.head_ref || github.ref_name }} - name: Deploy to GitHub Pages diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 921fba0a7..0933226b2 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -29,10 +29,10 @@ Project maintainers are responsible for enforcing this code of conduct. They can ## Suggestions for newbies - Contributors are welcomed to use AI models as "help" in solving issues, but you must always double check the code that you're submitting. -- Refrain from excesive comments generated by AI. +- Refrain from excessive comments generated by AI. - Refrain from docs generated entirely by AI. - Always check what files you are committing and submitting to the PR when you are using any agent for help or an AI model. -- If you don't how to tackle a problem and AI can't help you, please just ask or look in Stack Overlflow, Google, Medium etc. +- If you don't how to tackle a problem and AI can't help you, please just ask or look in Stack Overflow, Google, Medium etc. - Learning how to code is fun and easier when using AI, but sometimes it might be just too much ... what are you going to learn, if AI does everything for you and you don't know what the code you are submitting actually does?! ## Scope diff --git a/src/App/styles.less b/src/App/styles.less index 373b46900..adce0ae5c 100644 --- a/src/App/styles.less +++ b/src/App/styles.less @@ -35,7 +35,7 @@ @top-overlay-size: 5.25rem; @bottom-overlay-size: 0rem; @overlap-size: 3rem; -@transparency-grandient-pad: 6rem; +@transparency-gradient-pad: 6rem; :root { --landscape-shape-ratio: 0.5625; @@ -69,7 +69,7 @@ --top-overlay-size: @top-overlay-size; --bottom-overlay-size: @bottom-overlay-size; --overlap-size: @overlap-size; - --transparency-grandient-pad: @transparency-grandient-pad; + --transparency-gradient-pad: @transparency-gradient-pad; --safe-area-inset-top: @safe-area-inset-top; --safe-area-inset-right: @safe-area-inset-right; --safe-area-inset-bottom: @safe-area-inset-bottom; diff --git a/src/common/FileDrop/FileDrop.tsx b/src/common/FileDrop/FileDrop.tsx index aae4e146b..2993991e7 100644 --- a/src/common/FileDrop/FileDrop.tsx +++ b/src/common/FileDrop/FileDrop.tsx @@ -42,7 +42,7 @@ const FileDropProvider = ({ className, children }: Props) => { .then((buffer) => { listeners .filter(([type]) => file.type ? type === file.type : isFileType(buffer, type)) - .forEach(([, listerner]) => listerner(file.name, buffer)); + .forEach(([, listener]) => listener(file.name, buffer)); }); } diff --git a/src/common/useNotifications.d.ts b/src/common/useNotifications.d.ts index e3cae5b81..bfe63d67b 100644 --- a/src/common/useNotifications.d.ts +++ b/src/common/useNotifications.d.ts @@ -1,2 +1,2 @@ -declare const useNotifcations: () => Notifications; -export = useNotifcations; +declare const useNotifications: () => Notifications; +export = useNotifications; diff --git a/src/components/BottomSheet/BottomSheet.less b/src/components/BottomSheet/BottomSheet.less index f7e3315e1..7fcc6e508 100644 --- a/src/components/BottomSheet/BottomSheet.less +++ b/src/components/BottomSheet/BottomSheet.less @@ -86,7 +86,7 @@ } } -@media only screen and (min-width: @small) and (orientation: portait) { +@media only screen and (min-width: @small) and (orientation: portrait) { .bottom-sheet { display: none; } diff --git a/src/components/ColorInput/ColorPicker/ColorPicker.js b/src/components/ColorInput/ColorPicker/ColorPicker.js index 823ea55c8..3b66fcdf4 100644 --- a/src/components/ColorInput/ColorPicker/ColorPicker.js +++ b/src/components/ColorInput/ColorPicker/ColorPicker.js @@ -21,7 +21,7 @@ const ColorPicker = ({ className, value, onInput }) => { showRGB: false, showAlpha: true }); - const pickerClipboard = pickerElementRef.current.querySelector('.a-color-picker-clipbaord'); + const pickerClipboard = pickerElementRef.current.querySelector('.a-color-picker-clipboard'); if (pickerClipboard instanceof HTMLElement) { pickerClipboard.tabIndex = -1; } diff --git a/src/components/ColorInput/ColorPicker/styles.less b/src/components/ColorInput/ColorPicker/styles.less index 7156d440d..536228ed1 100644 --- a/src/components/ColorInput/ColorPicker/styles.less +++ b/src/components/ColorInput/ColorPicker/styles.less @@ -16,7 +16,7 @@ box-shadow: 0 0 .2rem var(--color-surfacedark); } - :global(.a-color-picker-clipbaord) { + :global(.a-color-picker-clipboard) { pointer-events: none; } } \ No newline at end of file diff --git a/src/components/ContinueWatchingItem/index.js b/src/components/ContinueWatchingItem/index.js index 5d3b2dd76..2c50f3c22 100644 --- a/src/components/ContinueWatchingItem/index.js +++ b/src/components/ContinueWatchingItem/index.js @@ -1,5 +1,5 @@ // Copyright (C) 2017-2023 Smart code 203358507 -const ContineWatchingItem = require('./ContinueWatchingItem'); +const ContinueWatchingItem = require('./ContinueWatchingItem'); -module.exports = ContineWatchingItem; +module.exports = ContinueWatchingItem; diff --git a/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js b/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js index d94c5f70b..fe690d0c4 100644 --- a/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js +++ b/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js @@ -228,7 +228,7 @@ const SubtitlesMenu = React.memo((props) => { /> { setInputValue(target.value); }, []); - const onSumbit = useCallback(() => { + const onSubmit = useCallback(() => { handleAddUrl(inputValue); }, [inputValue]); @@ -27,11 +27,11 @@ const AddItem = ({ onCancel, handleAddUrl }: Props) => { className={styles['input']} value={inputValue} onChange={handleValueChange} - onSubmit={onSumbit} + onSubmit={onSubmit} placeholder={'Enter URL'} />
- +
+ +
+ { + grouped.map(({ name, label, shortcuts }) => ( + + )) + } +
+ + + ), document.body); +}; + +export default ShortcutsModal; diff --git a/src/App/ShortcutsModal/index.ts b/src/App/ShortcutsModal/index.ts new file mode 100644 index 000000000..5a7549fac --- /dev/null +++ b/src/App/ShortcutsModal/index.ts @@ -0,0 +1,2 @@ +import ShortcutsModal from './ShortcutsModal'; +export default ShortcutsModal; diff --git a/src/App/ShortcutsModal/styles.less b/src/App/ShortcutsModal/styles.less new file mode 100644 index 000000000..ebbc19c62 --- /dev/null +++ b/src/App/ShortcutsModal/styles.less @@ -0,0 +1,91 @@ +@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less'; + +.shortcuts-modal { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + display: flex; + align-items: center; + justify-content: center; + + .backdrop { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: @color-background-dark5-40; + cursor: pointer; + } + + .container { + position: relative; + display: flex; + flex-direction: column; + gap: 1rem; + max-height: 80%; + max-width: 80%; + border-radius: var(--border-radius); + background-color: var(--modal-background-color); + box-shadow: var(--outer-glow); + overflow-y: auto; + + .header { + flex: none; + display: flex; + justify-content: space-between; + align-items: center; + height: 5rem; + padding-left: 2.5rem; + padding-right: 1rem; + + .title { + position: relative; + font-size: 1.5rem; + font-weight: 500; + color: var(--primary-foreground-color); + } + + .close-button { + position: relative; + width: 3rem; + height: 3rem; + padding: 0.5rem; + border-radius: var(--border-radius); + z-index: 2; + + .icon { + display: block; + width: 100%; + height: 100%; + color: var(--primary-foreground-color); + opacity: 0.4; + } + + &:hover, &:focus { + .icon { + opacity: 1; + color: var(--primary-foreground-color); + } + } + + &:focus { + outline-color: var(--primary-foreground-color); + } + } + } + + .content { + position: relative; + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 3rem; + padding: 0 2.5rem; + padding-bottom: 2rem; + overflow-y: auto; + } + } +} \ No newline at end of file diff --git a/src/common/Shortcuts/Shortcuts.tsx b/src/common/Shortcuts/Shortcuts.tsx new file mode 100644 index 000000000..f41e08271 --- /dev/null +++ b/src/common/Shortcuts/Shortcuts.tsx @@ -0,0 +1,54 @@ +import React, { createContext, useCallback, useContext, useEffect } from 'react'; +import shortcuts from './shortcuts'; + +const SHORTCUTS = shortcuts.map(({ shortcuts }) => shortcuts).flat(); + +export type ShortcutName = string; +export type ShortcutListener = () => void; + +interface ShortcutsContext { + grouped: ShortcutGroup[], +} + +const ShortcutsContext = createContext({} as ShortcutsContext); + +type Props = { + children: JSX.Element, + onShortcut: (name: ShortcutName) => void, +}; + +const ShortcutsProvider = ({ children, onShortcut }: Props) => { + const onKeyDown = useCallback(({ ctrlKey, shiftKey, key }: KeyboardEvent) => { + SHORTCUTS.forEach(({ name, combos }) => combos.forEach((keys) => { + const modifers = (keys.includes('Ctrl') ? ctrlKey : true) + && (keys.includes('Shift') ? shiftKey : true); + + if (modifers && keys.includes(key.toUpperCase())) { + onShortcut(name as ShortcutName); + } + })); + }, [onShortcut]); + + useEffect(() => { + document.addEventListener('keydown', onKeyDown); + + return () => { + document.removeEventListener('keydown', onKeyDown); + }; + }, [onKeyDown]); + + return ( + + {children} + + ); +}; + +const useShortcuts = () => { + return useContext(ShortcutsContext); +}; + +export { + ShortcutsProvider, + useShortcuts +}; diff --git a/src/common/Shortcuts/index.ts b/src/common/Shortcuts/index.ts new file mode 100644 index 000000000..f7fa38a18 --- /dev/null +++ b/src/common/Shortcuts/index.ts @@ -0,0 +1,5 @@ +import { ShortcutsProvider, useShortcuts } from './Shortcuts'; +export { + ShortcutsProvider, + useShortcuts, +}; diff --git a/src/common/Shortcuts/shortcuts.ts b/src/common/Shortcuts/shortcuts.ts new file mode 100644 index 000000000..d918733af --- /dev/null +++ b/src/common/Shortcuts/shortcuts.ts @@ -0,0 +1,91 @@ +const shortcuts: ShortcutGroup[] = [ + { + name: 'general', + label: 'SETTINGS_NAV_GENERAL', + shortcuts: [ + { + name: 'navigateTabs', + label: 'SETTINGS_SHORTCUT_NAVIGATE_MENUS', + combos: [['1', '2', '3', '4', '5', '6']], + }, + { + name: 'navigateSearch', + label: 'SETTINGS_SHORTCUT_GO_TO_SEARCH', + combos: [['0']], + }, + { + name: 'fullscreen', + label: 'SETTINGS_SHORTCUT_FULLSCREEN', + combos: [['F']], + }, + { + name: 'exit', + label: 'SETTINGS_SHORTCUT_EXIT_BACK', + combos: [['Escape']], + }, + { + name: 'shortcuts', + label: 'SETTINGS_SHORTCUT_SHORTCUTS', + combos: [['Ctrl', '?']], + }, + ] + }, + { + name: 'player', + label: 'SETTINGS_NAV_PLAYER', + shortcuts: [ + { + name: 'playPause', + label: 'SETTINGS_SHORTCUT_PLAY_PAUSE', + combos: [['Space']], + }, + { + name: 'seekForward', + label: 'SETTINGS_SHORTCUT_SEEK_FORWARD', + combos: [['ArrowRight'], ['Shift', 'ArrowRight']], + }, + { + name: 'seekBackward', + label: 'SETTINGS_SHORTCUT_SEEK_BACKWARD', + combos: [['ArrowLeft'], ['Shift', 'ArrowLeft']], + }, + { + name: 'volumeUp', + label: 'SETTINGS_SHORTCUT_VOLUME_UP', + combos: [['ArrowUp']], + }, + { + name: 'volumeDown', + label: 'SETTINGS_SHORTCUT_VOLUME_DOWN', + combos: [['ArrowDown']], + }, + { + name: 'subtitlesSize', + label: 'SETTINGS_SHORTCUT_SUBTITLES_SIZE', + combos: [['-'], ['=']], + }, + { + name: 'subtitlesDelay', + label: 'SETTINGS_SHORTCUT_SUBTITLES_DELAY', + combos: [['G'], ['H']], + }, + { + name: 'subtitlesMenu', + label: 'SETTINGS_SHORTCUT_MENU_SUBTITLES', + combos: [['S']], + }, + { + name: 'audioMenu', + label: 'SETTINGS_SHORTCUT_MENU_AUDIO', + combos: [['A']], + }, + { + name: 'infoMenu', + label: 'SETTINGS_SHORTCUT_MENU_INFO', + combos: [['I']], + }, + ] + }, +]; + +export default shortcuts; diff --git a/src/common/Shortcuts/types.d.ts b/src/common/Shortcuts/types.d.ts new file mode 100644 index 000000000..e4180616d --- /dev/null +++ b/src/common/Shortcuts/types.d.ts @@ -0,0 +1,11 @@ +type Shortcut = { + name: string, + label: string, + combos: string[][], +}; + +type ShortcutGroup = { + name: string, + label: string, + shortcuts: Shortcut[], +}; diff --git a/src/common/index.js b/src/common/index.js index 25df5c158..0b9cb252f 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -4,6 +4,7 @@ const { FileDropProvider, onFileDrop } = require('./FileDrop'); const { PlatformProvider, usePlatform } = require('./Platform'); const { ToastProvider, useToast } = require('./Toast'); const { TooltipProvider, Tooltip } = require('./Tooltips'); +const { ShortcutsProvider, useShortcuts } = require('./Shortcuts'); const comparatorWithPriorities = require('./comparatorWithPriorities'); const CONSTANTS = require('./CONSTANTS'); const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender'); @@ -35,6 +36,8 @@ module.exports = { onFileDrop, PlatformProvider, usePlatform, + ShortcutsProvider, + useShortcuts, ToastProvider, useToast, TooltipProvider, diff --git a/src/components/ShortcutsGroup/Combos/Combos.less b/src/components/ShortcutsGroup/Combos/Combos.less new file mode 100644 index 000000000..a862d54ca --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/Combos.less @@ -0,0 +1,22 @@ +.combos { + position: relative; + display: flex; + overflow: visible; + + .combo { + position: relative; + display: flex; + overflow: visible; + + .separator { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 3.5rem; + font-size: 1rem; + color: var(--primary-foreground-color); + opacity: 0.6; + } + } +} \ No newline at end of file diff --git a/src/components/ShortcutsGroup/Combos/Combos.tsx b/src/components/ShortcutsGroup/Combos/Combos.tsx new file mode 100644 index 000000000..0168441bc --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/Combos.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import Keys from './Keys'; +import styles from './Combos.less'; + +type Props = { + combos: string[][], +}; + +const Combos = ({ combos }: Props) => { + const { t } = useTranslation(); + + return ( +
+ { + combos.map((keys, index) => ( +
+ + { + index < (combos.length - 1) && ( +
+ { t('SETTINGS_SHORTCUT_OR') } +
+ ) + } +
+ )) + } +
+ ); +}; + +export default Combos; diff --git a/src/components/ShortcutsGroup/Combos/Keys/Keys.less b/src/components/ShortcutsGroup/Combos/Keys/Keys.less new file mode 100644 index 000000000..7bb8c76e7 --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/Keys/Keys.less @@ -0,0 +1,26 @@ +kbd { + flex: none; + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + height: 2.5rem; + min-width: 2.5rem; + padding: 0 1rem; + font-size: 1rem; + font-weight: 500; + color: var(--primary-foreground-color); + border-radius: 0.25em; + box-shadow: 0 4px 0 1px rgba(255, 255, 255, 0.1); + background-color: var(--overlay-color); +} + +.separator { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 2.5rem; + font-size: 1rem; + color: var(--primary-foreground-color); +} \ No newline at end of file diff --git a/src/components/ShortcutsGroup/Combos/Keys/Keys.tsx b/src/components/ShortcutsGroup/Combos/Keys/Keys.tsx new file mode 100644 index 000000000..71ec610da --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/Keys/Keys.tsx @@ -0,0 +1,51 @@ +import React, { Fragment, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import styles from './Keys.less'; + +type Props = { + keys: string[], +}; + +const Keys = ({ keys }: Props) => { + const { t } = useTranslation(); + + const keyLabelMap: Record = useMemo(() => ({ + 'Shift': `⇧ ${t('SETTINGS_SHORTCUT_SHIFT')}`, + 'Space': t('SETTINGS_SHORTCUT_SPACE'), + 'Ctrl': t('SETTINGS_SHORTCUT_CTRL'), + 'Escape': t('SETTINGS_SHORTCUT_ESC'), + 'ArrowUp': '↑', + 'ArrowDown': '↓', + 'ArrowLeft': '←', + 'ArrowRight': '→', + }), [t]); + + const isRange = useMemo(() => { + return keys.length > 1 && keys.every((key) => !Number.isNaN(parseInt(key))); + }, [keys]); + + const filteredKeys = useMemo(() => { + return isRange ? [keys[0], keys[keys.length - 1]] : keys; + }, [keys, isRange]); + + return ( + filteredKeys.map((key, index) => ( + + + {keyLabelMap[key] ?? key.toUpperCase()} + + { + index < (filteredKeys.length - 1) && ( +
+ { + isRange ? t('SETTINGS_SHORTCUT_TO') : '+' + } +
+ ) + } +
+ )) + ); +}; + +export default Keys; diff --git a/src/components/ShortcutsGroup/Combos/Keys/index.ts b/src/components/ShortcutsGroup/Combos/Keys/index.ts new file mode 100644 index 000000000..ba8d58731 --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/Keys/index.ts @@ -0,0 +1,2 @@ +import Keys from './Keys'; +export default Keys; diff --git a/src/components/ShortcutsGroup/Combos/index.ts b/src/components/ShortcutsGroup/Combos/index.ts new file mode 100644 index 000000000..c66667f91 --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/index.ts @@ -0,0 +1,2 @@ +import Combos from './Combos'; +export default Combos; diff --git a/src/components/ShortcutsGroup/ShortcutsGroup.less b/src/components/ShortcutsGroup/ShortcutsGroup.less new file mode 100644 index 000000000..f0fdd975c --- /dev/null +++ b/src/components/ShortcutsGroup/ShortcutsGroup.less @@ -0,0 +1,44 @@ +.shortcuts-group { + flex: 1 1 0; + position: relative; + min-width: 30rem; + display: flex; + flex-direction: column; + gap: 2rem; + overflow: visible; + + .title { + flex: none; + display: flex; + font-size: 1rem; + font-weight: 400; + color: var(--primary-foreground-color); + opacity: 0.6; + } + + .shortcuts { + position: relative; + display: flex; + flex-direction: column; + gap: 2rem; + overflow: visible; + + .shortcut { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + gap: 2rem; + overflow: visible; + + .label { + position: relative; + font-size: 1rem; + color: var(--primary-foreground-color); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + } + } +} diff --git a/src/components/ShortcutsGroup/ShortcutsGroup.tsx b/src/components/ShortcutsGroup/ShortcutsGroup.tsx new file mode 100644 index 000000000..069d5d1e8 --- /dev/null +++ b/src/components/ShortcutsGroup/ShortcutsGroup.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import classNames from 'classnames'; +import { useTranslation } from 'react-i18next'; +import Combos from './Combos'; +import styles from './ShortcutsGroup.less'; + +type Props = { + className?: string, + label: string, + shortcuts: Shortcut[], +}; + +const ShortcutsGroup = ({ className, label, shortcuts }: Props) => { + const { t } = useTranslation(); + + return ( +
+
+ {t(label)} +
+ +
+ { + shortcuts.map(({ name, label, combos }) => ( +
+
+ {t(label)} +
+ +
+ )) + } +
+
+ ); +}; + +export default ShortcutsGroup; diff --git a/src/components/ShortcutsGroup/index.ts b/src/components/ShortcutsGroup/index.ts new file mode 100644 index 000000000..11f8d0678 --- /dev/null +++ b/src/components/ShortcutsGroup/index.ts @@ -0,0 +1,2 @@ +import ShortcutsGroup from './ShortcutsGroup'; +export default ShortcutsGroup; diff --git a/src/components/index.ts b/src/components/index.ts index a5638007e..a47c2c709 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -25,6 +25,7 @@ import RadioButton from './RadioButton'; import SearchBar from './SearchBar'; import SharePrompt from './SharePrompt'; import Slider from './Slider'; +import ShortcutsGroup from './ShortcutsGroup'; import TextInput from './TextInput'; import Toggle from './Toggle'; import Transition from './Transition'; @@ -59,6 +60,7 @@ export { SearchBar, SharePrompt, Slider, + ShortcutsGroup, TextInput, Toggle, Transition, diff --git a/src/routes/Settings/Shortcuts/Shortcuts.less b/src/routes/Settings/Shortcuts/Shortcuts.less index 40d97987d..186cfa837 100644 --- a/src/routes/Settings/Shortcuts/Shortcuts.less +++ b/src/routes/Settings/Shortcuts/Shortcuts.less @@ -1,27 +1,4 @@ -.shortcut-container { - display: flex; - align-items: center; - justify-content: center; - padding: 0; - overflow: visible; - - kbd { - flex: 0 1 auto; - height: 2.5rem; - min-width: 2.5rem; - line-height: 2.5rem; - padding: 0 1rem; - font-weight: 500; - color: var(--primary-foreground-color); - border-radius: 0.25em; - box-shadow: 0 4px 0 1px var(--modal-background-color); - background-color: var(--overlay-color); - } - - .label { - flex: none; - margin: 0 1rem; - white-space: nowrap; - color: var(--primary-foreground-color); - } +.shortcuts-group { + width: 100%; + margin-bottom: 3rem; } \ No newline at end of file diff --git a/src/routes/Settings/Shortcuts/Shortcuts.tsx b/src/routes/Settings/Shortcuts/Shortcuts.tsx index d852280a6..a0599a503 100644 --- a/src/routes/Settings/Shortcuts/Shortcuts.tsx +++ b/src/routes/Settings/Shortcuts/Shortcuts.tsx @@ -1,97 +1,24 @@ import React, { forwardRef } from 'react'; -import { Section, Option } from '../components'; +import { Section } from '../components'; +import { ShortcutsGroup } from 'stremio/components'; +import { useShortcuts } from 'stremio/common'; import styles from './Shortcuts.less'; -import { useTranslation } from 'react-i18next'; const Shortcuts = forwardRef((_, ref) => { - const { t } = useTranslation(); + const { grouped } = useShortcuts(); return (
- - - - - - - - - - - - - - + { + grouped.map(({ name, label, shortcuts }) => ( + + )) + }
); }); diff --git a/tsconfig.json b/tsconfig.json index d55ac9356..c4b0a8626 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "lib": [ "ES2016", "DOM", "DOM.Iterable"], + "lib": ["ESNext", "DOM", "DOM.Iterable"], "jsx": "react", "baseUrl": "./src", "outDir": "./dist", From 539a7ebc10c8840c34d69f4c667b63da0da9ae8b Mon Sep 17 00:00:00 2001 From: Victor Sales <36749678+v1ctorsales@users.noreply.github.com> Date: Sun, 12 Oct 2025 13:41:42 +0300 Subject: [PATCH 14/32] fix(calendar): align day and more indicator inline in narrow desktop viewports --- src/routes/Calendar/Table/Cell/Cell.less | 69 +++++++++++++++--------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/src/routes/Calendar/Table/Cell/Cell.less b/src/routes/Calendar/Table/Cell/Cell.less index e989b4cf7..d66084392 100644 --- a/src/routes/Calendar/Table/Cell/Cell.less +++ b/src/routes/Calendar/Table/Cell/Cell.less @@ -28,7 +28,7 @@ .heading { position: relative; - width: 60%; + width: 100%; display: flex; align-items: start; padding: 0; @@ -52,7 +52,7 @@ position: relative; display: flex; flex-direction: row; - gap: 1rem; + gap: 0.1em; padding: 0.1rem; width: 90%; @@ -80,13 +80,10 @@ } .poster { - flex: auto; - z-index: 0; - position: relative; - height: 100%; - width: 100%; + height: auto; + max-height: 100%; + aspect-ratio: 2 / 3; object-fit: cover; - opacity: 1; } .icon, .poster { @@ -134,27 +131,19 @@ } } -@media only screen and (orientation: portrait) { +@media @phone-portrait { .cell { flex-direction: column; display: grid; - .heading { - justify-content: center; - } .items { - padding: 1px; - gap: 0.15rem; - justify-content: space-evenly; + padding: 1px; + gap: 0.15rem; + justify-content: space-evenly; - .item{ - width: 80%; - } - } - - .more { - display: flex; - display: none; + .item { + width: 80%; + } } } } @@ -180,4 +169,36 @@ } } } -} \ No newline at end of file +} + +@media only screen and (max-width: @minimum) and (orientation: portrait) and (pointer: fine) { + .cell { + position: relative; + flex-direction: row; + align-items: center; + display: flex; + + .items { + display: none; + } + + .heading { + display: flex; + align-items: center; + gap: 0.25rem; + width: auto; + } + + .day { + margin-right: 0.25rem; + } + + .more { + display: inline-flex; + position: relative; + height: auto; + padding: 0; + width: 30%; + } + } +} From fb9497a85631f28bc4eca0a39a45e52f608d496a Mon Sep 17 00:00:00 2001 From: Victor Sales <36749678+v1ctorsales@users.noreply.github.com> Date: Sun, 12 Oct 2025 18:23:34 +0300 Subject: [PATCH 15/32] feat(calendar): redesign calendar cell layout for responsiveness and banner support --- src/routes/Calendar/Table/Cell/Cell.less | 56 +++++++++++------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/routes/Calendar/Table/Cell/Cell.less b/src/routes/Calendar/Table/Cell/Cell.less index d66084392..0fdf615d2 100644 --- a/src/routes/Calendar/Table/Cell/Cell.less +++ b/src/routes/Calendar/Table/Cell/Cell.less @@ -28,10 +28,9 @@ .heading { position: relative; - width: 100%; + flex: 1 1 40%; display: flex; - align-items: start; - padding: 0; + align-items: flex-start; .day { flex: none; @@ -54,7 +53,10 @@ flex-direction: row; gap: 0.1em; padding: 0.1rem; - width: 90%; + flex: 1 1 60%; + overflow-x: auto; + overflow-y: hidden; + min-width: 0; .item { flex: none; @@ -63,8 +65,9 @@ justify-content: center; height: 100%; aspect-ratio: 2 / 3; - border-radius: var(--border-radius); - max-height: clamp(8rem, 25vh, 14rem); + border-radius: calc(var(--border-radius) /2); + max-height: 100%; + width: 100%; .icon { flex: none; @@ -84,6 +87,7 @@ max-height: 100%; aspect-ratio: 2 / 3; object-fit: cover; + border-radius: inherit } .icon, .poster { @@ -115,7 +119,10 @@ &.today { .heading { .day { + width: auto; background-color: var(--primary-accent-color); + height: auto; + padding: 0.3rem; } } } @@ -142,12 +149,21 @@ justify-content: space-evenly; .item { - width: 80%; + width: 100%; } } } } +@media only screen and (max-height: @xxsmall) and (max-width: @xxsmall) and (orientation: landscape) { + .cell{ + .items{ + width: 100%; + } + } +} + + @media only screen and (max-height: @medium) and (max-width: @medium) and (orientation: landscape) { .cell { gap: 0; @@ -161,12 +177,6 @@ .items { width: 100%; - - .item { - pointer-events: none; - border-radius: calc(var(--border-radius) / 2); - width: 80%; - } } } } @@ -175,30 +185,16 @@ .cell { position: relative; flex-direction: row; - align-items: center; display: flex; - .items { - display: none; - } - .heading { display: flex; - align-items: center; gap: 0.25rem; width: auto; - } + .day{ + font-size: 0.6rem; + } - .day { - margin-right: 0.25rem; - } - - .more { - display: inline-flex; - position: relative; - height: auto; - padding: 0; - width: 30%; } } } From 4860a028c2410bed51e6155b8147de8ceacc1e39 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 13 Oct 2025 11:29:16 +0200 Subject: [PATCH 16/32] chore: update translations --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index c824ad411..61e713041 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "react-i18next": "^15.1.3", "react-is": "18.3.1", "spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", - "stremio-translations": "github:Stremio/stremio-translations#abe7684165a031755e9aee39da26daa806ba7824", + "stremio-translations": "github:Stremio/stremio-translations#5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37", "url": "0.11.4", "use-long-press": "^3.2.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 121d0dbea..110fd7d71 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,8 +90,8 @@ importers: specifier: github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6 version: https://codeload.github.com/Stremio/spatial-navigation/tar.gz/64871b1422466f5f45d24ebc8bbd315b2ebab6a6 stremio-translations: - specifier: github:Stremio/stremio-translations#abe7684165a031755e9aee39da26daa806ba7824 - version: https://codeload.github.com/Stremio/stremio-translations/tar.gz/abe7684165a031755e9aee39da26daa806ba7824 + specifier: github:Stremio/stremio-translations#5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37 + version: https://codeload.github.com/Stremio/stremio-translations/tar.gz/5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37 url: specifier: 0.11.4 version: 0.11.4 @@ -4527,9 +4527,9 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} - stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/abe7684165a031755e9aee39da26daa806ba7824: - resolution: {tarball: https://codeload.github.com/Stremio/stremio-translations/tar.gz/abe7684165a031755e9aee39da26daa806ba7824} - version: 1.44.12 + stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37: + resolution: {tarball: https://codeload.github.com/Stremio/stremio-translations/tar.gz/5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37} + version: 1.44.13 string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} @@ -10283,7 +10283,7 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 - stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/abe7684165a031755e9aee39da26daa806ba7824: {} + stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37: {} string-length@4.0.2: dependencies: From 2e1ad64d02b23b7a508a708acab62616d4e79acd Mon Sep 17 00:00:00 2001 From: Victor Sales <36749678+v1ctorsales@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:06:52 +0300 Subject: [PATCH 17/32] refactor(calendar): replace fixed width with max-width for better banner scaling --- src/routes/Calendar/Table/Cell/Cell.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Calendar/Table/Cell/Cell.less b/src/routes/Calendar/Table/Cell/Cell.less index 0fdf615d2..95116de20 100644 --- a/src/routes/Calendar/Table/Cell/Cell.less +++ b/src/routes/Calendar/Table/Cell/Cell.less @@ -51,7 +51,7 @@ position: relative; display: flex; flex-direction: row; - gap: 0.1em; + gap: 0.2rem; padding: 0.1rem; flex: 1 1 60%; overflow-x: auto; @@ -67,7 +67,7 @@ aspect-ratio: 2 / 3; border-radius: calc(var(--border-radius) /2); max-height: 100%; - width: 100%; + max-width: 100%; .icon { flex: none; From e74072ebd5c2e548589630022d2dae156d3cebaf Mon Sep 17 00:00:00 2001 From: Victor Sales <36749678+v1ctorsales@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:40:13 +0300 Subject: [PATCH 18/32] fix(calendar): disable banner click in phone-portrait mode --- src/routes/Calendar/Table/Cell/Cell.less | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/routes/Calendar/Table/Cell/Cell.less b/src/routes/Calendar/Table/Cell/Cell.less index 95116de20..3a103c913 100644 --- a/src/routes/Calendar/Table/Cell/Cell.less +++ b/src/routes/Calendar/Table/Cell/Cell.less @@ -138,6 +138,17 @@ } } +@media only screen and (max-width: @minimum) and (orientation: portrait) { + .cell{ + .items{ + .item{ + pointer-events: none; + } + + } + } +} + @media @phone-portrait { .cell { flex-direction: column; @@ -150,6 +161,7 @@ .item { width: 100%; + pointer-events: none; } } } @@ -187,6 +199,12 @@ flex-direction: row; display: flex; + .items{ + .item{ + pointer-events: none; + } + } + .heading { display: flex; gap: 0.25rem; From a97dd01869f0342f0449b4d40278ea2b7a4a70e4 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 13 Oct 2025 12:55:12 +0200 Subject: [PATCH 19/32] refactor(shortcuts): use Ctrl + / for shortcuts modal --- src/common/Shortcuts/shortcuts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/Shortcuts/shortcuts.ts b/src/common/Shortcuts/shortcuts.ts index d918733af..614495f0a 100644 --- a/src/common/Shortcuts/shortcuts.ts +++ b/src/common/Shortcuts/shortcuts.ts @@ -26,7 +26,7 @@ const shortcuts: ShortcutGroup[] = [ { name: 'shortcuts', label: 'SETTINGS_SHORTCUT_SHORTCUTS', - combos: [['Ctrl', '?']], + combos: [['Ctrl', '/']], }, ] }, From d2d28be6ded2956837e6167854d99fd78f5cff8d Mon Sep 17 00:00:00 2001 From: Victor Sales <36749678+v1ctorsales@users.noreply.github.com> Date: Mon, 13 Oct 2025 16:27:15 +0300 Subject: [PATCH 20/32] style(responsive): add @phone-landscape media query --- src/routes/Calendar/Table/Cell/Cell.less | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/routes/Calendar/Table/Cell/Cell.less b/src/routes/Calendar/Table/Cell/Cell.less index 3a103c913..754715f32 100644 --- a/src/routes/Calendar/Table/Cell/Cell.less +++ b/src/routes/Calendar/Table/Cell/Cell.less @@ -167,12 +167,19 @@ } } -@media only screen and (max-height: @xxsmall) and (max-width: @xxsmall) and (orientation: landscape) { - .cell{ - .items{ - width: 100%; +@media @phone-landscape { + .cell { + flex-direction: row; + + .items { + padding: 1px; + gap: 0.15rem; + + .item { + pointer-events: none; + } + } } - } } From 0143bf914c1e343894d3d5f09d612b710445d1a9 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 13 Oct 2025 15:13:21 +0200 Subject: [PATCH 21/32] feat: add video mode setting --- src/routes/Player/Player.js | 1 + src/routes/Settings/Player/Player.tsx | 12 +++++++ .../Settings/Player/usePlayerOptions.ts | 33 +++++++++++++++++++ src/types/models/Ctx.d.ts | 1 + 4 files changed, 47 insertions(+) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index eab38bd96..4231418c6 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -345,6 +345,7 @@ const Player = ({ urlParams, queryParams }) => { forceTranscoding: forceTranscoding || casting, maxAudioChannels: settings.surroundSound ? 32 : 2, hardwareDecoding: settings.hardwareDecoding, + videoMode: settings.videoMode, streamingServerURL: streamingServer.baseUrl ? casting ? streamingServer.baseUrl diff --git a/src/routes/Settings/Player/Player.tsx b/src/routes/Settings/Player/Player.tsx index 72a941e81..29b98d650 100644 --- a/src/routes/Settings/Player/Player.tsx +++ b/src/routes/Settings/Player/Player.tsx @@ -3,6 +3,7 @@ import { ColorInput, MultiselectMenu, Toggle } from 'stremio/components'; import { useServices } from 'stremio/services'; import { Category, Option, Section } from '../components'; import usePlayerOptions from './usePlayerOptions'; +import { usePlatform } from 'stremio/common'; type Props = { profile: Profile, @@ -10,6 +11,7 @@ type Props = { const Player = forwardRef(({ profile }: Props, ref) => { const { shell } = useServices(); + const platform = usePlatform(); const { subtitlesLanguageSelect, @@ -26,6 +28,7 @@ const Player = forwardRef(({ profile }: Props, ref) => { bingeWatchingToggle, playInBackgroundToggle, hardwareDecodingToggle, + videoModeSelect, pauseOnMinimizeToggle, } = usePlayerOptions(profile); @@ -129,6 +132,15 @@ const Player = forwardRef(({ profile }: Props, ref) => { /> } + { + shell.active && platform.name === 'windows' && + + } { shell.active &&