vim moddddeee
This commit is contained in:
57
package-lock.json
generated
57
package-lock.json
generated
@@ -8,6 +8,8 @@
|
|||||||
"name": "markdownblog",
|
"name": "markdownblog",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fontsource/jetbrains-mono": "^5.2.6",
|
||||||
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@tailwindcss/typography": "^0.5.16",
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
"@types/node": "^20.11.24",
|
"@types/node": "^20.11.24",
|
||||||
"@types/react": "^18.2.61",
|
"@types/react": "^18.2.61",
|
||||||
@@ -25,6 +27,8 @@
|
|||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"jsdom": "^24.0.0",
|
"jsdom": "^24.0.0",
|
||||||
"marked": "^12.0.0",
|
"marked": "^12.0.0",
|
||||||
|
"monaco-editor": "^0.52.2",
|
||||||
|
"monaco-vim": "^0.4.2",
|
||||||
"next": "14.1.0",
|
"next": "14.1.0",
|
||||||
"pm2": "^6.0.8",
|
"pm2": "^6.0.8",
|
||||||
"postcss": "^8.4.35",
|
"postcss": "^8.4.35",
|
||||||
@@ -304,6 +308,15 @@
|
|||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@fontsource/jetbrains-mono": {
|
||||||
|
"version": "5.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.2.6.tgz",
|
||||||
|
"integrity": "sha512-nz//dBr99hXZmHp10wgNI00qThWImkzRR5PQjvRM+rpmuHO5rYBJCqPPWufidCvmkkryXx/GOP/lgqsM3R3Org==",
|
||||||
|
"license": "OFL-1.1",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ayuhito"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.13.0",
|
"version": "0.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||||
@@ -542,6 +555,29 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@monaco-editor/loader": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"state-local": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@monaco-editor/react": {
|
||||||
|
"version": "4.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz",
|
||||||
|
"integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@monaco-editor/loader": "^1.5.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"monaco-editor": ">= 0.25.0 < 1",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@next/env": {
|
"node_modules/@next/env": {
|
||||||
"version": "14.1.0",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz",
|
||||||
@@ -5664,6 +5700,21 @@
|
|||||||
"integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
|
"integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/monaco-editor": {
|
||||||
|
"version": "0.52.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
|
||||||
|
"integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/monaco-vim": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/monaco-vim/-/monaco-vim-0.4.2.tgz",
|
||||||
|
"integrity": "sha512-rdbQC3O2rmpwX2Orzig/6gZjZfH7q7TIeB+uEl49sa+QyNm3jCKJOw5mwxBdFzTqbrPD+URfg6A2lEkuL5kymw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"monaco-editor": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@@ -7590,6 +7641,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/state-local": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/stop-iteration-iterator": {
|
"node_modules/stop-iteration-iterator": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
"electron-dev": "concurrently \"npm run dev\" \"npm run electron\""
|
"electron-dev": "concurrently \"npm run dev\" \"npm run electron\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fontsource/jetbrains-mono": "^5.2.6",
|
||||||
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@tailwindcss/typography": "^0.5.16",
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
"@types/node": "^20.11.24",
|
"@types/node": "^20.11.24",
|
||||||
"@types/react": "^18.2.61",
|
"@types/react": "^18.2.61",
|
||||||
@@ -28,6 +30,8 @@
|
|||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"jsdom": "^24.0.0",
|
"jsdom": "^24.0.0",
|
||||||
"marked": "^12.0.0",
|
"marked": "^12.0.0",
|
||||||
|
"monaco-editor": "^0.52.2",
|
||||||
|
"monaco-vim": "^0.4.2",
|
||||||
"next": "14.1.0",
|
"next": "14.1.0",
|
||||||
"pm2": "^6.0.8",
|
"pm2": "^6.0.8",
|
||||||
"postcss": "^8.4.35",
|
"postcss": "^8.4.35",
|
||||||
|
|||||||
@@ -14,6 +14,15 @@ import hljs from 'highlight.js';
|
|||||||
import matter from 'gray-matter';
|
import matter from 'gray-matter';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { Theme } from 'emoji-picker-react';
|
import { Theme } from 'emoji-picker-react';
|
||||||
|
import '../highlight-github.css';
|
||||||
|
import MonacoEditor from '@monaco-editor/react';
|
||||||
|
import { initVimMode, VimMode } from 'monaco-vim';
|
||||||
|
import '@fontsource/jetbrains-mono';
|
||||||
|
|
||||||
|
// @ts-ignore-next-line
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// If you want, you can move this to a global.d.ts file
|
||||||
|
// declare module 'monaco-vim';
|
||||||
|
|
||||||
interface Post {
|
interface Post {
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -48,6 +57,17 @@ type Node = Post | Folder;
|
|||||||
|
|
||||||
const EmojiPicker = dynamic(() => import('emoji-picker-react'), { ssr: false });
|
const EmojiPicker = dynamic(() => import('emoji-picker-react'), { ssr: false });
|
||||||
|
|
||||||
|
// Patch marked renderer to always add 'hljs' class to code blocks
|
||||||
|
const renderer = new marked.Renderer();
|
||||||
|
renderer.code = function(code, infostring, escaped) {
|
||||||
|
const lang = (infostring || '').match(/\S*/)?.[0];
|
||||||
|
const highlighted = lang && hljs.getLanguage(lang)
|
||||||
|
? hljs.highlight(code, { language: lang }).value
|
||||||
|
: hljs.highlightAuto(code).value;
|
||||||
|
const langClass = lang ? `language-${lang}` : '';
|
||||||
|
return `<pre><code class="hljs ${langClass}">${highlighted}</code></pre>`;
|
||||||
|
};
|
||||||
|
|
||||||
export default function AdminPage() {
|
export default function AdminPage() {
|
||||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
const [username, setUsername] = useState('');
|
const [username, setUsername] = useState('');
|
||||||
@@ -97,6 +117,10 @@ export default function AdminPage() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const usernameRef = useRef<HTMLInputElement>(null);
|
const usernameRef = useRef<HTMLInputElement>(null);
|
||||||
const passwordRef = useRef<HTMLInputElement>(null);
|
const passwordRef = useRef<HTMLInputElement>(null);
|
||||||
|
const monacoRef = useRef<any>(null);
|
||||||
|
const vimStatusRef = useRef(null);
|
||||||
|
const vimInstanceRef = useRef<any>(null);
|
||||||
|
const [vimMode, setVimMode] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Check if already authenticated
|
// Check if already authenticated
|
||||||
@@ -129,13 +153,7 @@ export default function AdminPage() {
|
|||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
gfm: true,
|
gfm: true,
|
||||||
breaks: true,
|
breaks: true,
|
||||||
highlight: function(code: string, lang: string) {
|
renderer,
|
||||||
if (lang && hljs.getLanguage(lang)) {
|
|
||||||
return hljs.highlight(code, { language: lang }).value;
|
|
||||||
} else {
|
|
||||||
return hljs.highlightAuto(code).value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} as any);
|
} as any);
|
||||||
setPreviewHtml(marked.parse(newPost.content || '') as string);
|
setPreviewHtml(marked.parse(newPost.content || '') as string);
|
||||||
}, [newPost.content]);
|
}, [newPost.content]);
|
||||||
@@ -752,6 +770,17 @@ export default function AdminPage() {
|
|||||||
return Theme.LIGHT;
|
return Theme.LIGHT;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Attach/detach Vim mode when vimMode changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (vimMode && monacoRef.current) {
|
||||||
|
// @ts-ignore
|
||||||
|
vimInstanceRef.current = initVimMode(monacoRef.current, vimStatusRef.current);
|
||||||
|
} else if (vimInstanceRef.current) {
|
||||||
|
vimInstanceRef.current.dispose();
|
||||||
|
vimInstanceRef.current = null;
|
||||||
|
}
|
||||||
|
}, [vimMode, monacoRef.current]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-100 p-3 sm:p-8">
|
<div className="min-h-screen bg-gray-100 p-3 sm:p-8">
|
||||||
{pinFeedback && (
|
{pinFeedback && (
|
||||||
@@ -990,8 +1019,6 @@ export default function AdminPage() {
|
|||||||
Current folder: <span className="font-mono">{currentPath.join('/') || 'root'}</span>
|
Current folder: <span className="font-mono">{currentPath.join('/') || 'root'}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Drag and Drop Zone */}
|
{/* Drag and Drop Zone */}
|
||||||
<div
|
<div
|
||||||
className={`mb-6 sm:mb-8 p-4 sm:p-8 border-2 border-dashed rounded-lg text-center ${
|
className={`mb-6 sm:mb-8 p-4 sm:p-8 border-2 border-dashed rounded-lg text-center ${
|
||||||
@@ -1072,25 +1099,66 @@ export default function AdminPage() {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* Mobile-friendly content editor */}
|
<div className="flex items-center mb-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="vim-toggle"
|
||||||
|
checked={vimMode}
|
||||||
|
onChange={() => setVimMode(v => !v)}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="vim-toggle"
|
||||||
|
className="text-sm font-bold"
|
||||||
|
style={{
|
||||||
|
fontFamily: "'JetBrains Mono', 'monospace', cursive",
|
||||||
|
fontStyle: 'italic',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Vim Mode
|
||||||
|
</label>
|
||||||
|
<div ref={vimStatusRef} className="ml-4 text-xs text-gray-500 font-mono" />
|
||||||
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex flex-col sm:flex-row gap-4">
|
<div className="flex flex-col sm:flex-row gap-4">
|
||||||
<div className="w-full sm:w-1/2">
|
<div className="w-full sm:w-1/2">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">Inhalt (Markdown)</label>
|
<div style={{ height: '240px' }}>
|
||||||
<textarea
|
<MonacoEditor
|
||||||
|
height="100%"
|
||||||
|
defaultLanguage="markdown"
|
||||||
value={newPost.content}
|
value={newPost.content}
|
||||||
onChange={(e) => setNewPost({ ...newPost, content: e.target.value })}
|
onChange={(value) => setNewPost({ ...newPost, content: value || '' })}
|
||||||
className="w-full rounded-md border border-gray-300 px-3 py-2 font-mono text-sm sm:text-base"
|
options={{
|
||||||
style={{ height: '240px' }}
|
minimap: { enabled: false },
|
||||||
rows={10}
|
wordWrap: 'on',
|
||||||
required
|
fontSize: 14,
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
theme: 'vs-light',
|
||||||
|
lineNumbers: 'on',
|
||||||
|
automaticLayout: true,
|
||||||
|
fontFamily: 'JetBrains Mono, monospace',
|
||||||
|
}}
|
||||||
|
onMount={(editor) => {
|
||||||
|
monacoRef.current = editor;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className="w-full sm:w-1/2">
|
<div className="w-full sm:w-1/2">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">Vorschau</label>
|
<label className="block text-sm font-medium text-gray-700 mb-2">Vorschau</label>
|
||||||
<div className="p-3 sm:p-4 border rounded bg-gray-50 overflow-auto" style={{ height: '240px' }}>
|
<div className="p-3 sm:p-4 border rounded bg-gray-50 overflow-auto" style={{ height: '240px' }}>
|
||||||
<div className="prose prose-sm max-w-none" dangerouslySetInnerHTML={{ __html: previewHtml }} />
|
<div
|
||||||
|
className="prose prose-sm max-w-none"
|
||||||
|
style={{ fontFamily: 'inherit' }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: previewHtml }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<style jsx global>{`
|
||||||
|
.prose code, .prose pre {
|
||||||
|
font-family: 'JetBrains Mono', monospace !important;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
1
src/app/monaco-vim.d.ts
vendored
Normal file
1
src/app/monaco-vim.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
declare module 'monaco-vim';
|
||||||
Reference in New Issue
Block a user