-
-
-
-
+
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+ ({
+ value: language,
+ label: language,
+ }))}
+ placeholder="language"
+ value={activeTab.language}
+ onValueChange={setLanguage}
+ onPreview={setLanguage}
+ className="w-full xl:w-40"
+ />
+
+ ({
+ value: currentTheme,
+ label: currentTheme,
+ }))}
+ placeholder="theme"
+ value={theme}
+ onValueChange={setTheme}
+ onPreview={setTheme}
+ className="w-full xl:w-52"
+ />
+
-
-
- ({ value: l, label: l }))}
- placeholder="language"
- value={language}
- onValueChange={setLanguage}
- onPreview={setLanguage}
- className="w-full sm:w-40"
- />
-
- ({
- value: t,
- label: t,
- }))}
- placeholder="theme"
- value={theme}
- onValueChange={setTheme}
- onPreview={setTheme}
- className="w-full sm:w-52"
- />
-
-
+
);
}
diff --git a/src/components/monaco-editor.tsx b/src/components/monaco-editor.tsx
index c7485a7..b6ff91a 100644
--- a/src/components/monaco-editor.tsx
+++ b/src/components/monaco-editor.tsx
@@ -8,21 +8,21 @@ import { useEffect, useState } from "react";
import { Editor, type Monaco } from "@monaco-editor/react";
import { shikiToMonaco } from "@shikijs/monaco";
import { createHighlighter } from "shiki";
-import { LANGUAGES, LANGUAGE_NAMES } from "~/utils/languages";
+import { LANGUAGES, MONACO_LANGUAGES } from "~/utils/languages";
import { useEditor } from "./editor-provider";
import { THEME_MAP } from "~/utils/themes";
export function MonacoEditor() {
- const { language, theme, content, setContent } = useEditor();
+ const { activeTab, theme, updateActiveTabContent } = useEditor();
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
- const colors = THEME_MAP[theme].ui;
+ const colors = THEME_MAP[theme]?.ui;
if (!colors) return;
- Object.entries(colors).forEach(([key, value]) => {
+ for (const [key, value] of Object.entries(colors)) {
document.documentElement.style.setProperty(`--${key}`, value);
- });
+ }
}, [theme]);
const handleEditorDidMount = async (monaco: Monaco) => {
@@ -32,11 +32,11 @@ export function MonacoEditor() {
.filter(([key]) => key !== theme)
.map(([key, value]) => value.theme ?? key);
- LANGUAGE_NAMES.forEach((l) => monaco.languages.register({ id: l }));
+ LANGUAGES.forEach((l) => monaco.languages.register({ id: l }));
const highlighter = await createHighlighter({
themes: [currentTheme, ...restThemes],
- langs: LANGUAGES,
+ langs: [...MONACO_LANGUAGES],
});
shikiToMonaco(highlighter, monaco);
@@ -54,7 +54,7 @@ export function MonacoEditor() {
};
return (
-
+
{isLoading && (
@@ -65,17 +65,25 @@ export function MonacoEditor() {
)}
setContent(val || "")}
+ language={activeTab.language}
+ value={activeTab.content}
+ onChange={(val) => updateActiveTabContent(val || "")}
onMount={async (editor, monaco) => {
- handleEditorDidMount(monaco).catch(console.error);
- await AutoTypings.create(editor, {
- sourceCache: new LocalStorageCache(),
- monaco,
- });
+ try {
+ await handleEditorDidMount(monaco);
+ if (!editor.getModel()) return;
+
+ await AutoTypings.create(editor, {
+ sourceCache: new LocalStorageCache(),
+ monaco,
+ });
+ } catch (error) {
+ console.warn("AutoTypings init failed (ignored):", error);
+ }
}}
options={{
fontSize: 14,
diff --git a/src/components/save-button.tsx b/src/components/save-button.tsx
index d92cf47..ede8499 100644
--- a/src/components/save-button.tsx
+++ b/src/components/save-button.tsx
@@ -1,30 +1,25 @@
"use client";
import { useRouter } from "next/navigation";
-import { useEffect } from "react";
+import { useCallback, useEffect } from "react";
import { toast } from "sonner";
import { Button } from "~/components/ui/button";
import { addPaste } from "~/actions/paste";
+import type { PasteTab } from "~/utils/paste-tabs";
interface SaveButtonProps {
- content: string;
- language: string;
+ tabs: PasteTab[];
theme: string;
className?: string;
}
-export function SaveButton({
- content,
- language,
- theme,
- className,
-}: SaveButtonProps) {
+export function SaveButton({ tabs, theme, className }: SaveButtonProps) {
const router = useRouter();
- const handleSave = async () => {
+ const handleSave = useCallback(async () => {
try {
- if (!content) return;
- const result = await addPaste(content, language, theme);
+ if (!tabs.some((tab) => tab.content.trim())) return;
+ const result = await addPaste({ tabs, theme });
if (!result.id) return;
const url = `${window.location.origin}/${result.id}`;
@@ -36,18 +31,18 @@ export function SaveButton({
console.error("Failed to save paste:", error);
toast("failed to save paste");
}
- };
+ }, [tabs, theme, router]);
useEffect(() => {
- const handleKeyDown = (e: KeyboardEvent) => {
+ const onKeyDown = (e: KeyboardEvent) => {
if (e.key !== "s" || !(e.ctrlKey || e.metaKey)) return;
e.preventDefault();
- handleSave();
+ void handleSave();
};
- window.addEventListener("keydown", handleKeyDown);
- return () => window.removeEventListener("keydown", handleKeyDown);
- }, [content, language, theme]);
+ window.addEventListener("keydown", onKeyDown);
+ return () => window.removeEventListener("keydown", onKeyDown);
+ }, [handleSave]);
return (