diff --git a/commafeed-client/src/components/RichCodeEditor.tsx b/commafeed-client/src/components/RichCodeEditor.tsx
new file mode 100644
index 00000000..65e784b0
--- /dev/null
+++ b/commafeed-client/src/components/RichCodeEditor.tsx
@@ -0,0 +1,27 @@
+import { useMantineTheme } from "@mantine/core"
+import { Editor } from "@monaco-editor/react"
+
+interface RichCodeEditorProps {
+ height: number | string
+ language: "css" | "javascript"
+ value: string
+ onChange: (value: string | undefined) => void
+}
+
+function RichCodeEditor(props: RichCodeEditorProps) {
+ const theme = useMantineTheme()
+ const editorTheme = theme.colorScheme === "dark" ? "vs-dark" : "light"
+
+ return (
+
+ )
+}
+
+export default RichCodeEditor
diff --git a/commafeed-client/src/components/settings/CustomCodeSettings.tsx b/commafeed-client/src/components/settings/CustomCodeSettings.tsx
index 31f2b63d..8c6722ce 100644
--- a/commafeed-client/src/components/settings/CustomCodeSettings.tsx
+++ b/commafeed-client/src/components/settings/CustomCodeSettings.tsx
@@ -1,94 +1,79 @@
-import { Trans } from "@lingui/macro"
-import { Box, Button, Group, Input, Stack, useMantineTheme } from "@mantine/core"
-import { useForm } from "@mantine/form"
-import { Editor } from "@monaco-editor/react"
-import { client, errorToStrings } from "app/client"
-import { redirectToSelectedSource } from "app/slices/redirect"
-import { useAppDispatch, useAppSelector } from "app/store"
-import { Alert } from "components/Alert"
-import { useEffect } from "react"
-import { useAsyncCallback } from "react-async-hook"
-import { TbDeviceFloppy } from "react-icons/tb"
-
-interface FormData {
- customCss: string
- customJs: string
-}
-
-export function CustomCodeSettings() {
- const settings = useAppSelector(state => state.user.settings)
- const theme = useMantineTheme()
- const dispatch = useAppDispatch()
-
- const editorTheme = theme.colorScheme === "dark" ? "vs-dark" : "light"
-
- const form = useForm()
- const { setValues } = form
-
- const saveCustomCode = useAsyncCallback(
- async (d: FormData) => {
- if (!settings) return
- await client.user.saveSettings({
- ...settings,
- customCss: d.customCss,
- customJs: d.customJs,
- })
- },
- {
- onSuccess: () => {
- window.location.reload()
- },
- }
- )
-
- useEffect(() => {
- if (!settings) return
- setValues({
- customCss: settings.customCss,
- customJs: settings.customJs,
- })
- }, [setValues, settings])
-
- return (
- <>
- {saveCustomCode.error && (
-
-
-
- )}
-
-
- >
- )
-}
+import { Trans } from "@lingui/macro"
+import { Box, Button, Group, Input, Stack } from "@mantine/core"
+import { useForm } from "@mantine/form"
+import { client, errorToStrings } from "app/client"
+import { redirectToSelectedSource } from "app/slices/redirect"
+import { useAppDispatch, useAppSelector } from "app/store"
+import { Alert } from "components/Alert"
+import RichCodeEditor from "components/RichCodeEditor"
+import { useEffect } from "react"
+import { useAsyncCallback } from "react-async-hook"
+import { TbDeviceFloppy } from "react-icons/tb"
+
+interface FormData {
+ customCss: string
+ customJs: string
+}
+
+export function CustomCodeSettings() {
+ const settings = useAppSelector(state => state.user.settings)
+ const dispatch = useAppDispatch()
+
+ const form = useForm()
+ const { setValues } = form
+
+ const saveCustomCode = useAsyncCallback(
+ async (d: FormData) => {
+ if (!settings) return
+ await client.user.saveSettings({
+ ...settings,
+ customCss: d.customCss,
+ customJs: d.customJs,
+ })
+ },
+ {
+ onSuccess: () => {
+ window.location.reload()
+ },
+ }
+ )
+
+ useEffect(() => {
+ if (!settings) return
+ setValues({
+ customCss: settings.customCss,
+ customJs: settings.customJs,
+ })
+ }, [setValues, settings])
+
+ return (
+ <>
+ {saveCustomCode.error && (
+
+
+
+ )}
+
+
+ >
+ )
+}