Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,29 @@
margin: 0.5rem 0;
display: block;
}

/* Scrollbar fino e discreto em toda a aplicação — o scrollbar padrão do
navegador (grosso, cinza puro, quadrado) destoa do resto do design.
Firefox usa scrollbar-width/scrollbar-color; Chrome/Edge/Safari usam os
pseudo-elementos ::-webkit-scrollbar-*. Aplicado globalmente pra não
precisar caçar cada overflow-y-auto individualmente. */
* {
scrollbar-width: thin;
scrollbar-color: var(--border) transparent;
}
*::-webkit-scrollbar {
width: 8px;
height: 8px;
}
*::-webkit-scrollbar-track {
background: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: var(--border);
border-radius: 999px;
border: 2px solid transparent;
background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover {
background-color: var(--muted-foreground);
}
66 changes: 51 additions & 15 deletions src/components/markdown-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useRef, useState } from "react";
import type { ReactNode } from "react";
import ReactMarkdown from "react-markdown";
import { ImagePlus } from "lucide-react";
import { cn } from "@/lib/utils";

// Imagem vira base64 embutido no próprio markdown (data URL) — sem upload,
Expand Down Expand Up @@ -59,6 +60,7 @@ export function MarkdownEditor({
const [preview, setPreview] = useState(false);
const [pasteError, setPasteError] = useState("");
const textareaRef = useRef<HTMLTextAreaElement>(null);
const fileInputRef = useRef<HTMLInputElement>(null);

const insertAtCursor = (text: string) => {
const el = textareaRef.current;
Expand All @@ -77,32 +79,44 @@ export function MarkdownEditor({
});
};

const handlePaste = async (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
const imageItem = Array.from(e.clipboardData.items).find((item) =>
item.type.startsWith("image/")
);
if (!imageItem) return;

e.preventDefault();
const insertImageFile = async (file: File) => {
setPasteError("");
const file = imageItem.getAsFile();
if (!file) return;

if (!file.type.startsWith("image/")) {
setPasteError("Só é possível inserir arquivos de imagem.");
return;
}
if (file.size > MAX_PASTED_IMAGE_BYTES) {
setPasteError(
"Imagem muito grande (máx. 2 MB). Reduza o tamanho e tente colar de novo."
"Imagem muito grande (máx. 2 MB). Reduza o tamanho e tente de novo."
);
return;
}

try {
const dataUrl = await fileToDataUrl(file);
insertAtCursor(`\n![imagem](${dataUrl})\n`);
} catch {
setPasteError("Não foi possível colar a imagem. Tente novamente.");
setPasteError("Não foi possível inserir a imagem. Tente novamente.");
}
};

const handlePaste = (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
const imageItem = Array.from(e.clipboardData.items).find((item) =>
item.type.startsWith("image/")
);
if (!imageItem) return;

e.preventDefault();
const file = imageItem.getAsFile();
if (!file) return;
void insertImageFile(file);
};

const handleFilePick = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
e.target.value = "";
if (file) void insertImageFile(file);
};

const toggle = (
<div className="flex rounded-md border overflow-hidden text-xs shrink-0">
<button
Expand Down Expand Up @@ -154,7 +168,28 @@ export function MarkdownEditor({
</span>
)}
</div>
{toggle}
<div className="flex items-center gap-2">
{!preview && (
<>
<input
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleFilePick}
className="hidden"
/>
<button
type="button"
onClick={() => fileInputRef.current?.click()}
className="flex items-center gap-1.5 rounded-md border px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
>
<ImagePlus className="h-3.5 w-3.5" />
Inserir imagem
</button>
</>
)}
{toggle}
</div>
</div>

{preview ? (
Expand Down Expand Up @@ -199,7 +234,8 @@ export function MarkdownEditor({

{!preview && (
<p className="text-xs text-muted-foreground">
Dica: cole (Ctrl+V) uma imagem copiada pra inserir direto no texto.
Cole (Ctrl+V) uma imagem copiada ou use &quot;Inserir imagem&quot;
acima.
</p>
)}

Expand Down
Loading