From b742c6a768cf68e615a1af55990f9610fe56e61d Mon Sep 17 00:00:00 2001 From: David Ralph Date: Fri, 30 Jan 2026 23:39:05 +0000 Subject: [PATCH] feat: custom iframe widgets (close #656) --- src/App.jsx | 2 + src/features/misc/CenteredCustomWidgets.jsx | 58 ++++ src/features/misc/CustomWidgets.jsx | 58 ++++ .../misc/components/AddWidgetModal.jsx | 100 +++++++ src/features/misc/components/DragHandle.jsx | 7 + .../misc/components/SortableWidgetItem.jsx | 64 +++++ .../misc/components/SortableWidgetList.jsx | 38 +++ src/features/misc/components/index.jsx | 4 + src/features/misc/customwidgets.scss | 264 ++++++++++++++++++ src/features/misc/sections/Advanced.jsx | 252 ++++++++++++++++- src/features/misc/sections/Language.jsx | 2 +- src/features/misc/views/Widgets.jsx | 2 + 12 files changed, 839 insertions(+), 12 deletions(-) create mode 100644 src/features/misc/CenteredCustomWidgets.jsx create mode 100644 src/features/misc/CustomWidgets.jsx create mode 100644 src/features/misc/components/AddWidgetModal.jsx create mode 100644 src/features/misc/components/DragHandle.jsx create mode 100644 src/features/misc/components/SortableWidgetItem.jsx create mode 100644 src/features/misc/components/SortableWidgetList.jsx create mode 100644 src/features/misc/components/index.jsx create mode 100644 src/features/misc/customwidgets.scss diff --git a/src/App.jsx b/src/App.jsx index 157918e1..61b260b2 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -3,6 +3,7 @@ import { ToastContainer } from 'react-toastify'; import Background from 'features/background/Background'; import Widgets from 'features/misc/views/Widgets'; import Modals from 'features/misc/modals/Modals'; +import CustomWidgets from 'features/misc/CustomWidgets'; import { loadSettings, moveSettings } from 'utils/settings'; import EventBus from 'utils/eventbus'; import variables from 'config/variables'; @@ -60,6 +61,7 @@ const App = () => { return ( {showBackground && } + { + const [widgets, setWidgets] = useState([]); + + useEffect(() => { + const loadWidgets = () => { + const stored = JSON.parse(localStorage.getItem('customWidgets') || '[]'); + setWidgets(stored); + }; + + loadWidgets(); + + const handleRefresh = () => { + loadWidgets(); + }; + + EventBus.on('refresh-widgets', handleRefresh); + window.addEventListener('storage', handleRefresh); + + return () => { + EventBus.off('refresh-widgets', handleRefresh); + window.removeEventListener('storage', handleRefresh); + }; + }, []); + + // Only render center-positioned widgets + const centerWidgets = widgets.filter((w) => w.position === 'center'); + + if (centerWidgets.length === 0) { + return null; + } + + return ( + <> + {centerWidgets.map((widget) => ( +
+