@@ -640,7 +653,7 @@ export default function ShortStoryGenerator({onClose}: ShortStoryGeneratorProps)
{t("shortStoryGenerator.navigation.previous")}
-
+
void;
+ useSmart: boolean;
+ setUseSmart: (value: boolean) => void;
+}
+
+export default function AdvancedGenerationOptions({
+ useExplicit,
+ setUseExplicit,
+ useSmart,
+ setUseSmart
+}: AdvancedGenerationOptionsProps) {
+ const t = useTranslations();
+
+ return (
+
+
+
+ }
+ />
+
+ }
+ />
+
+
+ );
+}
diff --git a/components/form/InputField.tsx b/components/form/InputField.tsx
index 60a198e..189853c 100644
--- a/components/form/InputField.tsx
+++ b/components/form/InputField.tsx
@@ -14,6 +14,7 @@ interface InputFieldProps {
actionLabel?: string
actionIcon?: IconDefinition
hint?: string,
+ centered?: boolean,
}
export default function InputField(
@@ -27,11 +28,12 @@ export default function InputField(
action,
actionLabel,
actionIcon,
- hint
+ hint,
+ centered = false
}: InputFieldProps) {
return (
-
-
+
+
{
fieldName && (
@@ -64,7 +66,7 @@ export default function InputField(
)}
-
+
{input}
{
addButtonCallBack && (
diff --git a/components/form/ToggleWithConfirmation.tsx b/components/form/ToggleWithConfirmation.tsx
new file mode 100644
index 0000000..5df0984
--- /dev/null
+++ b/components/form/ToggleWithConfirmation.tsx
@@ -0,0 +1,63 @@
+'use client'
+import React, {useState} from "react";
+import ToggleSwitch from "@/components/form/ToggleSwitch";
+import AlertBox, {AlertType} from "@/components/AlertBox";
+
+interface ToggleWithConfirmationProps {
+ checked: boolean;
+ onChange: (checked: boolean) => void;
+ alertTitle: string;
+ alertMessage: string;
+ alertType: AlertType;
+ confirmText?: string;
+ cancelText?: string;
+ disabled?: boolean;
+}
+
+export default function ToggleWithConfirmation({
+ checked,
+ onChange,
+ alertTitle,
+ alertMessage,
+ alertType,
+ confirmText = "Activer",
+ cancelText = "Annuler",
+ disabled = false
+}: ToggleWithConfirmationProps) {
+ const [showAlert, setShowAlert] = useState
(false);
+
+ function handleToggle(newChecked: boolean): void {
+ if (newChecked) {
+ setShowAlert(true);
+ } else {
+ onChange(false);
+ }
+ }
+
+ async function handleConfirm(): Promise {
+ onChange(true);
+ setShowAlert(false);
+ }
+
+ function handleCancel(): void {
+ setShowAlert(false);
+ }
+
+ return (
+ <>
+
+
+ {showAlert && (
+
+ )}
+ >
+ );
+}
diff --git a/components/ghostwriter/GhostWriter.tsx b/components/ghostwriter/GhostWriter.tsx
index 33ff1b8..f1d43ae 100644
--- a/components/ghostwriter/GhostWriter.tsx
+++ b/components/ghostwriter/GhostWriter.tsx
@@ -34,6 +34,7 @@ import QuillSense, {AIGeneratedText} from "@/lib/models/QuillSense";
import {AIUsageContext, AIUsageContextProps} from "@/context/AIUsageContext";
import {LangContext} from "@/context/LangContext";
import {configs} from "@/lib/configs";
+import AdvancedGenerationOptions from "@/components/form/AdvancedGenerationOptions";
export default function GhostWriter() {
const t = useTranslations();
@@ -61,7 +62,10 @@ export default function GhostWriter() {
const [taguedObjects, setTaguedObjects] = useState([]);
const [taguedWorldElements, setTaguedWorldElements] = useState([]);
const [abortController, setAbortController] = useState | null>(null);
-
+
+ const [useExplicit, setUseExplicit] = useState(false);
+ const [useSmart, setUseSmart] = useState(false);
+
const isGPTEnabled: boolean = QuillSense.isOpenAIEnabled(session);
const isSubTierTree: boolean = QuillSense.getSubLevel(session) === 3;
const hasAccess: boolean = isGPTEnabled || isSubTierTree;
@@ -147,6 +151,8 @@ export default function GhostWriter() {
objects: taguedObjects,
worldElements: taguedWorldElements,
},
+ useExplicit: useExplicit,
+ useSmart: useSmart,
}),
});
@@ -371,6 +377,13 @@ export default function GhostWriter() {
}
/>
+
+
) : advanceSettings && (
@@ -404,7 +417,7 @@ export default function GhostWriter() {
{t("ghostWriter.tags.addTagPlaceholder")}
-
+
{
+ console.log('[AutoUpdater] Checking for updates...');
+ });
+
+ autoUpdater.on('update-available', (info: UpdateInfo) => {
+ console.log('[AutoUpdater] Update available:', info.version);
+ currentWindow?.webContents.send('update:available', info.version);
+ });
+
+ autoUpdater.on('update-not-available', () => {
+ console.log('[AutoUpdater] App is up to date');
+ });
+
+ autoUpdater.on('download-progress', (progress) => {
+ const percent = Math.round(progress.percent);
+ console.log(`[AutoUpdater] Downloading: ${percent}%`);
+ currentWindow?.webContents.send('update:progress', percent);
+ });
+
+ autoUpdater.on('update-downloaded', (info: UpdateInfo) => {
+ console.log('[AutoUpdater] Update ready:', info.version);
+ currentWindow?.webContents.send('update:ready', info.version);
+ });
+
+ autoUpdater.on('error', (error: Error) => {
+ console.error('[AutoUpdater] Error:', error.message);
+ });
+
+ // Check initial
+ autoUpdater.checkForUpdates().catch((err) => {
+ console.error('[AutoUpdater] Check failed:', err.message);
+ });
+
+ // Re-check périodique
+ setInterval(() => {
+ autoUpdater.checkForUpdates().catch((err) => {
+ console.error('[AutoUpdater] Periodic check failed:', err.message);
+ });
+ }, updateCheckInterval);
+}
+
+// Pour forcer l'installation immédiate (optionnel, appelable depuis le renderer)
+export function installUpdateNow(): void {
+ autoUpdater.quitAndInstall(false, true);
+}
\ No newline at end of file
diff --git a/electron/main.ts b/electron/main.ts
index 2a0a9ca..44644a2 100644
--- a/electron/main.ts
+++ b/electron/main.ts
@@ -87,6 +87,9 @@ function createLoginWindow(): void {
loginWindow.once('ready-to-show', () => {
loginWindow?.show();
+ if (loginWindow) {
+ initAutoUpdater(loginWindow);
+ }
});
loginWindow.on('closed', () => {
@@ -145,7 +148,9 @@ function createMainWindow(): void {
mainWindow.once('ready-to-show', () => {
mainWindow?.show();
- initAutoUpdater(mainWindow);
+ if (mainWindow) {
+ initAutoUpdater(mainWindow);
+ }
});
mainWindow.on('closed', () => {
diff --git a/lib/locales/en.json b/lib/locales/en.json
index ac12577..0833e10 100644
--- a/lib/locales/en.json
+++ b/lib/locales/en.json
@@ -1,4 +1,18 @@
{
+ "generationOptions": {
+ "explicit": {
+ "label": "Explicit",
+ "alertTitle": "Explicit Mode",
+ "alertMessage": "This mode enables mature content generation. Some restrictions remain in effect. For users 18 years and older only."
+ },
+ "smart": {
+ "label": "Smart",
+ "alertTitle": "Smart Mode",
+ "alertMessage": "This mode uses the most powerful models (Claude Opus, Grok 4.1) for superior generation quality. Additional fees apply."
+ },
+ "activate": "Activate",
+ "cancel": "Cancel"
+ },
"loginPage": {
"title": "Login",
"welcome": "Welcome to ERitors",
diff --git a/lib/locales/fr.json b/lib/locales/fr.json
index 786920d..14c528c 100644
--- a/lib/locales/fr.json
+++ b/lib/locales/fr.json
@@ -1,4 +1,18 @@
{
+ "generationOptions": {
+ "explicit": {
+ "label": "Explicite",
+ "alertTitle": "Mode Explicite",
+ "alertMessage": "Ce mode permet de générer du contenu mature. Certaines restrictions demeurent en vigueur. Réservé aux utilisateurs de 18 ans et plus."
+ },
+ "smart": {
+ "label": "Intelligent",
+ "alertTitle": "Mode Intelligent",
+ "alertMessage": "Ce mode utilise les modèles les plus performants (Claude Opus, Grok 4.1) pour une qualité de génération supérieure. Des frais supplémentaires s'appliquent."
+ },
+ "activate": "Activer",
+ "cancel": "Annuler"
+ },
"loginPage": {
"title": "Connexion",
"welcome": "Bienvenue sur ERitors",