Remove unused components and models for improved maintainability

- Deleted redundant components (`AddActionButton`, `AlertBox`, `AlertStack`, `BackButton`, `CancelButton`, and `CollapsableArea`) and related files.
- Removed unused models (`Book`, `BookSerie`, `BookTables`, `Character`, and `Chapter`) to reduce codebase clutter.
- Updated project structure and references to reflect these removals.
This commit is contained in:
natreex
2026-03-22 22:37:31 -04:00
parent e8aaef108b
commit 64ed90d993
229 changed files with 15091 additions and 21289 deletions

View File

@@ -1,48 +1,44 @@
'use client'
import {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
import {EditorContent} from '@tiptap/react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import React, {useCallback, useContext, useEffect, useRef, useState} from 'react';
import {EditorContent, JSONContent} from '@tiptap/react';
import {
faAlignCenter,
faAlignLeft,
faAlignRight,
faBold,
faCog,
faFloppyDisk,
faGhost,
faHeading,
faLayerGroup,
faListOl,
faListUl,
faParagraph,
faUnderline,
faXmark
} from '@fortawesome/free-solid-svg-icons';
import {EditorContext} from "@/context/EditorContext";
import {ChapterContext} from '@/context/ChapterContext';
import System from '@/lib/models/System';
import {AlertContext} from '@/context/AlertContext';
import {SessionContext} from "@/context/SessionContext";
import {BookContext} from '@/context/BookContext';
AlignCenter,
AlignJustify,
AlignLeft,
AlignRight,
Bold,
Ghost,
Heading1,
Heading2,
Heading3,
Layers,
List,
ListOrdered,
LucideIcon,
Save,
SlidersHorizontal,
Underline
} from 'lucide-react';
import {EditorContext, EditorContextProps} from "@/context/EditorContext";
import {ChapterContext, ChapterContextProps} from '@/context/ChapterContext';
import {BookContext, BookContextProps} from "@/context/BookContext";
import {apiPost} from '@/lib/api/client';
import {isDesktop} from '@/lib/configs';
import * as tauri from '@/lib/tauri';
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
import {AlertContext, AlertContextProps} from '@/context/AlertContext';
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
import DraftCompanion from "@/components/editor/DraftCompanion";
import GhostWriter from "@/components/ghostwriter/GhostWriter";
import SubmitButtonWLoading from "@/components/form/SubmitButtonWLoading";
import CollapsableButton from "@/components/CollapsableButton";
import {IconDefinition} from "@fortawesome/fontawesome-svg-core";
import IconButton from "@/components/ui/IconButton";
import UserEditorSettings, {EditorDisplaySettings} from "@/components/editor/UserEditorSetting";
import {useTranslations} from "next-intl";
import {useTranslations} from '@/lib/i18n';
import {LangContext, LangContextProps} from "@/context/LangContext";
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
import {LocalSyncQueueContext, LocalSyncQueueContextProps} from "@/context/SyncQueueContext";
import {BooksSyncContext, BooksSyncContextProps} from "@/context/BooksSyncContext";
import {SyncedBook} from "@/lib/models/SyncedBook";
import * as tauri from '@/lib/tauri';
interface ToolbarButton {
action: () => void;
icon: IconDefinition;
icon: LucideIcon;
isActive: boolean;
label?: string;
}
interface EditorClasses {
@@ -57,7 +53,7 @@ interface EditorClasses {
listItems: string;
}
const DEFAULT_EDITOR_SETTINGS: EditorDisplaySettings = {
const defaultEditorSettings: EditorDisplaySettings = {
zoomLevel: 3,
indent: 30,
lineHeight: 1.5,
@@ -67,53 +63,53 @@ const DEFAULT_EDITOR_SETTINGS: EditorDisplaySettings = {
focusMode: false
};
const FONT_SIZE_CLASSES = {
const fontSizeClasses: Record<number, string> = {
1: 'text-sm',
2: 'text-base',
3: 'text-lg',
4: 'text-xl',
5: 'text-2xl'
} as const;
};
const H1_SIZE_CLASSES = {
const h1SizeClasses: Record<number, string> = {
1: 'text-xl',
2: 'text-2xl',
3: 'text-3xl',
4: 'text-4xl',
5: 'text-5xl'
} as const;
};
const H2_SIZE_CLASSES = {
const h2SizeClasses: Record<number, string> = {
1: 'text-lg',
2: 'text-xl',
3: 'text-2xl',
4: 'text-3xl',
5: 'text-4xl'
} as const;
};
const H3_SIZE_CLASSES = {
const h3SizeClasses: Record<number, string> = {
1: 'text-base',
2: 'text-lg',
3: 'text-xl',
4: 'text-2xl',
5: 'text-3xl'
} as const;
};
const FONT_FAMILY_CLASSES = {
const fontFamilyClasses: Record<string, string> = {
'lora': 'Lora',
'serif': 'font-serif',
'sans-serif': 'font-sans',
'monospace': 'font-mono'
} as const;
};
const LINE_HEIGHT_CLASSES = {
const lineHeightClasses: Record<number, string> = {
1.2: 'leading-tight',
1.5: 'leading-normal',
1.75: 'leading-relaxed',
2: 'leading-loose'
} as const;
};
const MAX_WIDTH_CLASSES = {
const maxWidthClasses: Record<number, string> = {
600: 'max-w-xl',
650: 'max-w-2xl',
700: 'max-w-3xl',
@@ -127,9 +123,9 @@ const MAX_WIDTH_CLASSES = {
1100: 'max-w-full',
1150: 'max-w-full',
1200: 'max-w-full'
} as const;
};
function getClosestKey<T extends Record<number, any>>(value: number, obj: T): keyof T {
function getClosestKey(value: number, obj: Record<number, string>): number {
const keys: number[] = Object.keys(obj).map(Number).sort((a: number, b: number): number => a - b);
return keys.reduce((prev: number, curr: number): number =>
Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev
@@ -138,30 +134,27 @@ function getClosestKey<T extends Record<number, any>>(value: number, obj: T): ke
export default function TextEditor() {
const t = useTranslations();
const {lang} = useContext<LangContextProps>(LangContext)
const {editor} = useContext(EditorContext);
const {chapter, setChapter} = useContext(ChapterContext);
const {book, setBook} = useContext(BookContext);
const {errorMessage, successMessage} = useContext(AlertContext);
const {session} = useContext(SessionContext);
const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
const {addToQueue} = useContext<LocalSyncQueueContextProps>(LocalSyncQueueContext);
const {localSyncedBooks} = useContext<BooksSyncContextProps>(BooksSyncContext);
const {lang}: LangContextProps = useContext<LangContextProps>(LangContext)
const {editor}: EditorContextProps = useContext<EditorContextProps>(EditorContext);
const {chapter}: ChapterContextProps = useContext<ChapterContextProps>(ChapterContext);
const {book}: BookContextProps = useContext<BookContextProps>(BookContext);
const {errorMessage, successMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
const {session}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
const {isCurrentlyOffline}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
const [mainTimer, setMainTimer] = useState<number>(0);
const [showDraftCompanion, setShowDraftCompanion] = useState<boolean>(false);
const [showGhostWriter, setShowGhostWriter] = useState<boolean>(false);
const [showUserSettings, setShowUserSettings] = useState<boolean>(false);
const [isSaving, setIsSaving] = useState<boolean>(false);
const [isClosing, setIsClosing] = useState<boolean>(false);
const [editorSettings, setEditorSettings] = useState<EditorDisplaySettings>(DEFAULT_EDITOR_SETTINGS);
const [editorSettings, setEditorSettings] = useState<EditorDisplaySettings>(defaultEditorSettings);
const [editorClasses, setEditorClasses] = useState<EditorClasses>({
base: 'text-lg font-serif leading-normal',
h1: 'text-3xl font-bold',
h2: 'text-2xl font-bold',
h3: 'text-xl font-bold',
container: 'max-w-3xl',
theme: 'bg-tertiary/90 backdrop-blur-sm bg-opacity-25 text-text-primary',
theme: 'bg-tertiary text-text-primary',
paragraph: 'indent-6',
lists: 'pl-10',
listItems: 'text-lg'
@@ -171,33 +164,31 @@ export default function TextEditor() {
const timeoutRef: React.RefObject<number | null> = useRef<number | null>(null);
const updateEditorClasses: (settings: EditorDisplaySettings) => void = useCallback((settings: EditorDisplaySettings): void => {
const fontSizeKey = settings.zoomLevel as keyof typeof FONT_SIZE_CLASSES;
const h1SizeKey = settings.zoomLevel as keyof typeof H1_SIZE_CLASSES;
const h2SizeKey = settings.zoomLevel as keyof typeof H2_SIZE_CLASSES;
const h3SizeKey = settings.zoomLevel as keyof typeof H3_SIZE_CLASSES;
const fontFamilyKey = settings.fontFamily as keyof typeof FONT_FAMILY_CLASSES;
const lineHeightKey = settings.lineHeight as keyof typeof LINE_HEIGHT_CLASSES;
const maxWidthKey: number = getClosestKey(settings.maxWidth, MAX_WIDTH_CLASSES);
const zoomKey: number = getClosestKey(settings.zoomLevel, fontSizeClasses);
const lineHeightKey: number = getClosestKey(settings.lineHeight, lineHeightClasses);
const maxWidthKey: number = getClosestKey(settings.maxWidth, maxWidthClasses);
const indentClass = `indent-${Math.round(settings.indent / 4)}`;
const fontFamily: string = fontFamilyClasses[settings.fontFamily] || fontFamilyClasses['lora'];
const lineHeight: string = lineHeightClasses[lineHeightKey];
const indentClass: string = `indent-${Math.round(settings.indent / 4)}`;
const baseClass = `${FONT_SIZE_CLASSES[fontSizeKey]} ${FONT_FAMILY_CLASSES[fontFamilyKey]} ${LINE_HEIGHT_CLASSES[lineHeightKey]}`;
const h1Class = `${H1_SIZE_CLASSES[h1SizeKey]} font-bold ${FONT_FAMILY_CLASSES[fontFamilyKey]} ${LINE_HEIGHT_CLASSES[lineHeightKey]}`;
const h2Class = `${H2_SIZE_CLASSES[h2SizeKey]} font-bold ${FONT_FAMILY_CLASSES[fontFamilyKey]} ${LINE_HEIGHT_CLASSES[lineHeightKey]}`;
const h3Class = `${H3_SIZE_CLASSES[h3SizeKey]} font-bold ${FONT_FAMILY_CLASSES[fontFamilyKey]} ${LINE_HEIGHT_CLASSES[lineHeightKey]}`;
const containerClass = MAX_WIDTH_CLASSES[maxWidthKey as keyof typeof MAX_WIDTH_CLASSES];
const listsClass = `pl-${Math.round((settings.indent + 20) / 4)}`;
const baseClass: string = `${fontSizeClasses[zoomKey]} ${fontFamily} ${lineHeight}`;
const h1Class: string = `${h1SizeClasses[zoomKey]} font-bold ${fontFamily} ${lineHeight}`;
const h2Class: string = `${h2SizeClasses[zoomKey]} font-bold ${fontFamily} ${lineHeight}`;
const h3Class: string = `${h3SizeClasses[zoomKey]} font-bold ${fontFamily} ${lineHeight}`;
const containerClass: string = maxWidthClasses[maxWidthKey];
const listsClass: string = `pl-${Math.round((settings.indent + 20) / 4)}`;
let themeClass: string = '';
switch (settings.theme) {
case 'clair':
themeClass = 'bg-white text-black';
themeClass = 'bg-gray-light text-darkest-background';
break;
case 'sépia':
themeClass = 'text-amber-900';
themeClass = 'bg-editor-page-sepia text-darkest-background';
break;
default:
themeClass = 'bg-tertiary/90 backdrop-blur-sm bg-opacity-25 text-text-primary';
themeClass = 'bg-tertiary text-text-primary';
}
setEditorClasses({
@@ -213,109 +204,99 @@ export default function TextEditor() {
});
}, []);
const containerStyle = useMemo(() => {
if (editorSettings.theme === 'sépia') {
return {backgroundColor: '#f4f1e8'};
}
return {};
}, [editorSettings.theme]);
const editorContainerRef: React.RefObject<HTMLDivElement | null> = useRef<HTMLDivElement>(null);
const toolbarButtons: ToolbarButton[] = (() => {
const editorContainerBgClass: string =
editorSettings.theme === 'sépia' ? 'bg-editor-page-sepia' : '';
const toolbarButtons: ToolbarButton[] = ((): ToolbarButton[] => {
if (!editor) return [];
return [
{
action: (): boolean => editor.chain().focus().setParagraph().run(),
icon: faParagraph,
icon: AlignJustify,
isActive: editor.isActive('paragraph')
},
{
action: (): boolean => editor.chain().focus().toggleBold().run(),
icon: faBold,
icon: Bold,
isActive: editor.isActive('bold')
},
{
action: (): boolean => editor.chain().focus().toggleUnderline().run(),
icon: faUnderline,
icon: Underline,
isActive: editor.isActive('underline')
},
{
action: (): boolean => editor.chain().focus().setTextAlign('left').run(),
icon: faAlignLeft,
icon: AlignLeft,
isActive: editor.isActive({textAlign: 'left'})
},
{
action: (): boolean => editor.chain().focus().setTextAlign('center').run(),
icon: faAlignCenter,
icon: AlignCenter,
isActive: editor.isActive({textAlign: 'center'})
},
{
action: (): boolean => editor.chain().focus().setTextAlign('right').run(),
icon: faAlignRight,
icon: AlignRight,
isActive: editor.isActive({textAlign: 'right'})
},
{
action: (): boolean => editor.chain().focus().toggleBulletList().run(),
icon: faListUl,
icon: List,
isActive: editor.isActive('bulletList')
},
{
action: (): boolean => editor.chain().focus().toggleOrderedList().run(),
icon: faListOl,
icon: ListOrdered,
isActive: editor.isActive('orderedList')
},
{
action: (): boolean => editor.chain().focus().toggleHeading({level: 1}).run(),
icon: faHeading,
isActive: editor.isActive('heading', {level: 1}),
label: '1'
icon: Heading1,
isActive: editor.isActive('heading', {level: 1})
},
{
action: (): boolean => editor.chain().focus().toggleHeading({level: 2}).run(),
icon: faHeading,
isActive: editor.isActive('heading', {level: 2}),
label: '2'
icon: Heading2,
isActive: editor.isActive('heading', {level: 2})
},
{
action: (): boolean => editor.chain().focus().toggleHeading({level: 3}).run(),
icon: faHeading,
isActive: editor.isActive('heading', {level: 3}),
label: '3'
icon: Heading3,
isActive: editor.isActive('heading', {level: 3})
},
];
})();
const saveContent: () => Promise<void> = useCallback(async (): Promise<void> => {
if (!editor || !chapter) return;
setIsSaving(true);
const content = editor.state.doc.toJSON();
const content: Record<string, unknown> = editor.state.doc.toJSON();
const chapterId: string = chapter.chapterId || '';
const version: number = chapter.chapterContent.version || 0;
try {
let response: boolean;
const saveData = {
chapterId,
version,
content,
totalWordCount: editor.getText().length,
currentTime: mainTimer
};
if (isCurrentlyOffline() || book?.localBook){
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
response = await tauri.saveChapterContent({
chapterId: saveData.chapterId,
version: saveData.version,
content: saveData.content,
totalWordCount: saveData.totalWordCount,
contentId: saveData.chapterId,
chapterId,
version,
content,
totalWordCount: editor.getText().length,
contentId: '',
});
} else {
response = await System.authPostToServer<boolean>(`chapter/content`, saveData, session?.accessToken, lang);
if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === book?.bookId)) {
addToQueue('save_chapter_content', {data: saveData});
}
response = await apiPost<boolean>(`chapter/content`, {
chapterId,
version,
content,
totalWordCount: editor.getText().length,
currentTime: mainTimer
}, session?.accessToken ?? '');
}
if (!response) {
errorMessage(t('editor.error.savedFailed'));
@@ -333,7 +314,7 @@ export default function TextEditor() {
}
setIsSaving(false);
}
}, [editor, chapter, mainTimer, session?.accessToken, successMessage, errorMessage, addToQueue, book?.localBook, isCurrentlyOffline]);
}, [editor, chapter, mainTimer, session?.accessToken, successMessage, errorMessage]);
const handleShowDraftCompanion: () => void = useCallback((): void => {
setShowDraftCompanion((prev: boolean): boolean => !prev);
@@ -354,21 +335,13 @@ export default function TextEditor() {
setShowDraftCompanion(false);
setShowGhostWriter(false);
}, []);
const handleCloseBook: () => Promise<void> = useCallback(async (): Promise<void> => {
setIsClosing(true);
await saveContent();
setBook && setBook(null);
setChapter && setChapter(undefined);
setIsClosing(false);
}, [saveContent, setBook, setChapter]);
useEffect((): void => {
if (!editor) return;
const editorElement: HTMLElement = editor.view.dom;
if (editorElement) {
const indentClasses: string[] = Array.from({length: 21}, (_, i) => `indent-${i}`);
const indentClasses: string[] = Array.from({length: 21}, (_: unknown, i: number): string => `indent-${i}`);
editorElement.classList.remove(...indentClasses);
if (editorClasses.paragraph) {
@@ -384,8 +357,8 @@ export default function TextEditor() {
useEffect((): () => void => {
function startTimer(): void {
if (timerRef.current === null) {
timerRef.current = window.setInterval(() => {
setMainTimer(prevTimer => prevTimer + 1);
timerRef.current = window.setInterval((): void => {
setMainTimer((prevTimer: number): number => prevTimer + 1);
}, 1000);
}
}
@@ -431,9 +404,10 @@ export default function TextEditor() {
if (!editor) return;
if (chapter?.chapterContent.content) {
try {
const parsedContent = JSON.parse(chapter.chapterContent.content);
const parsedContent: JSONContent = JSON.parse(chapter.chapterContent.content);
editor.commands.setContent(parsedContent);
} catch (error) {
} catch (e: unknown) {
errorMessage(t('editor.error.parsingContent'));
editor.commands.setContent({
type: "doc",
content: [{type: "paragraph", content: []}]
@@ -463,102 +437,86 @@ export default function TextEditor() {
}
return (
<div className="flex flex-col flex-1 w-full h-full">
<div className="flex flex-col flex-1 w-full h-full bg-tertiary">
<div
className={`flex justify-between gap-2 lg:gap-3 border-b border-secondary/30 px-2 lg:px-4 py-2 lg:py-3 bg-gradient-to-b from-dark-background/80 to-dark-background/50 backdrop-blur-sm transition-opacity duration-300 shadow-md overflow-x-auto ${editorSettings.focusMode ? 'opacity-70 hover:opacity-100' : ''}`}>
<div className="flex gap-1 flex-shrink-0">
className={`flex justify-between items-center gap-3 rounded-xl mx-1 mb-1 px-4 py-2 bg-darkest-background transition-opacity duration-300 ${editorSettings.focusMode ? 'opacity-70 hover:opacity-100' : ''}`}>
<div className="flex flex-wrap gap-1">
{toolbarButtons.map((button: ToolbarButton, index: number) => (
<button
<IconButton
key={index}
icon={button.icon}
variant="ghost"
shape="square"
selected={button.isActive}
onClick={button.action}
className={`group flex items-center px-2 lg:px-3 py-1.5 lg:py-2 rounded-lg transition-all duration-200 flex-shrink-0 ${button.isActive ? 'bg-primary text-text-primary shadow-md shadow-primary/30 scale-105' : 'text-muted hover:text-text-primary hover:bg-secondary/50 hover:shadow-sm hover:scale-105'}`}
>
<FontAwesomeIcon icon={button.icon} className={'w-3.5 h-3.5 lg:w-4 lg:h-4 transition-transform duration-200 group-hover:scale-110'}/>
{
button.label &&
<span className="ml-1 lg:ml-2 text-xs lg:text-sm font-medium">
{t(`textEditor.toolbar.${button.label}`)}
</span>
}
</button>
/>
))}
</div>
<div className="flex items-center gap-2 flex-shrink-0">
<CollapsableButton
showCollapsable={showUserSettings}
text={t("textEditor.preferences")}
<div className="flex items-center gap-1">
<IconButton
icon={SlidersHorizontal}
variant="ghost"
shape="square"
selected={showUserSettings}
onClick={handleShowUserSettings}
icon={faCog}
tooltip={t("textEditor.preferences")}
/>
{chapter?.chapterContent.version === 2 && !isCurrentlyOffline() && !book?.localBook && book?.quillsenseEnabled !== false && (
<CollapsableButton
showCollapsable={showGhostWriter}
text={t("textEditor.ghostWriter")}
{chapter?.chapterContent.version === 2 && book?.quillsenseEnabled !== false && (
<IconButton
icon={Ghost}
variant="ghost"
shape="square"
selected={showGhostWriter}
onClick={handleShowGhostWriter}
icon={faGhost}
tooltip={t("textEditor.ghostWriter")}
/>
)}
{chapter?.chapterContent.version && chapter.chapterContent.version > 2 && (
<CollapsableButton
showCollapsable={showDraftCompanion}
text={t("textEditor.draftCompanion")}
<IconButton
icon={Layers}
variant="ghost"
shape="square"
selected={showDraftCompanion}
onClick={handleShowDraftCompanion}
icon={faLayerGroup}
tooltip={t("textEditor.draftCompanion")}
/>
)}
<button
<IconButton
icon={Save}
variant="ghost"
shape="square"
onClick={saveContent}
disabled={isSaving}
className={`group py-2.5 px-3 lg:px-5 rounded-lg font-semibold transition-all flex items-center justify-center gap-2 relative overflow-hidden ${
isSaving
? 'bg-secondary cursor-not-allowed opacity-75'
: 'bg-secondary/80 hover:bg-secondary shadow-md hover:shadow-lg hover:shadow-primary/20 hover:scale-105 border border-secondary/50 hover:border-primary/30'
}`}
>
<FontAwesomeIcon icon={faFloppyDisk} className="w-4 h-4 transition-transform group-hover:scale-110 text-primary" />
<span className="hidden lg:inline text-sm text-primary">{isSaving ? t("textEditor.saving") : t("textEditor.save")}</span>
</button>
<button
onClick={handleCloseBook}
disabled={isClosing}
className={`group py-2.5 px-3 lg:px-5 rounded-lg font-semibold transition-all flex items-center justify-center gap-2 relative overflow-hidden ${
isClosing
? 'bg-secondary/30 cursor-not-allowed opacity-75'
: 'bg-secondary/30 text-muted hover:text-text-primary hover:bg-secondary hover:shadow-sm hover:scale-105'
}`}
>
<FontAwesomeIcon icon={faXmark} className="w-4 h-4 transition-transform duration-200 group-hover:scale-110" />
<span className="hidden lg:inline text-sm">{t("textEditor.close")}</span>
</button>
tooltip={t("textEditor.save")}
/>
</div>
</div>
<div className="flex justify-between w-full h-full overflow-auto">
<div className="flex flex-1 min-h-0 bg-tertiary">
<div
className={`flex-1 p-8 overflow-auto transition-all duration-300 ${editorSettings.focusMode ? 'bg-black/20' : ''}`}>
<div
className={`editor-container mx-auto p-6 rounded-2xl shadow-2xl min-h-[80%] border border-secondary/50 ${editorClasses.container} ${editorClasses.theme} relative`}
style={containerStyle}>
className={`flex-1 bg-background rounded-xl mx-1 overflow-hidden transition-all duration-300 ${editorSettings.focusMode ? 'bg-darkest-background' : ''}`}>
<div className="h-full overflow-auto p-4">
<div
className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-primary/30 to-transparent"></div>
<div
className="absolute bottom-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-primary/30 to-transparent"></div>
<EditorContent className={`w-full h-full ${editorClasses.base} editor-content`}
editor={editor}/>
ref={editorContainerRef}
className={`editor-container mx-auto p-4 rounded-xl min-h-full ${editorClasses.container} ${editorClasses.theme} ${editorContainerBgClass}`}>
<EditorContent className={`w-full h-full ${editorClasses.base} editor-content`}
editor={editor}/>
</div>
</div>
</div>
{(showDraftCompanion || showGhostWriter || showUserSettings) && (
<div
className={`w-4/12 transition-opacity duration-300 ${editorSettings.focusMode ? 'opacity-50 hover:opacity-100' : ''}`}>
{showDraftCompanion && <DraftCompanion/>}
{showGhostWriter && <GhostWriter/>}
{showUserSettings && (
<UserEditorSettings
settings={editorSettings}
onSettingsChange={setEditorSettings}
/>
)}
className={`w-4/12 bg-darkest-background rounded-xl mx-1 overflow-hidden flex flex-col transition-opacity duration-300 ${editorSettings.focusMode ? 'opacity-50 hover:opacity-100' : ''}`}>
<div className="flex-1 overflow-auto flex flex-col">
{showDraftCompanion && <DraftCompanion/>}
{showGhostWriter && <GhostWriter/>}
{showUserSettings && (
<UserEditorSettings
settings={editorSettings}
onSettingsChange={setEditorSettings}
/>
)}
</div>
</div>
)}
</div>