Add advanced generation options with Explicit and Smart modes
- Implemented `AdvancedGenerationOptions` component for toggling Explicit and Smart modes with confirmation dialogs. - Integrated generation options into `GhostWriter`, `DraftCompanion`, and `ShortStoryGenerator`. - Introduced `ToggleWithConfirmation` component for user interaction with alerts. - Updated `InputField` to support centered alignment for better layout flexibility. - Localized Explicit and Smart mode strings in English and French. - Enhanced content preview logic to filter placeholder text before display. - Added `autoUpdater` initialization checks and refactored updater setup for improved reliability.
This commit is contained in:
@@ -27,6 +27,9 @@ export default function QSTextGeneratedPreview(
|
|||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
|
const filteredValue: string = value.replace(/^starting\.{0,3}\s*/i, '').trim();
|
||||||
|
const hasRealContent: boolean = filteredValue.length > 0;
|
||||||
|
|
||||||
useEffect((): () => void => {
|
useEffect((): () => void => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
const timer = setTimeout(() => setIsVisible(true), 10);
|
const timer = setTimeout(() => setIsVisible(true), 10);
|
||||||
@@ -83,21 +86,46 @@ export default function QSTextGeneratedPreview(
|
|||||||
<div className="flex-1 p-5 overflow-auto custom-scrollbar">
|
<div className="flex-1 p-5 overflow-auto custom-scrollbar">
|
||||||
<div
|
<div
|
||||||
className="w-full bg-darkest-background text-text-primary p-5 rounded-xl border border-secondary/50 shadow-inner">
|
className="w-full bg-darkest-background text-text-primary p-5 rounded-xl border border-secondary/50 shadow-inner">
|
||||||
{isGenerating && !value ? (
|
{isGenerating && !hasRealContent ? (
|
||||||
<div className="space-y-4 animate-pulse">
|
<div className="space-y-3 animate-pulse">
|
||||||
<div className="h-4 bg-secondary/30 rounded w-full"></div>
|
<div className="flex flex-wrap gap-2">
|
||||||
<div className="h-4 bg-secondary/30 rounded w-11/12"></div>
|
<span className="h-5 bg-primary/20 rounded px-4"></span>
|
||||||
<div className="h-4 bg-secondary/30 rounded w-full"></div>
|
<span className="h-5 bg-primary/15 rounded px-6"></span>
|
||||||
<div className="h-4 bg-secondary/30 rounded w-10/12"></div>
|
<span className="h-5 bg-primary/20 rounded px-3"></span>
|
||||||
<div className="h-4 bg-secondary/30 rounded w-full"></div>
|
<span className="h-5 bg-primary/10 rounded px-8"></span>
|
||||||
<div className="h-4 bg-secondary/30 rounded w-9/12"></div>
|
<span className="h-5 bg-primary/20 rounded px-5"></span>
|
||||||
<div className="h-4 bg-secondary/30 rounded w-full"></div>
|
<span className="h-5 bg-primary/15 rounded px-4"></span>
|
||||||
<div className="h-4 bg-secondary/30 rounded w-11/12"></div>
|
<span className="h-5 bg-primary/20 rounded px-7"></span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<span className="h-5 bg-primary/15 rounded px-5"></span>
|
||||||
|
<span className="h-5 bg-primary/20 rounded px-3"></span>
|
||||||
|
<span className="h-5 bg-primary/10 rounded px-6"></span>
|
||||||
|
<span className="h-5 bg-primary/20 rounded px-4"></span>
|
||||||
|
<span className="h-5 bg-primary/15 rounded px-8"></span>
|
||||||
|
<span className="h-5 bg-primary/20 rounded px-3"></span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<span className="h-5 bg-primary/20 rounded px-6"></span>
|
||||||
|
<span className="h-5 bg-primary/10 rounded px-4"></span>
|
||||||
|
<span className="h-5 bg-primary/20 rounded px-5"></span>
|
||||||
|
<span className="h-5 bg-primary/15 rounded px-7"></span>
|
||||||
|
<span className="h-5 bg-primary/20 rounded px-3"></span>
|
||||||
|
<span className="h-5 bg-primary/10 rounded px-5"></span>
|
||||||
|
<span className="h-5 bg-primary/20 rounded px-4"></span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<span className="h-5 bg-primary/15 rounded px-4"></span>
|
||||||
|
<span className="h-5 bg-primary/20 rounded px-6"></span>
|
||||||
|
<span className="h-5 bg-primary/10 rounded px-3"></span>
|
||||||
|
<span className="h-5 bg-primary/20 rounded px-5"></span>
|
||||||
|
<span className="h-5 bg-primary/15 rounded px-7"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="text-justify leading-relaxed whitespace-pre-wrap fade-in-text">
|
<div className="text-justify leading-relaxed whitespace-pre-wrap fade-in-text">
|
||||||
{value}
|
{filteredValue}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import QuillSense from "@/lib/models/QuillSense";
|
|||||||
import {useTranslations} from "next-intl";
|
import {useTranslations} from "next-intl";
|
||||||
import {LangContext, LangContextProps} from "@/context/LangContext";
|
import {LangContext, LangContextProps} from "@/context/LangContext";
|
||||||
import {AIUsageContext, AIUsageContextProps} from "@/context/AIUsageContext";
|
import {AIUsageContext, AIUsageContextProps} from "@/context/AIUsageContext";
|
||||||
|
import AdvancedGenerationOptions from "@/components/form/AdvancedGenerationOptions";
|
||||||
|
|
||||||
interface ShortStoryGeneratorProps {
|
interface ShortStoryGeneratorProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -94,6 +95,9 @@ export default function ShortStoryGenerator({onClose}: ShortStoryGeneratorProps)
|
|||||||
const [hasGenerated, setHasGenerated] = useState<boolean>(false);
|
const [hasGenerated, setHasGenerated] = useState<boolean>(false);
|
||||||
const [abortController, setAbortController] = useState<ReadableStreamDefaultReader<Uint8Array> | null>(null);
|
const [abortController, setAbortController] = useState<ReadableStreamDefaultReader<Uint8Array> | null>(null);
|
||||||
|
|
||||||
|
const [useExplicit, setUseExplicit] = useState<boolean>(false);
|
||||||
|
const [useSmart, setUseSmart] = useState<boolean>(false);
|
||||||
|
|
||||||
const isAnthropicEnabled: boolean = QuillSense.isAnthropicEnabled(session);
|
const isAnthropicEnabled: boolean = QuillSense.isAnthropicEnabled(session);
|
||||||
const isSubTierTwo: boolean = QuillSense.getSubLevel(session) >= 2;
|
const isSubTierTwo: boolean = QuillSense.getSubLevel(session) >= 2;
|
||||||
const hasAccess: boolean = isAnthropicEnabled || isSubTierTwo;
|
const hasAccess: boolean = isAnthropicEnabled || isSubTierTwo;
|
||||||
@@ -169,7 +173,9 @@ export default function ShortStoryGenerator({onClose}: ShortStoryGeneratorProps)
|
|||||||
language: language,
|
language: language,
|
||||||
dialogueType: dialogueType,
|
dialogueType: dialogueType,
|
||||||
directives: directives,
|
directives: directives,
|
||||||
wordsCount: wordsCount
|
wordsCount: wordsCount,
|
||||||
|
useExplicit: useExplicit,
|
||||||
|
useSmart: useSmart,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -561,6 +567,13 @@ export default function ShortStoryGenerator({onClose}: ShortStoryGeneratorProps)
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<AdvancedGenerationOptions
|
||||||
|
useExplicit={useExplicit}
|
||||||
|
setUseExplicit={setUseExplicit}
|
||||||
|
useSmart={useSmart}
|
||||||
|
setUseSmart={setUseSmart}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import {BookTags} from "@/lib/models/Book";
|
|||||||
import {AIUsageContext, AIUsageContextProps} from "@/context/AIUsageContext";
|
import {AIUsageContext, AIUsageContextProps} from "@/context/AIUsageContext";
|
||||||
import {configs} from "@/lib/configs";
|
import {configs} from "@/lib/configs";
|
||||||
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
|
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
|
||||||
|
import AdvancedGenerationOptions from "@/components/form/AdvancedGenerationOptions";
|
||||||
|
|
||||||
interface CompanionContent {
|
interface CompanionContent {
|
||||||
version: number;
|
version: number;
|
||||||
@@ -95,6 +96,9 @@ export default function DraftCompanion() {
|
|||||||
const [showObjectSuggestions, setShowObjectSuggestions] = useState<boolean>(false);
|
const [showObjectSuggestions, setShowObjectSuggestions] = useState<boolean>(false);
|
||||||
const [showWorldElementSuggestions, setShowWorldElementSuggestions] = useState<boolean>(false);
|
const [showWorldElementSuggestions, setShowWorldElementSuggestions] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [useExplicit, setUseExplicit] = useState<boolean>(false);
|
||||||
|
const [useSmart, setUseSmart] = useState<boolean>(false);
|
||||||
|
|
||||||
const isGPTEnabled: boolean = QuillSense.isOpenAIEnabled(session);
|
const isGPTEnabled: boolean = QuillSense.isOpenAIEnabled(session);
|
||||||
const isSubTierTree: boolean = QuillSense.getSubLevel(session) === 3;
|
const isSubTierTree: boolean = QuillSense.getSubLevel(session) === 3;
|
||||||
const hasAccess: boolean = (isGPTEnabled || isSubTierTree) && !isCurrentlyOffline() && !book?.localBook;
|
const hasAccess: boolean = (isGPTEnabled || isSubTierTree) && !isCurrentlyOffline() && !book?.localBook;
|
||||||
@@ -222,7 +226,9 @@ export default function DraftCompanion() {
|
|||||||
locations: taguedLocations,
|
locations: taguedLocations,
|
||||||
objects: taguedObjects,
|
objects: taguedObjects,
|
||||||
worldElements: taguedWorldElements,
|
worldElements: taguedWorldElements,
|
||||||
}
|
},
|
||||||
|
useExplicit: useExplicit,
|
||||||
|
useSmart: useSmart,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -432,7 +438,7 @@ export default function DraftCompanion() {
|
|||||||
return element ? element.label : value;
|
return element ? element.label : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showEnhancer && hasAccess && book?.quillsenseEnabled !== false) {
|
if (showEnhancer && book?.quillsenseEnabled !== false) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full min-h-0 overflow-hidden">
|
<div className="flex flex-col h-full min-h-0 overflow-hidden">
|
||||||
<div
|
<div
|
||||||
@@ -539,6 +545,13 @@ export default function DraftCompanion() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<AdvancedGenerationOptions
|
||||||
|
useExplicit={useExplicit}
|
||||||
|
setUseExplicit={setUseExplicit}
|
||||||
|
useSmart={useSmart}
|
||||||
|
setUseSmart={setUseSmart}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
61
components/form/AdvancedGenerationOptions.tsx
Normal file
61
components/form/AdvancedGenerationOptions.tsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
'use client'
|
||||||
|
import React from "react";
|
||||||
|
import {faBrain, faTriangleExclamation} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import InputField from "@/components/form/InputField";
|
||||||
|
import ToggleWithConfirmation from "@/components/form/ToggleWithConfirmation";
|
||||||
|
import {useTranslations} from "next-intl";
|
||||||
|
|
||||||
|
interface AdvancedGenerationOptionsProps {
|
||||||
|
useExplicit: boolean;
|
||||||
|
setUseExplicit: (value: boolean) => void;
|
||||||
|
useSmart: boolean;
|
||||||
|
setUseSmart: (value: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AdvancedGenerationOptions({
|
||||||
|
useExplicit,
|
||||||
|
setUseExplicit,
|
||||||
|
useSmart,
|
||||||
|
setUseSmart
|
||||||
|
}: AdvancedGenerationOptionsProps) {
|
||||||
|
const t = useTranslations();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-secondary/20 rounded-xl p-5 shadow-inner border border-secondary/30">
|
||||||
|
<div className="flex justify-evenly items-center">
|
||||||
|
<InputField
|
||||||
|
icon={faTriangleExclamation}
|
||||||
|
fieldName={t("generationOptions.explicit.label")}
|
||||||
|
centered
|
||||||
|
input={
|
||||||
|
<ToggleWithConfirmation
|
||||||
|
checked={useExplicit}
|
||||||
|
onChange={setUseExplicit}
|
||||||
|
alertTitle={t("generationOptions.explicit.alertTitle")}
|
||||||
|
alertMessage={t("generationOptions.explicit.alertMessage")}
|
||||||
|
alertType="alert"
|
||||||
|
confirmText={t("generationOptions.activate")}
|
||||||
|
cancelText={t("generationOptions.cancel")}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<InputField
|
||||||
|
icon={faBrain}
|
||||||
|
fieldName={t("generationOptions.smart.label")}
|
||||||
|
centered
|
||||||
|
input={
|
||||||
|
<ToggleWithConfirmation
|
||||||
|
checked={useSmart}
|
||||||
|
onChange={setUseSmart}
|
||||||
|
alertTitle={t("generationOptions.smart.alertTitle")}
|
||||||
|
alertMessage={t("generationOptions.smart.alertMessage")}
|
||||||
|
alertType="informatif"
|
||||||
|
confirmText={t("generationOptions.activate")}
|
||||||
|
cancelText={t("generationOptions.cancel")}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ interface InputFieldProps {
|
|||||||
actionLabel?: string
|
actionLabel?: string
|
||||||
actionIcon?: IconDefinition
|
actionIcon?: IconDefinition
|
||||||
hint?: string,
|
hint?: string,
|
||||||
|
centered?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function InputField(
|
export default function InputField(
|
||||||
@@ -27,11 +28,12 @@ export default function InputField(
|
|||||||
action,
|
action,
|
||||||
actionLabel,
|
actionLabel,
|
||||||
actionIcon,
|
actionIcon,
|
||||||
hint
|
hint,
|
||||||
|
centered = false
|
||||||
}: InputFieldProps) {
|
}: InputFieldProps) {
|
||||||
return (
|
return (
|
||||||
<div className={'flex flex-col'}>
|
<div className={`flex flex-col ${centered ? 'items-center' : ''}`}>
|
||||||
<div className={'flex justify-between items-center mb-2 lg:mb-3 flex-wrap gap-2'}>
|
<div className={`flex items-center mb-2 lg:mb-3 flex-wrap gap-2 ${centered ? 'justify-center' : 'justify-between'}`}>
|
||||||
{
|
{
|
||||||
fieldName && (
|
fieldName && (
|
||||||
<h3 className="text-text-primary text-xl font-[ADLaM Display] font-medium mb-2 flex items-center gap-2">
|
<h3 className="text-text-primary text-xl font-[ADLaM Display] font-medium mb-2 flex items-center gap-2">
|
||||||
@@ -64,7 +66,7 @@ export default function InputField(
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center gap-2">
|
<div className={`flex items-center gap-2 ${centered ? 'justify-center' : 'justify-between'}`}>
|
||||||
{input}
|
{input}
|
||||||
{
|
{
|
||||||
addButtonCallBack && (
|
addButtonCallBack && (
|
||||||
|
|||||||
63
components/form/ToggleWithConfirmation.tsx
Normal file
63
components/form/ToggleWithConfirmation.tsx
Normal file
@@ -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<boolean>(false);
|
||||||
|
|
||||||
|
function handleToggle(newChecked: boolean): void {
|
||||||
|
if (newChecked) {
|
||||||
|
setShowAlert(true);
|
||||||
|
} else {
|
||||||
|
onChange(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleConfirm(): Promise<void> {
|
||||||
|
onChange(true);
|
||||||
|
setShowAlert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel(): void {
|
||||||
|
setShowAlert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ToggleSwitch checked={checked} onChange={handleToggle} disabled={disabled}/>
|
||||||
|
|
||||||
|
{showAlert && (
|
||||||
|
<AlertBox
|
||||||
|
title={alertTitle}
|
||||||
|
message={alertMessage}
|
||||||
|
type={alertType}
|
||||||
|
confirmText={confirmText}
|
||||||
|
cancelText={cancelText}
|
||||||
|
onConfirm={handleConfirm}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ import QuillSense, {AIGeneratedText} from "@/lib/models/QuillSense";
|
|||||||
import {AIUsageContext, AIUsageContextProps} from "@/context/AIUsageContext";
|
import {AIUsageContext, AIUsageContextProps} from "@/context/AIUsageContext";
|
||||||
import {LangContext} from "@/context/LangContext";
|
import {LangContext} from "@/context/LangContext";
|
||||||
import {configs} from "@/lib/configs";
|
import {configs} from "@/lib/configs";
|
||||||
|
import AdvancedGenerationOptions from "@/components/form/AdvancedGenerationOptions";
|
||||||
|
|
||||||
export default function GhostWriter() {
|
export default function GhostWriter() {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
@@ -62,6 +63,9 @@ export default function GhostWriter() {
|
|||||||
const [taguedWorldElements, setTaguedWorldElements] = useState<string[]>([]);
|
const [taguedWorldElements, setTaguedWorldElements] = useState<string[]>([]);
|
||||||
const [abortController, setAbortController] = useState<ReadableStreamDefaultReader<Uint8Array> | null>(null);
|
const [abortController, setAbortController] = useState<ReadableStreamDefaultReader<Uint8Array> | null>(null);
|
||||||
|
|
||||||
|
const [useExplicit, setUseExplicit] = useState<boolean>(false);
|
||||||
|
const [useSmart, setUseSmart] = useState<boolean>(false);
|
||||||
|
|
||||||
const isGPTEnabled: boolean = QuillSense.isOpenAIEnabled(session);
|
const isGPTEnabled: boolean = QuillSense.isOpenAIEnabled(session);
|
||||||
const isSubTierTree: boolean = QuillSense.getSubLevel(session) === 3;
|
const isSubTierTree: boolean = QuillSense.getSubLevel(session) === 3;
|
||||||
const hasAccess: boolean = isGPTEnabled || isSubTierTree;
|
const hasAccess: boolean = isGPTEnabled || isSubTierTree;
|
||||||
@@ -147,6 +151,8 @@ export default function GhostWriter() {
|
|||||||
objects: taguedObjects,
|
objects: taguedObjects,
|
||||||
worldElements: taguedWorldElements,
|
worldElements: taguedWorldElements,
|
||||||
},
|
},
|
||||||
|
useExplicit: useExplicit,
|
||||||
|
useSmart: useSmart,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -371,6 +377,13 @@ export default function GhostWriter() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<AdvancedGenerationOptions
|
||||||
|
useExplicit={useExplicit}
|
||||||
|
setUseExplicit={setUseExplicit}
|
||||||
|
useSmart={useSmart}
|
||||||
|
setUseSmart={setUseSmart}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : advanceSettings && (
|
) : advanceSettings && (
|
||||||
<GhostWriterSettings advancedPrompt={advancedPrompt} setAdvancedPrompt={setAdvancedPrompt}/>
|
<GhostWriterSettings advancedPrompt={advancedPrompt} setAdvancedPrompt={setAdvancedPrompt}/>
|
||||||
|
|||||||
75
electron/autoUpdater.ts
Normal file
75
electron/autoUpdater.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import pkg from 'electron-updater';
|
||||||
|
import type { UpdateInfo } from 'electron-updater';
|
||||||
|
const { autoUpdater } = pkg;
|
||||||
|
import { app, BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
const updateCheckInterval = 4 * 60 * 60 * 1000; // 4 heures
|
||||||
|
|
||||||
|
let initialized = false;
|
||||||
|
let currentWindow: BrowserWindow | null = null;
|
||||||
|
|
||||||
|
export function initAutoUpdater(window: BrowserWindow): void {
|
||||||
|
currentWindow = window;
|
||||||
|
|
||||||
|
if (!app.isPackaged) {
|
||||||
|
console.log('[AutoUpdater] Skipped in development mode');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si déjà initialisé, juste mettre à jour la fenêtre cible
|
||||||
|
if (initialized) {
|
||||||
|
console.log('[AutoUpdater] Window target updated');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
// Config: télécharge auto, installe au quit
|
||||||
|
autoUpdater.autoDownload = true;
|
||||||
|
autoUpdater.autoInstallOnAppQuit = true;
|
||||||
|
|
||||||
|
autoUpdater.on('checking-for-update', () => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -87,6 +87,9 @@ function createLoginWindow(): void {
|
|||||||
|
|
||||||
loginWindow.once('ready-to-show', () => {
|
loginWindow.once('ready-to-show', () => {
|
||||||
loginWindow?.show();
|
loginWindow?.show();
|
||||||
|
if (loginWindow) {
|
||||||
|
initAutoUpdater(loginWindow);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
loginWindow.on('closed', () => {
|
loginWindow.on('closed', () => {
|
||||||
@@ -145,7 +148,9 @@ function createMainWindow(): void {
|
|||||||
|
|
||||||
mainWindow.once('ready-to-show', () => {
|
mainWindow.once('ready-to-show', () => {
|
||||||
mainWindow?.show();
|
mainWindow?.show();
|
||||||
initAutoUpdater(mainWindow);
|
if (mainWindow) {
|
||||||
|
initAutoUpdater(mainWindow);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mainWindow.on('closed', () => {
|
mainWindow.on('closed', () => {
|
||||||
|
|||||||
@@ -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": {
|
"loginPage": {
|
||||||
"title": "Login",
|
"title": "Login",
|
||||||
"welcome": "Welcome to ERitors",
|
"welcome": "Welcome to ERitors",
|
||||||
|
|||||||
@@ -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": {
|
"loginPage": {
|
||||||
"title": "Connexion",
|
"title": "Connexion",
|
||||||
"welcome": "Bienvenue sur ERitors",
|
"welcome": "Bienvenue sur ERitors",
|
||||||
|
|||||||
Reference in New Issue
Block a user