feat: add markdown editor for page description
This commit is contained in:
parent
c950e4a3f8
commit
7e1faf8258
715
pnpm-lock.yaml
715
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
16
src/client/components/MarkdownEditor/editor.tsx
Normal file
16
src/client/components/MarkdownEditor/editor.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { Editor, EditorProps } from '@bytemd/react';
|
||||
import { plugins } from './plugins';
|
||||
import 'bytemd/dist/index.css';
|
||||
import './style.less';
|
||||
import { useLocale } from './useLocale';
|
||||
|
||||
interface MarkdownEditorProps extends EditorProps {}
|
||||
export const MarkdownEditor: React.FC<MarkdownEditorProps> = React.memo(
|
||||
(props) => {
|
||||
const locale = useLocale();
|
||||
|
||||
return <Editor plugins={plugins} locale={locale} {...props} />;
|
||||
}
|
||||
);
|
||||
MarkdownEditor.displayName = 'MarkdownEditor';
|
9
src/client/components/MarkdownEditor/index.tsx
Normal file
9
src/client/components/MarkdownEditor/index.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import loadable from '@loadable/component';
|
||||
|
||||
export const MarkdownEditor = loadable(() =>
|
||||
import('./editor').then((module) => module.MarkdownEditor)
|
||||
);
|
||||
|
||||
export const MarkdownViewer = loadable(() =>
|
||||
import('./viewer').then((module) => module.MarkdownViewer)
|
||||
);
|
6
src/client/components/MarkdownEditor/plugins.ts
Normal file
6
src/client/components/MarkdownEditor/plugins.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import gfm from '@bytemd/plugin-gfm';
|
||||
|
||||
export const plugins = [
|
||||
gfm(),
|
||||
// Add more plugins here
|
||||
];
|
42
src/client/components/MarkdownEditor/style.less
Normal file
42
src/client/components/MarkdownEditor/style.less
Normal file
@ -0,0 +1,42 @@
|
||||
.bytemd .bytemd-toolbar-right [bytemd-tippy-path='5'] {
|
||||
// Hidden github icon
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bytemd-fullscreen.bytemd {
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.dark {
|
||||
.bytemd {
|
||||
@apply bg-zinc-900 border-zinc-800 text-foreground;
|
||||
|
||||
.bytemd-toolbar {
|
||||
@apply bg-zinc-900 border-zinc-800;
|
||||
|
||||
.bytemd-toolbar-icon:hover {
|
||||
@apply bg-muted;
|
||||
}
|
||||
}
|
||||
|
||||
.bytemd-preview {
|
||||
@apply border-zinc-800;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
@apply bg-zinc-900 text-foreground;
|
||||
|
||||
.CodeMirror-cursor {
|
||||
@apply border-foreground;
|
||||
}
|
||||
|
||||
.CodeMirror-selected {
|
||||
@apply bg-zinc-800;
|
||||
}
|
||||
}
|
||||
|
||||
.bytemd-status {
|
||||
@apply border-zinc-800;
|
||||
}
|
||||
}
|
||||
}
|
54
src/client/components/MarkdownEditor/useLocale.ts
Normal file
54
src/client/components/MarkdownEditor/useLocale.ts
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* TODO: wait for i18n with namespace
|
||||
*/
|
||||
export function useLocale() {
|
||||
return {
|
||||
bold: 'Bold',
|
||||
boldText: 'bold text',
|
||||
cheatsheet: 'Markdown Cheatsheet',
|
||||
closeHelp: 'Close help',
|
||||
closeToc: 'Close table of contents',
|
||||
code: 'Code',
|
||||
codeBlock: 'Code block',
|
||||
codeLang: 'lang',
|
||||
codeText: 'code',
|
||||
exitFullscreen: 'Exit fullscreen',
|
||||
exitPreviewOnly: 'Exit preview only',
|
||||
exitWriteOnly: 'Exit write only',
|
||||
fullscreen: 'Fullscreen',
|
||||
h1: 'Heading 1',
|
||||
h2: 'Heading 2',
|
||||
h3: 'Heading 3',
|
||||
h4: 'Heading 4',
|
||||
h5: 'Heading 5',
|
||||
h6: 'Heading 6',
|
||||
headingText: 'heading',
|
||||
help: 'Help',
|
||||
hr: 'Horizontal rule',
|
||||
image: 'Image',
|
||||
imageAlt: 'alt',
|
||||
imageTitle: 'title',
|
||||
italic: 'Italic',
|
||||
italicText: 'italic text',
|
||||
limited: 'The maximum character limit has been reached',
|
||||
lines: 'Lines',
|
||||
link: 'Link',
|
||||
linkText: 'link text',
|
||||
ol: 'Ordered list',
|
||||
olItem: 'item',
|
||||
preview: 'Preview',
|
||||
previewOnly: 'Preview only',
|
||||
quote: 'Quote',
|
||||
quotedText: 'quoted text',
|
||||
shortcuts: 'Shortcuts',
|
||||
source: 'Source code',
|
||||
sync: 'Scroll sync',
|
||||
toc: 'Table of contents',
|
||||
top: 'Scroll to top',
|
||||
ul: 'Unordered list',
|
||||
ulItem: 'item',
|
||||
words: 'Words',
|
||||
write: 'Write',
|
||||
writeOnly: 'Write only',
|
||||
};
|
||||
}
|
15
src/client/components/MarkdownEditor/viewer.tsx
Normal file
15
src/client/components/MarkdownEditor/viewer.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import { Viewer } from '@bytemd/react';
|
||||
import { plugins } from './plugins';
|
||||
import 'bytemd/dist/index.css';
|
||||
import './style.less';
|
||||
|
||||
interface MarkdownViewerProps {
|
||||
value: string;
|
||||
}
|
||||
export const MarkdownViewer: React.FC<MarkdownViewerProps> = React.memo(
|
||||
(props) => {
|
||||
return <Viewer plugins={plugins} value={props.value ?? ''} />;
|
||||
}
|
||||
);
|
||||
MarkdownViewer.displayName = 'MarkdownViewer';
|
@ -5,12 +5,14 @@ import { domainValidator, urlSlugValidator } from '../../../utils/validator';
|
||||
import { useTranslation } from '@i18next-toolkit/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { LuMinusCircle, LuPlus } from 'react-icons/lu';
|
||||
import { MarkdownEditor } from '@/components/MarkdownEditor';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
export interface MonitorStatusPageEditFormValues {
|
||||
title: string;
|
||||
slug: string;
|
||||
description: string;
|
||||
monitorList: PrismaJson.MonitorStatusPageList;
|
||||
domain: string;
|
||||
}
|
||||
@ -72,6 +74,10 @@ export const MonitorStatusPageEditForm: React.FC<MonitorStatusPageEditFormProps>
|
||||
<Input addonBefore={`${window.origin}/status/`} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('Description')} name="description">
|
||||
<MarkdownEditor value={''} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('Custom Domain')}
|
||||
name="domain"
|
||||
|
@ -13,6 +13,7 @@ import { useTranslation } from '@i18next-toolkit/react';
|
||||
import { Link, useNavigate } from '@tanstack/react-router';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { MarkdownViewer } from '@/components/MarkdownEditor';
|
||||
|
||||
interface MonitorStatusPageProps {
|
||||
slug: string;
|
||||
@ -126,6 +127,11 @@ export const MonitorStatusPage: React.FC<MonitorStatusPageProps> = React.memo(
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Desc */}
|
||||
<div className="mb-4">
|
||||
<MarkdownViewer value={info?.description ?? ''} />
|
||||
</div>
|
||||
|
||||
<div className="mb-2 text-lg">{t('Services')}</div>
|
||||
|
||||
{info && (
|
||||
|
@ -2,9 +2,10 @@
|
||||
const config = {
|
||||
locales: ['en', 'zh', 'jp', 'fr', 'de', 'pl', 'pt', 'ru'],
|
||||
verbose: true,
|
||||
namespaces: ['translation'],
|
||||
translator: {
|
||||
type: 'openai',
|
||||
model: 'gpt-4'
|
||||
model: 'gpt-4',
|
||||
},
|
||||
scanner: {
|
||||
autoImport: false,
|
||||
@ -44,7 +45,7 @@ const config = {
|
||||
'YYYY-MM-DD HH:mm',
|
||||
'CPU',
|
||||
'RAM',
|
||||
'HDD'
|
||||
'HDD',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
@ -21,6 +21,8 @@
|
||||
"@ant-design/icons": "^5.3.6",
|
||||
"@antv/l7": "^2.20.14",
|
||||
"@antv/larkmap": "^1.4.13",
|
||||
"@bytemd/plugin-gfm": "^1.21.0",
|
||||
"@bytemd/react": "^1.21.0",
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
"@i18next-toolkit/react": "^1.0.6",
|
||||
"@loadable/component": "^5.16.3",
|
||||
@ -52,6 +54,7 @@
|
||||
"antd": "^5.13.1",
|
||||
"array-move": "^3.0.1",
|
||||
"axios": "^1.5.0",
|
||||
"bytemd": "^1.21.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.0",
|
||||
"cmdk": "^1.0.0",
|
||||
@ -88,7 +91,7 @@
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@i18next-toolkit/cli": "^1.2.1",
|
||||
"@i18next-toolkit/cli": "^1.2.2",
|
||||
"@tanstack/router-vite-plugin": "^1.20.5",
|
||||
"@types/leaflet": "^1.9.8",
|
||||
"@types/loadable__component": "^5.13.8",
|
||||
|
@ -120,6 +120,7 @@
|
||||
"k85344b23": "Laden",
|
||||
"k85c5fd4c": "Noch kein Monitor eingerichtet",
|
||||
"k8746ec38": "Monitor auswählen",
|
||||
"k87615a96": "Bestätigen Sie das Löschen dieser Seite?",
|
||||
"k887ae5cd": "Erstellen",
|
||||
"k88a9bf01": "Abzeichen anzeigen",
|
||||
"k88d2647b": "Webseite",
|
||||
@ -193,7 +194,7 @@
|
||||
"kc4e91854": "Sind Sie sicher, dass Sie alle Herzschläge für diesen Monitor löschen möchten?",
|
||||
"kc5573507": "Hinzufügen",
|
||||
"kc5f82d53": "Zum Beispiel: pushdeer://pushKey",
|
||||
"kc6888ac4": "Auto",
|
||||
"kc6888ac4": "Automatisch",
|
||||
"kc6cac621": "(Keine)",
|
||||
"kc70d69ad": "Antwort",
|
||||
"kc9b446d1": "Ausführung abgeschlossen",
|
||||
@ -239,6 +240,7 @@
|
||||
"ke9dcaa64": "Keine Benachrichtigung gefunden",
|
||||
"kea954889": "Weiter",
|
||||
"keaf7576f": "Titel",
|
||||
"keb78cff1": "Description",
|
||||
"ked37937b": "Metriken",
|
||||
"ked7eea1a": "OBEN",
|
||||
"ked8814bc": "Kopieren",
|
||||
|
@ -120,6 +120,7 @@
|
||||
"k85344b23": "Load",
|
||||
"k85c5fd4c": "No any monitor has been set",
|
||||
"k8746ec38": "Select monitor",
|
||||
"k87615a96": "Confirm to delete this page?",
|
||||
"k887ae5cd": "Create",
|
||||
"k88a9bf01": "Show Badge",
|
||||
"k88d2647b": "Website",
|
||||
@ -239,6 +240,7 @@
|
||||
"ke9dcaa64": "Not found any notification",
|
||||
"kea954889": "Continue",
|
||||
"keaf7576f": "Title",
|
||||
"keb78cff1": "Description",
|
||||
"ked37937b": "Metrics",
|
||||
"ked7eea1a": "UP",
|
||||
"ked8814bc": "Copy",
|
||||
|
@ -101,7 +101,7 @@
|
||||
"k721589c1": "Aujourd'hui",
|
||||
"k7350bd93": "En même temps, nous pouvons également l'utiliser dans certains scénarios d'application côté client, comme la collecte de la fréquence d'utilisation du CLI, la collecte de l'installation d'applications auto-hébergées, etc.",
|
||||
"k74a240": "Barre de santé",
|
||||
"k75581e13": "CC",
|
||||
"k75581e13": "Sous-titres",
|
||||
"k75bfaaa6": "Ajoutez ce code dans le script de tête de votre site web",
|
||||
"k763816ac": "Aperçu",
|
||||
"k784dd132": "Test",
|
||||
@ -120,6 +120,7 @@
|
||||
"k85344b23": "Charge",
|
||||
"k85c5fd4c": "Aucun moniteur n'a été défini",
|
||||
"k8746ec38": "Sélectionner un moniteur",
|
||||
"k87615a96": "Confirmer la suppression de cette page ?",
|
||||
"k887ae5cd": "Créer",
|
||||
"k88a9bf01": "Afficher le badge",
|
||||
"k88d2647b": "Site Web",
|
||||
@ -239,6 +240,7 @@
|
||||
"ke9dcaa64": "Aucune notification trouvée",
|
||||
"kea954889": "Continuer",
|
||||
"keaf7576f": "Titre",
|
||||
"keb78cff1": "Description",
|
||||
"ked37937b": "Métriques",
|
||||
"ked7eea1a": "EN LIGNE",
|
||||
"ked8814bc": "Copier",
|
||||
|
@ -120,6 +120,7 @@
|
||||
"k85344b23": "ロード",
|
||||
"k85c5fd4c": "まだモニターが設定されていません",
|
||||
"k8746ec38": "モニターを選択",
|
||||
"k87615a96": "このページを削除しますか?",
|
||||
"k887ae5cd": "作成",
|
||||
"k88a9bf01": "バッジを表示",
|
||||
"k88d2647b": "ウェブサイト",
|
||||
@ -239,6 +240,7 @@
|
||||
"ke9dcaa64": "通知は見つかりませんでした",
|
||||
"kea954889": "続行",
|
||||
"keaf7576f": "タイトル",
|
||||
"keb78cff1": "Description",
|
||||
"ked37937b": "メトリクス",
|
||||
"ked7eea1a": "アップ",
|
||||
"ked8814bc": "コピー",
|
||||
|
@ -120,6 +120,7 @@
|
||||
"k85344b23": "Obciążenie",
|
||||
"k85c5fd4c": "Nie ustawiono żadnego monitora",
|
||||
"k8746ec38": "Wybierz monitor",
|
||||
"k87615a96": "Potwierdź usunięcie tej strony?",
|
||||
"k887ae5cd": "Utwórz",
|
||||
"k88a9bf01": "Pokaż odznakę",
|
||||
"k88d2647b": "Strona internetowa",
|
||||
@ -239,6 +240,7 @@
|
||||
"ke9dcaa64": "Nie znaleziono żadnych powiadomień",
|
||||
"kea954889": "Kontynuuj",
|
||||
"keaf7576f": "Tytuł",
|
||||
"keb78cff1": "Description",
|
||||
"ked37937b": "Metryki",
|
||||
"ked7eea1a": "DZIAŁA",
|
||||
"ked8814bc": "Kopiuj",
|
||||
|
@ -120,6 +120,7 @@
|
||||
"k85344b23": "Carregar",
|
||||
"k85c5fd4c": "Não foi definido qualquer monitor",
|
||||
"k8746ec38": "Selecionar monitor",
|
||||
"k87615a96": "Confirmar exclusão desta página?",
|
||||
"k887ae5cd": "Criar",
|
||||
"k88a9bf01": "Mostrar crachá",
|
||||
"k88d2647b": "Sítio Web",
|
||||
@ -177,7 +178,7 @@
|
||||
"kb35cde91": "Pesquisar",
|
||||
"kb5673707": "Últimos 7 dias",
|
||||
"kb659c1bc": "Exp. do certificado",
|
||||
"kb8de8c50": "BCC",
|
||||
"kb8de8c50": "CCO",
|
||||
"kbb31d3db": "Data da estatística",
|
||||
"kbb4e12c5": "Escuro",
|
||||
"kbb58c99c": "Eliminar com êxito",
|
||||
@ -239,6 +240,7 @@
|
||||
"ke9dcaa64": "Não encontrei nenhuma notificação",
|
||||
"kea954889": "Continuar",
|
||||
"keaf7576f": "Título",
|
||||
"keb78cff1": "Description",
|
||||
"ked37937b": "Métricas",
|
||||
"ked7eea1a": "ATIVO",
|
||||
"ked8814bc": "Copiar",
|
||||
|
@ -120,6 +120,7 @@
|
||||
"k85344b23": "Нагрузка",
|
||||
"k85c5fd4c": "Мониторы еще не настроены",
|
||||
"k8746ec38": "Выбрать монитор",
|
||||
"k87615a96": "Подтвердить удаление этой страницы?",
|
||||
"k887ae5cd": "Создать",
|
||||
"k88a9bf01": "Показать значок",
|
||||
"k88d2647b": "Веб-сайт",
|
||||
@ -239,6 +240,7 @@
|
||||
"ke9dcaa64": "Уведомлений не найдено",
|
||||
"kea954889": "Продолжить",
|
||||
"keaf7576f": "Заголовок",
|
||||
"keb78cff1": "Description",
|
||||
"ked37937b": "Метрики",
|
||||
"ked7eea1a": "ВВЕРХ",
|
||||
"ked8814bc": "Копировать",
|
||||
|
@ -120,6 +120,7 @@
|
||||
"k85344b23": "负载",
|
||||
"k85c5fd4c": "还没有设置任何监控器",
|
||||
"k8746ec38": "选择监控器",
|
||||
"k87615a96": "确认删除此页面?",
|
||||
"k887ae5cd": "创建",
|
||||
"k88a9bf01": "显示徽章",
|
||||
"k88d2647b": "网站",
|
||||
@ -239,6 +240,7 @@
|
||||
"ke9dcaa64": "没有找到任何通知",
|
||||
"kea954889": "继续",
|
||||
"keaf7576f": "标题",
|
||||
"keb78cff1": "Description",
|
||||
"ked37937b": "指标",
|
||||
"ked7eea1a": "上线",
|
||||
"ked8814bc": "复制",
|
||||
|
Loading…
Reference in New Issue
Block a user