Commit 0893fd21 authored by Kantz's avatar Kantz
Browse files

Canvas größe verringert und deutsche Übersetztung hinzugefügt

parent 6ab334f1
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import CanvasToggle from "./CanvasToggle"; import CanvasToggle from "./CanvasToggle";
import { t } from "../../i18n";
type CanvasDrawerProps = { type CanvasDrawerProps = {
isVisible: boolean; isVisible: boolean;
...@@ -37,8 +38,8 @@ export default function CanvasDrawer({ ...@@ -37,8 +38,8 @@ export default function CanvasDrawer({
} }
const ratio = window.devicePixelRatio || 1; const ratio = window.devicePixelRatio || 1;
const width = 720; const width = Math.min(560, window.innerWidth - 80);
const height = 320; const height = 150;
canvas.width = width * ratio; canvas.width = width * ratio;
canvas.height = height * ratio; canvas.height = height * ratio;
...@@ -134,7 +135,20 @@ export default function CanvasDrawer({ ...@@ -134,7 +135,20 @@ export default function CanvasDrawer({
return ( return (
<div className="canvas-zone"> <div className="canvas-zone">
<div className="canvas-header"> <div className="canvas-header">
<div className="canvas-title">Canvas</div> <div className="canvas-title">{t("canvas")}</div>
{isVisible ? (
<div className="canvas-actions canvas-actions-header">
<button className="btn" type="button" onClick={handleToggleEraser}>
{isErasing ? t("pen") : t("eraser")}
</button>
<button className="btn" type="button" onClick={handleClear}>
{t("clear")}
</button>
<button className="btn primary" type="button" onClick={handleSave}>
{t("saveAndConvert")}
</button>
</div>
) : null}
<CanvasToggle isVisible={isVisible} onToggle={onToggle} /> <CanvasToggle isVisible={isVisible} onToggle={onToggle} />
</div> </div>
{isVisible ? ( {isVisible ? (
...@@ -156,20 +170,9 @@ export default function CanvasDrawer({ ...@@ -156,20 +170,9 @@ export default function CanvasDrawer({
onPointerLeave={handlePointerUp} onPointerLeave={handlePointerUp}
/> />
</div> </div>
<div className="canvas-actions">
<button className="btn" type="button" onClick={handleToggleEraser}>
{isErasing ? "Pen" : "Eraser"}
</button>
<button className="btn" type="button" onClick={handleClear}>
Clear
</button>
<button className="btn primary" type="button" onClick={handleSave}>
Save + Convert
</button>
</div>
</div> </div>
) : ( ) : (
<div className="canvas-hidden">Canvas hidden</div> <div className="canvas-hidden">{t("canvasHidden")}</div>
)} )}
</div> </div>
); );
......
import { t } from "../../i18n";
type CanvasToggleProps = { type CanvasToggleProps = {
isVisible: boolean; isVisible: boolean;
onToggle: () => void; onToggle: () => void;
...@@ -9,7 +11,7 @@ export default function CanvasToggle({ ...@@ -9,7 +11,7 @@ export default function CanvasToggle({
}: CanvasToggleProps) { }: CanvasToggleProps) {
return ( return (
<button className="btn" type="button" onClick={onToggle}> <button className="btn" type="button" onClick={onToggle}>
{isVisible ? "Hide" : "Show"} {isVisible ? t("hide") : t("show")}
</button> </button>
); );
} }
...@@ -2,6 +2,7 @@ import MessageInput from "./MessageInput"; ...@@ -2,6 +2,7 @@ import MessageInput from "./MessageInput";
import MessageList from "./MessageList"; import MessageList from "./MessageList";
import type { ChatMessage } from "./MessageList"; import type { ChatMessage } from "./MessageList";
import type { RetrievedDoc } from "../Retrieval/DocPanel"; import type { RetrievedDoc } from "../Retrieval/DocPanel";
import { t } from "../../i18n";
type ChatWindowProps = { type ChatWindowProps = {
messages: ChatMessage[]; messages: ChatMessage[];
...@@ -26,7 +27,7 @@ export default function ChatWindow({ ...@@ -26,7 +27,7 @@ export default function ChatWindow({
}: ChatWindowProps) { }: ChatWindowProps) {
return ( return (
<div className="chat-window"> <div className="chat-window">
<div className="chat-title">Chat</div> <div className="chat-title">{t("chat")}</div>
<MessageList <MessageList
messages={messages} messages={messages}
onInspectDoc={onInspectDoc} onInspectDoc={onInspectDoc}
......
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import type { RetrievedDoc } from "../Retrieval/DocPanel"; import type { RetrievedDoc } from "../Retrieval/DocPanel";
import { t } from "../../i18n";
type MessageBubbleProps = { type MessageBubbleProps = {
role: "user" | "assistant"; role: "user" | "assistant";
...@@ -48,7 +49,9 @@ export default function MessageBubble({ ...@@ -48,7 +49,9 @@ export default function MessageBubble({
return ( return (
<div className={`message-bubble ${role}`} ref={bubbleRef}> <div className={`message-bubble ${role}`} ref={bubbleRef}>
<div className="message-role">{role}</div> <div className="message-role">
{role === "user" ? t("roleUser") : t("roleAssistant")}
</div>
<div className="message-text"> <div className="message-text">
<ReactMarkdown <ReactMarkdown
components={{ components={{
......
import { EditPencil, Send } from "iconoir-react"; import { EditPencil, Send } from "iconoir-react";
import { t } from "../../i18n";
type MessageInputProps = { type MessageInputProps = {
value: string; value: string;
...@@ -22,8 +23,8 @@ export default function MessageInput({ ...@@ -22,8 +23,8 @@ export default function MessageInput({
className="btn" className="btn"
type="button" type="button"
onClick={onToggleCanvas} onClick={onToggleCanvas}
aria-label="Draw" aria-label={t("draw")}
title="Draw" title={t("draw")}
> >
<EditPencil width={18} height={18} aria-hidden="true" /> <EditPencil width={18} height={18} aria-hidden="true" />
</button> </button>
...@@ -32,15 +33,15 @@ export default function MessageInput({ ...@@ -32,15 +33,15 @@ export default function MessageInput({
type="button" type="button"
onClick={onSend} onClick={onSend}
disabled={!canSend} disabled={!canSend}
aria-label="Send" aria-label={t("send")}
title="Send" title={t("send")}
> >
<Send width={18} height={18} aria-hidden="true" /> <Send width={18} height={18} aria-hidden="true" />
</button> </button>
</div> </div>
<textarea <textarea
className="composer-input" className="composer-input"
placeholder="Type your question or paste LaTeX..." placeholder={t("typeQuestionOrLatex")}
rows={3} rows={3}
value={value} value={value}
onChange={(event) => onChange(event.target.value)} onChange={(event) => onChange(event.target.value)}
......
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import { t } from "../../i18n";
declare global { declare global {
interface Window { interface Window {
...@@ -63,10 +64,10 @@ export default function DocCard({ ...@@ -63,10 +64,10 @@ export default function DocCard({
</div> </div>
<div className="doc-actions"> <div className="doc-actions">
<button className="btn small" type="button" onClick={onInspect}> <button className="btn small" type="button" onClick={onInspect}>
Inspect {t("inspect")}
</button> </button>
<button className="btn small" type="button" onClick={onCite}> <button className="btn small" type="button" onClick={onCite}>
Cite {t("cite")}
</button> </button>
</div> </div>
</details> </details>
......
import DocCard from "./DocCard"; import DocCard from "./DocCard";
import { t } from "../../i18n";
export type RetrievedDoc = { export type RetrievedDoc = {
uid: string; uid: string;
...@@ -37,7 +38,7 @@ const buildTitle = (doc: RetrievedDoc) => { ...@@ -37,7 +38,7 @@ const buildTitle = (doc: RetrievedDoc) => {
meta.subsection_title || meta.subsection_title ||
meta.section_title || meta.section_title ||
meta.path || meta.path ||
"Untitled" t("untitled")
); );
}; };
...@@ -113,16 +114,16 @@ export default function DocPanel({ ...@@ -113,16 +114,16 @@ export default function DocPanel({
return ( return (
<div className="retrieval-panel"> <div className="retrieval-panel">
<div className="retrieval-title">Retrieved Notes</div> <div className="retrieval-title">{t("retrievedNotes")}</div>
{isLoading ? <div className="retrieval-state">Loading...</div> : null} {isLoading ? <div className="retrieval-state">{t("loading")}</div> : null}
{error ? <div className="retrieval-state error">{error}</div> : null} {error ? <div className="retrieval-state error">{error}</div> : null}
{!isLoading && !error && total === 0 ? ( {!isLoading && !error && total === 0 ? (
<div className="retrieval-state">No results yet.</div> <div className="retrieval-state">{t("noResultsYet")}</div>
) : null} ) : null}
{renderGroup("Direct children", directChildren, onCiteDoc, onInspectDoc)} {renderGroup(t("directChildren"), directChildren, onCiteDoc, onInspectDoc)}
{renderGroup("Indirect children", indirectChildren, onCiteDoc, onInspectDoc)} {renderGroup(t("indirectChildren"), indirectChildren, onCiteDoc, onInspectDoc)}
{renderGroup("Subsection summary", subsections, onCiteDoc, onInspectDoc)} {renderGroup(t("subsectionSummary"), subsections, onCiteDoc, onInspectDoc)}
{renderGroup("Section summary", sections, onCiteDoc, onInspectDoc)} {renderGroup(t("sectionSummary"), sections, onCiteDoc, onInspectDoc)}
</div> </div>
); );
} }
const translations = {
en: {
chats: "Chats",
prototypeWorkspace: "Prototype Workspace",
task: "Task",
chat: "Chat",
canvas: "Canvas",
retrievedNotes: "Retrieved Notes",
typeQuestionOrLatex: "Type your question or paste LaTeX...",
noResultsYet: "No results yet.",
eraser: "Eraser",
pen: "Pen",
clear: "Clear",
saveAndConvert: "Save + Convert",
hide: "Hide",
show: "Show",
newChat: "New Chat",
loading: "Loading...",
directChildren: "Direct children",
indirectChildren: "Indirect children",
subsectionSummary: "Subsection summary",
sectionSummary: "Section summary",
untitled: "Untitled",
inspect: "Inspect",
cite: "Cite",
draw: "Draw",
send: "Send",
roleUser: "user",
roleAssistant: "assistant",
noTasksAvailable: "No tasks available",
noTaskSelected: "No task selected.",
savedChats: "Saved Chats",
saving: "Saving...",
noSavedChatsYet: "No saved chats yet.",
canvasHidden: "Canvas hidden",
failedLoadTasks: "Could not load tasks.",
chatRequestFailed: "The chat request failed. Please check backend logs.",
retrievalFailed: "Source retrieval failed. Please check backend logs.",
failedLoadSavedChats: "Could not load saved chats.",
failedLoadSelectedChat: "Could not load the selected chat.",
savingDrawingConverting: "Saving drawing and converting to LaTeX...",
saveFailedStatus: "Saving failed (status: {status}).",
canvasSaveFailed: "Saving the canvas failed.",
canvasSaved: "Canvas saved.",
canvasSaveFailedCheckLogs:
"Saving the canvas failed. Please check backend logs.",
},
de: {
chats: "Chats",
prototypeWorkspace: "Prototyp-Arbeitsbereich",
task: "Aufgabe",
chat: "Chat",
canvas: "Canvas",
retrievedNotes: "Quellen",
typeQuestionOrLatex:
"Gebe hier deine Frage ein. Nutze $ $ für mathematische Eingaben...",
noResultsYet: "bisher keine Quellen",
eraser: "Radierer",
pen: "Stift",
clear: "Leeren",
saveAndConvert: "Speichern + Konvertieren",
hide: "verstecken",
show: "anzeigen",
newChat: "neuer Chat",
loading: "Lade...",
directChildren: "Direkte Quellen",
indirectChildren: "Indirekte Quellen",
subsectionSummary: "Unterabschnitt-Zusammenfassung",
sectionSummary: "Abschnitt-Zusammenfassung",
untitled: "Ohne Titel",
inspect: "Ansehen",
cite: "Zitieren",
draw: "Zeichnen",
send: "Senden",
roleUser: "nutzer",
roleAssistant: "assistent",
noTasksAvailable: "Keine Aufgaben verfuegbar",
noTaskSelected: "Keine Aufgabe ausgewaehlt.",
savedChats: "Gespeicherte Chats",
saving: "Speichere...",
noSavedChatsYet: "Noch keine gespeicherten Chats.",
canvasHidden: "Canvas ausgeblendet",
failedLoadTasks: "Aufgaben konnten nicht geladen werden.",
chatRequestFailed:
"Chat-Anfrage ist fehlgeschlagen. Bitte pruefe die Backend-Logs.",
retrievalFailed:
"Quellenabruf ist fehlgeschlagen. Bitte pruefe die Backend-Logs.",
failedLoadSavedChats: "Gespeicherte Chats konnten nicht geladen werden.",
failedLoadSelectedChat: "Der ausgewaehlte Chat konnte nicht geladen werden.",
savingDrawingConverting:
"Zeichnung wird gespeichert und in LaTeX konvertiert...",
saveFailedStatus: "Speichern fehlgeschlagen (Status: {status}).",
canvasSaveFailed: "Speichern des Canvas fehlgeschlagen.",
canvasSaved: "Canvas gespeichert.",
canvasSaveFailedCheckLogs:
"Speichern des Canvas fehlgeschlagen. Bitte pruefe die Backend-Logs.",
},
} as const;
type Language = keyof typeof translations;
type TranslationKey = keyof (typeof translations)["en"];
const envLanguage = String(import.meta.env.VITE_FRONTEND_LANG || "en")
.trim()
.toLowerCase();
export const language: Language = envLanguage.startsWith("de") ? "de" : "en";
export const t = (
key: TranslationKey,
vars?: Record<string, string | number>
): string => {
let value: string = translations[language][key] || translations.en[key];
if (!vars) {
return value;
}
Object.entries(vars).forEach(([name, raw]) => {
value = value.replaceAll(`{${name}}`, String(raw));
});
return value;
};
...@@ -5,6 +5,7 @@ import CanvasDrawer from "../components/Canvas/CanvasDrawer"; ...@@ -5,6 +5,7 @@ import CanvasDrawer from "../components/Canvas/CanvasDrawer";
import DocPanel from "../components/Retrieval/DocPanel"; import DocPanel from "../components/Retrieval/DocPanel";
import type { ChatMessage } from "../components/Chat/MessageList"; import type { ChatMessage } from "../components/Chat/MessageList";
import type { RetrievedDoc } from "../components/Retrieval/DocPanel"; import type { RetrievedDoc } from "../components/Retrieval/DocPanel";
import { t } from "../i18n";
const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:8000"; const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:8000";
...@@ -252,7 +253,7 @@ export default function App() { ...@@ -252,7 +253,7 @@ export default function App() {
if (!isActive) { if (!isActive) {
return; return;
} }
setTasksError("Failed to load tasks."); setTasksError(t("failedLoadTasks"));
setIsTaskModeEnabled(false); setIsTaskModeEnabled(false);
setTaskFiles([]); setTaskFiles([]);
setSelectedTaskRef(null); setSelectedTaskRef(null);
...@@ -349,13 +350,14 @@ export default function App() { ...@@ -349,13 +350,14 @@ export default function App() {
} }
const payload: { reply?: string } = await response.json(); const payload: { reply?: string } = await response.json();
if (payload.reply) { const reply = payload.reply;
if (typeof reply === "string" && reply.length > 0) {
setMessages((prev) => [ setMessages((prev) => [
...prev, ...prev,
{ {
id: `m-${Date.now()}-assistant`, id: `m-${Date.now()}-assistant`,
role: "assistant", role: "assistant",
text: payload.reply, text: reply,
}, },
]); ]);
} }
...@@ -365,10 +367,10 @@ export default function App() { ...@@ -365,10 +367,10 @@ export default function App() {
{ {
id: `m-${Date.now()}-assistant`, id: `m-${Date.now()}-assistant`,
role: "assistant", role: "assistant",
text: "Chat request failed. Check the API logs.", text: t("chatRequestFailed"),
}, },
]); ]);
setRetrievalError("Retrieval failed. Check the API logs."); setRetrievalError(t("retrievalFailed"));
setRetrievalLoading(false); setRetrievalLoading(false);
void error; void error;
return; return;
...@@ -425,7 +427,7 @@ export default function App() { ...@@ -425,7 +427,7 @@ export default function App() {
setSubsections(nextSubsections); setSubsections(nextSubsections);
setSections(nextSections); setSections(nextSections);
} catch (error) { } catch (error) {
setRetrievalError("Retrieval failed. Check the API logs."); setRetrievalError(t("retrievalFailed"));
void error; void error;
} finally { } finally {
setRetrievalLoading(false); setRetrievalLoading(false);
...@@ -472,7 +474,7 @@ export default function App() { ...@@ -472,7 +474,7 @@ export default function App() {
setSelectedArchiveId(payload[0].chat_id); setSelectedArchiveId(payload[0].chat_id);
} }
} catch (error) { } catch (error) {
setArchiveError("Failed to load saved chats."); setArchiveError(t("failedLoadSavedChats"));
void error; void error;
} }
}; };
...@@ -495,7 +497,7 @@ export default function App() { ...@@ -495,7 +497,7 @@ export default function App() {
setMessages(payload.history || []); setMessages(payload.history || []);
setChatSessionId(payload.chat_id); setChatSessionId(payload.chat_id);
} catch (error) { } catch (error) {
setArchiveError("Failed to load selected chat."); setArchiveError(t("failedLoadSelectedChat"));
void error; void error;
} }
}; };
...@@ -535,7 +537,7 @@ export default function App() { ...@@ -535,7 +537,7 @@ export default function App() {
const handleCanvasSave = async (dataUrl: string) => { const handleCanvasSave = async (dataUrl: string) => {
setCanvasStatus({ setCanvasStatus({
kind: "info", kind: "info",
message: "Saving drawing and converting to LaTeX...", message: t("savingDrawingConverting"),
}); });
try { try {
...@@ -546,7 +548,7 @@ export default function App() { ...@@ -546,7 +548,7 @@ export default function App() {
}); });
if (!response.ok) { if (!response.ok) {
let message = `Save failed: ${response.status}`; let message = t("saveFailedStatus", { status: response.status });
try { try {
const errorPayload: { detail?: string } = await response.json(); const errorPayload: { detail?: string } = await response.json();
if (errorPayload.detail) { if (errorPayload.detail) {
...@@ -563,21 +565,20 @@ export default function App() { ...@@ -563,21 +565,20 @@ export default function App() {
if (payload.status && payload.status !== "ok") { if (payload.status && payload.status !== "ok") {
setCanvasStatus({ setCanvasStatus({
kind: "error", kind: "error",
message: payload.latex || "Canvas save failed.", message: payload.latex || t("canvasSaveFailed"),
}); });
return; return;
} }
if (payload.latex) { const latex = payload.latex;
setDraft((prev) => if (typeof latex === "string" && latex.length > 0) {
prev ? `${prev} ${payload.latex}` : payload.latex setDraft((prev) => (prev ? `${prev} ${latex}` : latex));
); setCanvasStatus({ kind: "ok", message: t("canvasSaved") });
setCanvasStatus({ kind: "ok", message: "Canvas saved." });
setIsCanvasVisible(false); setIsCanvasVisible(false);
} }
} catch (error) { } catch (error) {
setCanvasStatus({ setCanvasStatus({
kind: "error", kind: "error",
message: "Canvas save failed. Check the API logs.", message: t("canvasSaveFailedCheckLogs"),
}); });
void error; void error;
} }
...@@ -593,7 +594,7 @@ export default function App() { ...@@ -593,7 +594,7 @@ export default function App() {
doc.metadata.subsection_title || doc.metadata.subsection_title ||
doc.metadata.section_title || doc.metadata.section_title ||
doc.metadata.path || doc.metadata.path ||
"Document"; t("untitled");
const html = `<!doctype html> const html = `<!doctype html>
<html lang="en"> <html lang="en">
...@@ -655,13 +656,13 @@ export default function App() { ...@@ -655,13 +656,13 @@ export default function App() {
className="btn sidebar-toggle" className="btn sidebar-toggle"
onClick={handleOpenSidebar} onClick={handleOpenSidebar}
> >
Chats {t("chats")}
</button> </button>
<div className="brand"> <div className="brand">
<span className="brand-mark">SUM</span> <span className="brand-mark">SUM</span>
<div className="brand-text"> <div className="brand-text">
<div className="brand-title">Math Tutor</div> <div className="brand-title">Math Tutor</div>
<div className="brand-subtitle">Prototype Workspace</div> <div className="brand-subtitle">{t("prototypeWorkspace")}</div>
</div> </div>
</div> </div>
<div className="header-meta"> <div className="header-meta">
...@@ -676,7 +677,7 @@ export default function App() { ...@@ -676,7 +677,7 @@ export default function App() {
{isTaskModeEnabled ? ( {isTaskModeEnabled ? (
<section className="task-panel"> <section className="task-panel">
<div className="task-panel-header"> <div className="task-panel-header">
<div className="task-panel-title">Task</div> <div className="task-panel-title">{t("task")}</div>
<select <select
className="task-select" className="task-select"
value={ value={
...@@ -694,12 +695,12 @@ export default function App() { ...@@ -694,12 +695,12 @@ export default function App() {
</option> </option>
)) ))
) : ( ) : (
<option value="">No tasks available</option> <option value="">{t("noTasksAvailable")}</option>
)} )}
</select> </select>
</div> </div>
<div className="task-panel-content" ref={taskDisplayRef}> <div className="task-panel-content" ref={taskDisplayRef}>
{selectedTask?.fullText || "No task selected."} {selectedTask?.fullText || t("noTaskSelected")}
</div> </div>
{tasksError ? <div className="task-panel-error">{tasksError}</div> : null} {tasksError ? <div className="task-panel-error">{tasksError}</div> : null}
</section> </section>
...@@ -742,7 +743,7 @@ export default function App() { ...@@ -742,7 +743,7 @@ export default function App() {
</main> </main>
<div className={`sidebar ${isSidebarOpen ? "active" : ""}`}> <div className={`sidebar ${isSidebarOpen ? "active" : ""}`}>
<div className="sd-header"> <div className="sd-header">
<h4 className="sd-title">Saved Chats</h4> <h4 className="sd-title">{t("savedChats")}</h4>
<button <button
type="button" type="button"
className="sidebar-button" className="sidebar-button"
...@@ -760,7 +761,7 @@ export default function App() { ...@@ -760,7 +761,7 @@ export default function App() {
onClick={handleNewChat} onClick={handleNewChat}
disabled={isArchiving} disabled={isArchiving}
> >
{isArchiving ? "Saving..." : "New Chat"} {isArchiving ? t("saving") : t("newChat")}
</button> </button>
</li> </li>
<li> <li>
...@@ -785,7 +786,7 @@ export default function App() { ...@@ -785,7 +786,7 @@ export default function App() {
)) ))
) : ( ) : (
<li> <li>
<div className="sd-empty">No saved chats yet.</div> <div className="sd-empty">{t("noSavedChatsYet")}</div>
</li> </li>
)} )}
</ul> </ul>
......
...@@ -366,13 +366,14 @@ body { ...@@ -366,13 +366,14 @@ body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
flex: 0 0 320px; flex: 0 0 auto;
} }
.canvas-header { .canvas-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; gap: 8px;
flex-wrap: wrap;
} }
.canvas-body { .canvas-body {
...@@ -411,8 +412,8 @@ body { ...@@ -411,8 +412,8 @@ body {
.canvas-board { .canvas-board {
width: 100%; width: 100%;
height: 100%; height: 60%;
min-height: 200px; min-height: 100px;
display: block; display: block;
cursor: crosshair; cursor: crosshair;
} }
...@@ -420,6 +421,11 @@ body { ...@@ -420,6 +421,11 @@ body {
.canvas-actions { .canvas-actions {
display: flex; display: flex;
gap: 8px; gap: 8px;
flex-wrap: wrap;
}
.canvas-actions-header {
margin-left: auto;
} }
.canvas-hidden { .canvas-hidden {
...@@ -549,7 +555,7 @@ body { ...@@ -549,7 +555,7 @@ body {
flex: 1 1 120px; flex: 1 1 120px;
} }
.canvas-actions { .canvas-actions-header {
flex-direction: column; margin-left: 0;
} }
} }
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment