Add custom about page and TOS
This commit is contained in:
30
src/App.tsx
30
src/App.tsx
@@ -1,5 +1,9 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { ThemeProvider } from 'styled-components';
|
||||||
|
import About from './components/About.tsx';
|
||||||
import Editor from './components/Editor';
|
import Editor from './components/Editor';
|
||||||
|
import usePreference from './hooks/usePreference.ts';
|
||||||
|
import themes, { Themes } from './style/themes.ts';
|
||||||
import { loadFromBytebin } from './util/storage';
|
import { loadFromBytebin } from './util/storage';
|
||||||
|
|
||||||
const INITIAL = Symbol();
|
const INITIAL = Symbol();
|
||||||
@@ -14,6 +18,12 @@ export default function App() {
|
|||||||
const [forcedContent, setForcedContent] = useState<string>('');
|
const [forcedContent, setForcedContent] = useState<string>('');
|
||||||
const [actualContent, setActualContent] = useState<string>('');
|
const [actualContent, setActualContent] = useState<string>('');
|
||||||
const [contentType, setContentType] = useState<string>();
|
const [contentType, setContentType] = useState<string>();
|
||||||
|
const [theme, setTheme] = usePreference<keyof Themes>(
|
||||||
|
'theme',
|
||||||
|
'dark',
|
||||||
|
pref => !!themes[pref]
|
||||||
|
);
|
||||||
|
const [showAbout, setShowAbout] = useState<boolean>(true);
|
||||||
|
|
||||||
function setContent(content: string) {
|
function setContent(content: string) {
|
||||||
setActualContent(content);
|
setActualContent(content);
|
||||||
@@ -40,13 +50,19 @@ export default function App() {
|
|||||||
}, [pasteId, state]);
|
}, [pasteId, state]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Editor
|
<ThemeProvider theme={themes[theme]}>
|
||||||
forcedContent={forcedContent}
|
<Editor
|
||||||
actualContent={actualContent}
|
forcedContent={forcedContent}
|
||||||
setActualContent={setActualContent}
|
actualContent={actualContent}
|
||||||
contentType={contentType}
|
setActualContent={setActualContent}
|
||||||
pasteId={pasteId}
|
contentType={contentType}
|
||||||
/>
|
pasteId={pasteId}
|
||||||
|
theme={theme}
|
||||||
|
setTheme={setTheme}
|
||||||
|
setShowAbout={setShowAbout}
|
||||||
|
/>
|
||||||
|
{showAbout && <About setVisible={setShowAbout} />}
|
||||||
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
190
src/components/About.tsx
Normal file
190
src/components/About.tsx
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import Button from './Button.tsx';
|
||||||
|
|
||||||
|
const CloseButton = ({
|
||||||
|
setVisible,
|
||||||
|
}: {
|
||||||
|
setVisible: (show: boolean) => void;
|
||||||
|
}) => {
|
||||||
|
const close = useCallback(() => {
|
||||||
|
setVisible(false);
|
||||||
|
}, [setVisible]);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '5px',
|
||||||
|
right: '5px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button style={{ padding: '5px' }} onClick={close}>
|
||||||
|
[X]
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function About({
|
||||||
|
setVisible,
|
||||||
|
}: {
|
||||||
|
setVisible: (show: boolean) => void;
|
||||||
|
}) {
|
||||||
|
const [showTos, setShowTos] = useState<boolean>(false);
|
||||||
|
|
||||||
|
if (showTos) {
|
||||||
|
return <Tos setVisible={setShowTos} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AboutPanel>
|
||||||
|
<CloseButton setVisible={setVisible} />
|
||||||
|
<BannerContainer>
|
||||||
|
<Banner>{'{paste}'}</Banner>
|
||||||
|
</BannerContainer>
|
||||||
|
<p>
|
||||||
|
<b>paste is a simple web app for writing & sharing code</b>. It's a
|
||||||
|
different take on conventional pastebin sites like pastebin.com or
|
||||||
|
hastebin.
|
||||||
|
</p>
|
||||||
|
{window.location.hostname === 'pastes.dev' && (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
<b>pastes.dev</b> is the official, publicly accessible paste
|
||||||
|
instance, and can be used by anyone, subject to the{' '}
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={e => {
|
||||||
|
setShowTos(true);
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Terms of Service
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
To access pastes.dev programmatically, please use the{' '}
|
||||||
|
<a href="https://github.com/lucko/paste#readme" target="_blank">
|
||||||
|
API
|
||||||
|
</a>
|
||||||
|
. :)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Please{' '}
|
||||||
|
<b>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={e => {
|
||||||
|
setShowTos(true);
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
report
|
||||||
|
</a>
|
||||||
|
</b>{' '}
|
||||||
|
illegal, malicious, or abusive content so it can be removed.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<p style={{ textAlign: 'center' }}>
|
||||||
|
<a href="https://github.com/lucko/paste" target="_blank">
|
||||||
|
paste
|
||||||
|
</a>{' '}
|
||||||
|
is free & open source on GitHub.
|
||||||
|
<br />
|
||||||
|
Copyright © 2021-{new Date().getFullYear()}{' '}
|
||||||
|
<a href="https://github.com/lucko" target="_blank">
|
||||||
|
lucko
|
||||||
|
</a>{' '}
|
||||||
|
& other paste{' '}
|
||||||
|
<a
|
||||||
|
href="https://github.com/lucko/paste/graphs/contributors"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
contributors
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</AboutPanel>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Tos = ({ setVisible }: { setVisible: (show: boolean) => void }) => {
|
||||||
|
return (
|
||||||
|
<AboutPanel>
|
||||||
|
<CloseButton setVisible={setVisible} />
|
||||||
|
<h1>Terms of Service</h1>
|
||||||
|
<p>
|
||||||
|
Welcome to pastes.dev. By using this service, you agree to the following
|
||||||
|
terms:
|
||||||
|
</p>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<b>No Illegal Use:</b> You may not use pastes.dev to share, store, or
|
||||||
|
distribute any content that is illegal, harmful, or violates any laws
|
||||||
|
or regulations.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>No Malicious Content:</b> Do not upload or share content intended
|
||||||
|
to harm others, including but not limited to malware, phishing links,
|
||||||
|
or personal data without consent.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Content Responsibility:</b> You are solely responsible for the
|
||||||
|
content you post. We do not review content and are not liable for what
|
||||||
|
users choose to share.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Moderation:</b> We reserve the right to remove any content at our
|
||||||
|
discretion, and to restrict or terminate access to the service for
|
||||||
|
abuse or violations of these terms.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>No Guarantees:</b> This service is provided "as is" with no
|
||||||
|
warranties. We do not guarantee uptime, data retention, or
|
||||||
|
availability.
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p>
|
||||||
|
By using pastes.dev, you accept these terms. If you do not agree, please
|
||||||
|
do not use the service.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Reporting Abuse</b>
|
||||||
|
<br />
|
||||||
|
If you encounter illegal or malicious content, please report it by email
|
||||||
|
to report-abuse {'<at>'} pastes.dev.
|
||||||
|
</p>
|
||||||
|
</AboutPanel>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const AboutPanel = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 99;
|
||||||
|
max-width: 650px;
|
||||||
|
|
||||||
|
color: ${props => props.theme.primary};
|
||||||
|
background-color: ${props => props.theme.secondary};
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const BannerContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Banner = styled.div`
|
||||||
|
background-color: ${props => props.theme.background};
|
||||||
|
color: ${props => props.theme.logo};
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 70px;
|
||||||
|
letter-spacing: -5px;
|
||||||
|
padding: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
`;
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { isMobile } from 'react-device-detect';
|
import { isMobile } from 'react-device-detect';
|
||||||
import { ThemeProvider } from 'styled-components';
|
|
||||||
|
|
||||||
import usePreference from '../hooks/usePreference';
|
import usePreference from '../hooks/usePreference';
|
||||||
import themes, { Themes } from '../style/themes';
|
import { Themes } from '../style/themes.ts';
|
||||||
import EditorControls from './EditorControls';
|
import EditorControls from './EditorControls';
|
||||||
import EditorGlobalStyle from './EditorGlobalStyle';
|
import EditorGlobalStyle from './EditorGlobalStyle';
|
||||||
import EditorTextArea from './EditorTextArea';
|
import EditorTextArea from './EditorTextArea';
|
||||||
@@ -14,6 +13,9 @@ export interface EditorProps {
|
|||||||
setActualContent: (value: string) => void;
|
setActualContent: (value: string) => void;
|
||||||
contentType?: string;
|
contentType?: string;
|
||||||
pasteId?: string;
|
pasteId?: string;
|
||||||
|
theme: keyof Themes;
|
||||||
|
setTheme: (value: keyof Themes) => void;
|
||||||
|
setShowAbout: (value: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ResetFunction = () => void;
|
export type ResetFunction = () => void;
|
||||||
@@ -24,16 +26,14 @@ export default function Editor({
|
|||||||
setActualContent,
|
setActualContent,
|
||||||
contentType,
|
contentType,
|
||||||
pasteId,
|
pasteId,
|
||||||
|
theme,
|
||||||
|
setTheme,
|
||||||
|
setShowAbout,
|
||||||
}: EditorProps) {
|
}: EditorProps) {
|
||||||
const [language, setLanguage] = useState<string>('plain');
|
const [language, setLanguage] = useState<string>('plain');
|
||||||
const [readOnly, setReadOnly] = useState<boolean>(isMobile && !!pasteId);
|
const [readOnly, setReadOnly] = useState<boolean>(isMobile && !!pasteId);
|
||||||
const resetFunction = useRef<ResetFunction>(null);
|
const resetFunction = useRef<ResetFunction>(null);
|
||||||
|
|
||||||
const [theme, setTheme] = usePreference<keyof Themes>(
|
|
||||||
'theme',
|
|
||||||
'dark',
|
|
||||||
pref => !!themes[pref]
|
|
||||||
);
|
|
||||||
const [fontSize, setFontSize, fontSizeCheck] = usePreference<number>(
|
const [fontSize, setFontSize, fontSizeCheck] = usePreference<number>(
|
||||||
'fontsize',
|
'fontsize',
|
||||||
16,
|
16,
|
||||||
@@ -61,33 +61,31 @@ export default function Editor({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ThemeProvider theme={themes[theme]}>
|
<EditorGlobalStyle />
|
||||||
<EditorGlobalStyle />
|
<EditorControls
|
||||||
<EditorControls
|
actualContent={actualContent}
|
||||||
actualContent={actualContent}
|
resetFunction={resetFunction}
|
||||||
resetFunction={resetFunction}
|
language={language}
|
||||||
language={language}
|
setLanguage={setLanguage}
|
||||||
setLanguage={setLanguage}
|
readOnly={readOnly}
|
||||||
readOnly={readOnly}
|
setReadOnly={setReadOnly}
|
||||||
setReadOnly={setReadOnly}
|
theme={theme}
|
||||||
theme={theme}
|
setTheme={setTheme}
|
||||||
setTheme={setTheme}
|
wordWrap={wordWrap}
|
||||||
wordWrap={wordWrap}
|
setWordWrap={setWordWrap}
|
||||||
setWordWrap={setWordWrap}
|
zoom={zoom}
|
||||||
zoom={zoom}
|
setShowAbout={setShowAbout}
|
||||||
/>
|
/>
|
||||||
<EditorTextArea
|
<EditorTextArea
|
||||||
forcedContent={forcedContent}
|
forcedContent={forcedContent}
|
||||||
actualContent={actualContent}
|
actualContent={actualContent}
|
||||||
setActualContent={setActualContent}
|
setActualContent={setActualContent}
|
||||||
theme={themes[theme]}
|
language={language}
|
||||||
language={language}
|
fontSize={fontSize}
|
||||||
fontSize={fontSize}
|
readOnly={readOnly}
|
||||||
readOnly={readOnly}
|
wordWrap={wordWrap}
|
||||||
wordWrap={wordWrap}
|
resetFunction={resetFunction}
|
||||||
resetFunction={resetFunction}
|
/>
|
||||||
/>
|
|
||||||
</ThemeProvider>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export interface EditorControlsProps {
|
|||||||
wordWrap: boolean;
|
wordWrap: boolean;
|
||||||
setWordWrap: (value: boolean) => void;
|
setWordWrap: (value: boolean) => void;
|
||||||
zoom: (delta: number) => void;
|
zoom: (delta: number) => void;
|
||||||
|
setShowAbout: (value: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EditorControls({
|
export default function EditorControls({
|
||||||
@@ -36,6 +37,7 @@ export default function EditorControls({
|
|||||||
wordWrap,
|
wordWrap,
|
||||||
setWordWrap,
|
setWordWrap,
|
||||||
zoom,
|
zoom,
|
||||||
|
setShowAbout,
|
||||||
}: EditorControlsProps) {
|
}: EditorControlsProps) {
|
||||||
const [saving, setSaving] = useState<boolean>(false);
|
const [saving, setSaving] = useState<boolean>(false);
|
||||||
const [recentlySaved, setRecentlySaved] = useState<boolean>(false);
|
const [recentlySaved, setRecentlySaved] = useState<boolean>(false);
|
||||||
@@ -44,6 +46,10 @@ export default function EditorControls({
|
|||||||
setRecentlySaved(false);
|
setRecentlySaved(false);
|
||||||
}, [actualContent, language]);
|
}, [actualContent, language]);
|
||||||
|
|
||||||
|
const showAbout = useCallback(() => {
|
||||||
|
setShowAbout(true);
|
||||||
|
}, [setShowAbout]);
|
||||||
|
|
||||||
const save = useCallback(() => {
|
const save = useCallback(() => {
|
||||||
if (!actualContent || recentlySaved) {
|
if (!actualContent || recentlySaved) {
|
||||||
return;
|
return;
|
||||||
@@ -126,15 +132,7 @@ export default function EditorControls({
|
|||||||
setValue={setTheme}
|
setValue={setTheme}
|
||||||
ids={Object.keys(themes) as (keyof Themes)[]}
|
ids={Object.keys(themes) as (keyof Themes)[]}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button onClick={showAbout}>[about]</Button>
|
||||||
className="optional"
|
|
||||||
as="a"
|
|
||||||
href="https://github.com/lucko/paste#readme"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
[about]
|
|
||||||
</Button>
|
|
||||||
</Section>
|
</Section>
|
||||||
</Header>
|
</Header>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import styled from 'styled-components';
|
import styled, { useTheme } from 'styled-components';
|
||||||
import themes, { Theme } from '../style/themes';
|
import themes, { Theme } from '../style/themes';
|
||||||
|
|
||||||
import type { editor } from 'monaco-editor';
|
import type { editor } from 'monaco-editor';
|
||||||
@@ -58,7 +58,6 @@ export interface EditorTextAreaProps {
|
|||||||
forcedContent: string;
|
forcedContent: string;
|
||||||
actualContent: string;
|
actualContent: string;
|
||||||
setActualContent: (value: string) => void;
|
setActualContent: (value: string) => void;
|
||||||
theme: Theme;
|
|
||||||
language: string;
|
language: string;
|
||||||
fontSize: number;
|
fontSize: number;
|
||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
@@ -70,7 +69,6 @@ export default function EditorTextArea({
|
|||||||
forcedContent,
|
forcedContent,
|
||||||
actualContent,
|
actualContent,
|
||||||
setActualContent,
|
setActualContent,
|
||||||
theme,
|
|
||||||
language,
|
language,
|
||||||
fontSize,
|
fontSize,
|
||||||
readOnly,
|
readOnly,
|
||||||
@@ -81,6 +79,7 @@ export default function EditorTextArea({
|
|||||||
const [monaco, setMonaco] = useState<Monaco>();
|
const [monaco, setMonaco] = useState<Monaco>();
|
||||||
const [selected, toggleSelected] = useSelectedLine();
|
const [selected, toggleSelected] = useSelectedLine();
|
||||||
const editorAreaRef = useRef<HTMLDivElement>(null);
|
const editorAreaRef = useRef<HTMLDivElement>(null);
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
useLineNumberMagic(
|
useLineNumberMagic(
|
||||||
editorAreaRef,
|
editorAreaRef,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export interface Theme {
|
|||||||
secondary: Color;
|
secondary: Color;
|
||||||
highlight: Color;
|
highlight: Color;
|
||||||
background: Color;
|
background: Color;
|
||||||
|
logo: Color;
|
||||||
lightOrDark: 'light' | 'dark';
|
lightOrDark: 'light' | 'dark';
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
color: Color;
|
color: Color;
|
||||||
@@ -43,6 +44,7 @@ const themes: Themes = {
|
|||||||
secondary: '#010409', // canvas.inset
|
secondary: '#010409', // canvas.inset
|
||||||
highlight: '#161b22', // canvas.overlay
|
highlight: '#161b22', // canvas.overlay
|
||||||
background: '#0d1117', // canvas.default
|
background: '#0d1117', // canvas.default
|
||||||
|
logo: '#d2a8ff',
|
||||||
lightOrDark: 'dark',
|
lightOrDark: 'dark',
|
||||||
|
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
@@ -85,6 +87,7 @@ const themes: Themes = {
|
|||||||
secondary: '#022550',
|
secondary: '#022550',
|
||||||
highlight: '#36368c',
|
highlight: '#36368c',
|
||||||
background: '#ffffff',
|
background: '#ffffff',
|
||||||
|
logo: '#022550',
|
||||||
lightOrDark: 'light',
|
lightOrDark: 'light',
|
||||||
|
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
@@ -127,6 +130,7 @@ const themes: Themes = {
|
|||||||
secondary: '#383a59',
|
secondary: '#383a59',
|
||||||
highlight: '#44475a',
|
highlight: '#44475a',
|
||||||
background: '#282a36',
|
background: '#282a36',
|
||||||
|
logo: '#BD93F9', // purple
|
||||||
lightOrDark: 'dark',
|
lightOrDark: 'dark',
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
color: '#586e75',
|
color: '#586e75',
|
||||||
@@ -149,6 +153,7 @@ const themes: Themes = {
|
|||||||
secondary: '#222218',
|
secondary: '#222218',
|
||||||
highlight: '#49483E',
|
highlight: '#49483E',
|
||||||
background: '#272822',
|
background: '#272822',
|
||||||
|
logo: '#f92672', // red
|
||||||
lightOrDark: 'dark',
|
lightOrDark: 'dark',
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
color: '#49483E',
|
color: '#49483E',
|
||||||
@@ -171,6 +176,7 @@ const themes: Themes = {
|
|||||||
secondary: '#073642', // base02
|
secondary: '#073642', // base02
|
||||||
highlight: '#002b36', // base03
|
highlight: '#002b36', // base03
|
||||||
background: '#002B36', // base03
|
background: '#002B36', // base03
|
||||||
|
logo: '#dc322f', // red
|
||||||
lightOrDark: 'dark',
|
lightOrDark: 'dark',
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
color: '#93a1a1', // base1
|
color: '#93a1a1', // base1
|
||||||
@@ -193,6 +199,7 @@ const themes: Themes = {
|
|||||||
secondary: '#eee8d5', // base2
|
secondary: '#eee8d5', // base2
|
||||||
highlight: '#FDF6E3', // base3
|
highlight: '#FDF6E3', // base3
|
||||||
background: '#FDF6E3', // base3
|
background: '#FDF6E3', // base3
|
||||||
|
logo: '#dc322f', // red
|
||||||
lightOrDark: 'light',
|
lightOrDark: 'light',
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
color: '#586e75', // base01
|
color: '#586e75', // base01
|
||||||
@@ -380,6 +387,7 @@ export function createCatppuccinTheme(flavor: CatppuccinFlavor): Theme {
|
|||||||
color: color(flavor.colors.rosewater),
|
color: color(flavor.colors.rosewater),
|
||||||
backgroundColor: color(flavor.colors.surface2),
|
backgroundColor: color(flavor.colors.surface2),
|
||||||
},
|
},
|
||||||
|
logo: color(flavor.colors.mauve),
|
||||||
editor: editorTheme,
|
editor: editorTheme,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user