Bump app version to 0.5.0 and implement offline mode support across components
- Added offline detection logic with `OfflineContext` to improve app functionality in offline scenarios. - Integrated Tauri IPC functions to handle local tool settings and character attributes when offline. - Refined indentation logic in `TextEditor` for better compatibility with WebKit engines. - Removed unused `indent` property and related settings in editor components to simplify configuration. - Updated locale files with improved translation consistency and parameterized placeholders.
This commit is contained in:
@@ -31,9 +31,14 @@ import {SessionContext, SessionContextProps} from "@/context/SessionContext";
|
||||
import DraftCompanion from "@/components/editor/DraftCompanion";
|
||||
import GhostWriter from "@/components/ghostwriter/GhostWriter";
|
||||
import IconButton from "@/components/ui/IconButton";
|
||||
import Button from "@/components/ui/Button";
|
||||
import UserEditorSettings, {EditorDisplaySettings} from "@/components/editor/UserEditorSetting";
|
||||
import {useTranslations} from '@/lib/i18n';
|
||||
import {LangContext, LangContextProps} from "@/context/LangContext";
|
||||
import {isWebKitWithoutIndentFix} from "@/lib/utils/webkitDetect";
|
||||
import {getCookie, setCookie} from "@/lib/utils/cookies";
|
||||
import Modal from "@/components/ui/Modal";
|
||||
import {Info} from 'lucide-react';
|
||||
|
||||
interface ToolbarButton {
|
||||
action: () => void;
|
||||
@@ -48,14 +53,12 @@ interface EditorClasses {
|
||||
h3: string;
|
||||
container: string;
|
||||
theme: string;
|
||||
paragraph: string;
|
||||
lists: string;
|
||||
listItems: string;
|
||||
}
|
||||
|
||||
const defaultEditorSettings: EditorDisplaySettings = {
|
||||
zoomLevel: 3,
|
||||
indent: 30,
|
||||
lineHeight: 1.5,
|
||||
theme: 'sombre',
|
||||
fontFamily: 'lora',
|
||||
@@ -148,6 +151,8 @@ export default function TextEditor() {
|
||||
const [showUserSettings, setShowUserSettings] = useState<boolean>(false);
|
||||
const [isSaving, setIsSaving] = useState<boolean>(false);
|
||||
const [editorSettings, setEditorSettings] = useState<EditorDisplaySettings>(defaultEditorSettings);
|
||||
const [indentDisabled] = useState<boolean>(() => isWebKitWithoutIndentFix());
|
||||
const [showIndentModal, setShowIndentModal] = useState<boolean>(() => isWebKitWithoutIndentFix() && !getCookie('indent_notice_seen'));
|
||||
const [editorClasses, setEditorClasses] = useState<EditorClasses>({
|
||||
base: 'text-lg font-serif leading-normal',
|
||||
h1: 'text-3xl font-bold',
|
||||
@@ -155,7 +160,6 @@ export default function TextEditor() {
|
||||
h3: 'text-xl font-bold',
|
||||
container: 'max-w-3xl',
|
||||
theme: 'bg-tertiary text-text-primary',
|
||||
paragraph: 'indent-6',
|
||||
lists: 'pl-10',
|
||||
listItems: 'text-lg'
|
||||
});
|
||||
@@ -170,14 +174,12 @@ export default function TextEditor() {
|
||||
|
||||
const fontFamily: string = fontFamilyClasses[settings.fontFamily] || fontFamilyClasses['lora'];
|
||||
const lineHeight: string = lineHeightClasses[lineHeightKey];
|
||||
const indentClass: string = `indent-${Math.round(settings.indent / 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)}`;
|
||||
const listsClass: string = 'pl-12';
|
||||
|
||||
let themeClass: string = '';
|
||||
switch (settings.theme) {
|
||||
@@ -198,7 +200,6 @@ export default function TextEditor() {
|
||||
h3: h3Class,
|
||||
container: containerClass,
|
||||
theme: themeClass,
|
||||
paragraph: indentClass,
|
||||
lists: listsClass,
|
||||
listItems: baseClass
|
||||
});
|
||||
@@ -335,24 +336,16 @@ export default function TextEditor() {
|
||||
setShowDraftCompanion(false);
|
||||
setShowGhostWriter(false);
|
||||
}, []);
|
||||
|
||||
useEffect((): void => {
|
||||
if (!editor) return;
|
||||
|
||||
const editorElement: HTMLElement = editor.view.dom;
|
||||
if (editorElement) {
|
||||
const indentClasses: string[] = Array.from({length: 21}, (_: unknown, i: number): string => `indent-${i}`);
|
||||
editorElement.classList.remove(...indentClasses);
|
||||
|
||||
if (editorClasses.paragraph) {
|
||||
editorElement.classList.add(editorClasses.paragraph);
|
||||
}
|
||||
}
|
||||
}, [editor, editorClasses.paragraph]);
|
||||
|
||||
const handleCloseIndentModal: () => void = useCallback((): void => {
|
||||
setCookie('indent_notice_seen', 'true', 365);
|
||||
setShowIndentModal(false);
|
||||
}, []);
|
||||
|
||||
useEffect((): void => {
|
||||
updateEditorClasses(editorSettings);
|
||||
}, [editorSettings, updateEditorClasses]);
|
||||
|
||||
|
||||
useEffect((): () => void => {
|
||||
function startTimer(): void {
|
||||
@@ -437,7 +430,7 @@ export default function TextEditor() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-1 w-full h-full bg-tertiary">
|
||||
<div className={`flex flex-col flex-1 w-full h-full bg-tertiary ${indentDisabled ? 'no-text-indent' : ''}`}>
|
||||
<div
|
||||
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">
|
||||
@@ -481,6 +474,15 @@ export default function TextEditor() {
|
||||
tooltip={t("textEditor.draftCompanion")}
|
||||
/>
|
||||
)}
|
||||
{indentDisabled && (
|
||||
<IconButton
|
||||
icon={Info}
|
||||
variant="ghost"
|
||||
shape="square"
|
||||
onClick={(): void => setShowIndentModal(true)}
|
||||
tooltip={t("textEditor.indentDisabled")}
|
||||
/>
|
||||
)}
|
||||
<IconButton
|
||||
icon={Save}
|
||||
variant="ghost"
|
||||
@@ -520,6 +522,23 @@ export default function TextEditor() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{showIndentModal && (
|
||||
<Modal
|
||||
title={t("textEditor.indentDisabledTitle")}
|
||||
icon={Info}
|
||||
size="sm"
|
||||
onClose={handleCloseIndentModal}
|
||||
footer={
|
||||
<Button variant="primary" onClick={handleCloseIndentModal}>
|
||||
{t("textEditor.indentDisabledUnderstood")}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<p className="text-text-secondary leading-relaxed">
|
||||
{t("textEditor.indentDisabledDescription")}
|
||||
</p>
|
||||
</Modal>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import React, {ChangeEvent, useCallback, useContext, useEffect, useMemo} from 'react';
|
||||
import {Baseline, CaseSensitive, Eye, Indent, Palette, Type} from 'lucide-react';
|
||||
import {Baseline, CaseSensitive, Eye, Palette, Type} from 'lucide-react';
|
||||
import {useTranslations} from '@/lib/i18n';
|
||||
import SelectBox from "@/components/form/SelectBox";
|
||||
import Button from "@/components/ui/Button";
|
||||
@@ -13,7 +13,6 @@ interface UserEditorSettingsProps {
|
||||
|
||||
export interface EditorDisplaySettings {
|
||||
zoomLevel: number;
|
||||
indent: number;
|
||||
lineHeight: number;
|
||||
theme: 'clair' | 'sombre' | 'sépia';
|
||||
fontFamily: 'lora' | 'serif' | 'sans-serif' | 'monospace';
|
||||
@@ -31,7 +30,6 @@ function isValidFontFamily(value: string): value is EditorDisplaySettings['fontF
|
||||
|
||||
const defaultSettings: EditorDisplaySettings = {
|
||||
zoomLevel: 3,
|
||||
indent: 30,
|
||||
lineHeight: 1.5,
|
||||
theme: 'sombre',
|
||||
fontFamily: 'lora',
|
||||
@@ -130,29 +128,6 @@ export default function UserEditorSettings({settings, onSettingsChange}: UserEdi
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="flex items-center gap-2 mb-2 text-text-primary">
|
||||
<Indent className="text-muted w-5 h-5" strokeWidth={1.75}/>
|
||||
{t("userEditorSettings.indent")}
|
||||
</label>
|
||||
<div className="space-y-2">
|
||||
<input
|
||||
type="range"
|
||||
min={0}
|
||||
max={50}
|
||||
step={5}
|
||||
value={settings.indent}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>): void => handleSettingChange('indent', Number(e.target.value))}
|
||||
className="w-full accent-primary"
|
||||
/>
|
||||
<div className="flex justify-between text-sm text-muted">
|
||||
<span>{t("userEditorSettings.indentNone")}</span>
|
||||
<span className="text-text-primary font-medium">{settings.indent}px</span>
|
||||
<span>{t("userEditorSettings.indentMax")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="flex items-center gap-2 mb-2 text-text-primary">
|
||||
<Baseline className="text-muted w-5 h-5" strokeWidth={1.75}/>
|
||||
|
||||
Reference in New Issue
Block a user