Remove ExportBook component and integrate new export workflows
- Deleted `ExportBook` component and its usage in `BookCard.tsx`. - Integrated improved book export workflows in `BookSettingOption` for better user experience. - Updated database models and repositories to support export options with chapter/version selection. - Added localization support for export-related messages and tooltips. - Upgraded dependencies to include libraries required for export formats (e.g., DOCX, PDF, EPUB). - Bumped app version to 0.4.1.
This commit is contained in:
@@ -1,190 +0,0 @@
|
|||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
|
||||||
import {faDownload} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import {useContext, useRef, useState} from "react";
|
|
||||||
import {SessionContext} from "@/context/SessionContext";
|
|
||||||
import {AlertContext} from "@/context/AlertContext";
|
|
||||||
import {configs} from "@/lib/configs";
|
|
||||||
|
|
||||||
interface CreateEpubProps {
|
|
||||||
bookId: string;
|
|
||||||
bookTitle: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ExportBook({bookId, bookTitle}: CreateEpubProps) {
|
|
||||||
const {session} = useContext(SessionContext);
|
|
||||||
const {successMessage, errorMessage} = useContext(AlertContext);
|
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
||||||
|
|
||||||
function handleClickOutside(event: MouseEvent): void {
|
|
||||||
if (
|
|
||||||
menuRef.current &&
|
|
||||||
buttonRef.current &&
|
|
||||||
!menuRef.current.contains(event.target as Node) &&
|
|
||||||
!buttonRef.current.contains(event.target as Node)
|
|
||||||
) {
|
|
||||||
setShowMenu(false);
|
|
||||||
document.removeEventListener("mousedown", handleClickOutside);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleMenu(): void {
|
|
||||||
if (!showMenu) {
|
|
||||||
setTimeout((): void => {
|
|
||||||
document.addEventListener("mousedown", handleClickOutside);
|
|
||||||
}, 0);
|
|
||||||
} else {
|
|
||||||
document.removeEventListener("mousedown", handleClickOutside);
|
|
||||||
}
|
|
||||||
setShowMenu(!showMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDownloadEpub() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${configs.apiUrl}book/transform/epub?id=${bookId}`,
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${session.accessToken}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
errorMessage(`Échec du téléchargement du EPUB.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const blob = await response.blob();
|
|
||||||
const virtualUrl = window.URL.createObjectURL(blob);
|
|
||||||
const aLink = document.createElement("a");
|
|
||||||
aLink.href = virtualUrl;
|
|
||||||
aLink.download = `${bookTitle}.epub`;
|
|
||||||
document.body.appendChild(aLink);
|
|
||||||
aLink.click();
|
|
||||||
aLink.remove();
|
|
||||||
window.URL.revokeObjectURL(virtualUrl);
|
|
||||||
setShowMenu(false);
|
|
||||||
successMessage(`Votre fichier EPUB a été téléchargé.`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error downloading EPUB:`, error);
|
|
||||||
errorMessage(`Une erreur est survenue lors du téléchargement.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDownloadPdf() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${configs.apiUrl}book/transform/pdf?id=${bookId}`,
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${session.accessToken}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
errorMessage(`Échec du téléchargement du PDF.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const blob = await response.blob();
|
|
||||||
const virtualUrl = window.URL.createObjectURL(blob);
|
|
||||||
const aLink = document.createElement("a");
|
|
||||||
aLink.href = virtualUrl;
|
|
||||||
aLink.download = `${bookTitle}.pdf`;
|
|
||||||
document.body.appendChild(aLink);
|
|
||||||
aLink.click();
|
|
||||||
aLink.remove();
|
|
||||||
window.URL.revokeObjectURL(virtualUrl);
|
|
||||||
setShowMenu(false);
|
|
||||||
successMessage(`Votre fichier PDF a été téléchargé.`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error downloading PDF:`, error);
|
|
||||||
errorMessage(`Une erreur est survenue lors du téléchargement.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDownloadDocx() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${configs.apiUrl}book/transform/docx?id=${bookId}`,
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${session.accessToken}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
errorMessage(`Échec du téléchargement du DOCX.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const blob = await response.blob();
|
|
||||||
const virtualUrl = window.URL.createObjectURL(blob);
|
|
||||||
const aLink = document.createElement("a");
|
|
||||||
aLink.href = virtualUrl;
|
|
||||||
aLink.download = `${bookTitle}.docx`;
|
|
||||||
document.body.appendChild(aLink);
|
|
||||||
aLink.click();
|
|
||||||
aLink.remove();
|
|
||||||
window.URL.revokeObjectURL(virtualUrl);
|
|
||||||
setShowMenu(false);
|
|
||||||
successMessage(`Votre fichier DOCX a été téléchargé.`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error downloading DOCX:`, error);
|
|
||||||
errorMessage(`Une erreur est survenue lors du téléchargement.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
<button
|
|
||||||
ref={buttonRef}
|
|
||||||
onClick={toggleMenu}
|
|
||||||
className="text-muted hover:text-primary transition-all duration-200 p-1.5 rounded-lg hover:bg-secondary/50 hover:scale-110"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faDownload} className={'w-4 h-4'}/>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{showMenu && (
|
|
||||||
<div
|
|
||||||
ref={menuRef}
|
|
||||||
className="absolute z-50 bg-tertiary/90 backdrop-blur-sm shadow-2xl rounded-xl border border-secondary/50"
|
|
||||||
style={{
|
|
||||||
width: '110px',
|
|
||||||
right: '-30px',
|
|
||||||
top: '100%',
|
|
||||||
marginTop: '8px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ul className="py-2">
|
|
||||||
<li
|
|
||||||
className="px-3 py-2 hover:bg-secondary cursor-pointer text-sm text-muted hover:text-text-primary transition-all duration-200 hover:scale-105 font-medium"
|
|
||||||
onClick={handleDownloadEpub}
|
|
||||||
>
|
|
||||||
EPUB
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
className="px-3 py-2 hover:bg-secondary cursor-pointer text-sm text-muted hover:text-text-primary transition-all duration-200 hover:scale-105 font-medium"
|
|
||||||
onClick={handleDownloadPdf}
|
|
||||||
>
|
|
||||||
PDF
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
className="px-3 py-2 hover:bg-secondary cursor-pointer text-sm text-muted hover:text-text-primary transition-all duration-200 hover:scale-105 font-medium"
|
|
||||||
onClick={handleDownloadDocx}
|
|
||||||
>
|
|
||||||
DOCX
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
// Removed Next.js Link import for Electron
|
// Removed Next.js Link import for Electron
|
||||||
import {BookProps} from "@/lib/models/Book";
|
import {BookProps} from "@/lib/models/Book";
|
||||||
import DeleteBook from "@/components/book/settings/DeleteBook";
|
import DeleteBook from "@/components/book/settings/DeleteBook";
|
||||||
import ExportBook from "@/components/ExportBook";
|
|
||||||
import {useTranslations} from "next-intl";
|
import {useTranslations} from "next-intl";
|
||||||
import SyncBook from "@/components/SyncBook";
|
import SyncBook from "@/components/SyncBook";
|
||||||
import {SyncType} from "@/context/BooksSyncContext";
|
import {SyncType} from "@/context/BooksSyncContext";
|
||||||
import {useEffect} from "react";
|
|
||||||
|
|
||||||
interface BookCardProps {
|
interface BookCardProps {
|
||||||
book: BookProps;
|
book: BookProps;
|
||||||
@@ -68,7 +66,6 @@ export default function BookCard({book, onClickCallback, index, syncStatus}: Boo
|
|||||||
<div className="flex justify-between items-center pt-3 border-t border-secondary/30">
|
<div className="flex justify-between items-center pt-3 border-t border-secondary/30">
|
||||||
<SyncBook status={syncStatus} bookId={book.bookId}/>
|
<SyncBook status={syncStatus} bookId={book.bookId}/>
|
||||||
<div className="flex items-center gap-1" {...index === 0 && {'data-guide': 'bottom-book-card'}}>
|
<div className="flex items-center gap-1" {...index === 0 && {'data-guide': 'bottom-book-card'}}>
|
||||||
<ExportBook bookTitle={book.title} bookId={book.bookId}/>
|
|
||||||
<DeleteBook bookId={book.bookId}/>
|
<DeleteBook bookId={book.bookId}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {useContext, useEffect, useState} from "react";
|
import {useContext, useEffect, useRef, useState} from "react";
|
||||||
import System from "@/lib/models/System";
|
import System from "@/lib/models/System";
|
||||||
import {AlertContext} from "@/context/AlertContext";
|
import {AlertContext} from "@/context/AlertContext";
|
||||||
import {BookContext} from "@/context/BookContext";
|
import {BookContext} from "@/context/BookContext";
|
||||||
import SearchBook from "./SearchBook";
|
import SearchBook from "./SearchBook";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faBook, faDownload, faGear, faTrash} from "@fortawesome/free-solid-svg-icons";
|
import {faBook, faChevronLeft, faChevronRight, faDownload, faGear, faTrash} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {SessionContext} from "@/context/SessionContext";
|
import {SessionContext} from "@/context/SessionContext";
|
||||||
import Book, {BookProps} from "@/lib/models/Book";
|
import Book, {BookProps} from "@/lib/models/Book";
|
||||||
import BookCard from "@/components/book/BookCard";
|
import BookCard from "@/components/book/BookCard";
|
||||||
@@ -55,6 +55,7 @@ export default function BookList() {
|
|||||||
const [isLoadingBooks, setIsLoadingBooks] = useState<boolean>(true);
|
const [isLoadingBooks, setIsLoadingBooks] = useState<boolean>(true);
|
||||||
const [showSeriesSettingId, setShowSeriesSettingId] = useState<string | null>(null);
|
const [showSeriesSettingId, setShowSeriesSettingId] = useState<string | null>(null);
|
||||||
const [isLocalSeries, setIsLocalSeries] = useState<boolean>(false);
|
const [isLocalSeries, setIsLocalSeries] = useState<boolean>(false);
|
||||||
|
const carouselRefs = useRef<Record<string, HTMLDivElement | null>>({});
|
||||||
|
|
||||||
const [bookGuide, setBookGuide] = useState<boolean>(false);
|
const [bookGuide, setBookGuide] = useState<boolean>(false);
|
||||||
|
|
||||||
@@ -447,6 +448,17 @@ export default function BookList() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scrollCarousel(category: string, direction: 'left' | 'right'): void {
|
||||||
|
const container: HTMLDivElement | null = carouselRefs.current[category];
|
||||||
|
if (!container) return;
|
||||||
|
const cardWidth: number = container.querySelector<HTMLDivElement>(':scope > div')?.offsetWidth || 250;
|
||||||
|
const scrollAmount: number = cardWidth * 2;
|
||||||
|
container.scrollBy({
|
||||||
|
left: direction === 'left' ? -scrollAmount : scrollAmount,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handleSeriesSettingsClick(seriesId: string): void {
|
function handleSeriesSettingsClick(seriesId: string): void {
|
||||||
const isLocal: boolean = isCurrentlyOffline() ||
|
const isLocal: boolean = isCurrentlyOffline() ||
|
||||||
Boolean(localOnlySeries.find((s: SyncedSeries): boolean => s.id === seriesId));
|
Boolean(localOnlySeries.find((s: SyncedSeries): boolean => s.id === seriesId));
|
||||||
@@ -464,7 +476,7 @@ export default function BookList() {
|
|||||||
<SearchBook searchQuery={searchQuery} setSearchQuery={setSearchQuery}/>
|
<SearchBook searchQuery={searchQuery} setSearchQuery={setSearchQuery}/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-col w-full overflow-y-auto h-full min-h-0 flex-grow">
|
<div className="flex flex-col w-full overflow-y-auto overflow-x-hidden h-full min-h-0 flex-grow">
|
||||||
{
|
{
|
||||||
isLoadingBooks ? (
|
isLoadingBooks ? (
|
||||||
<>
|
<>
|
||||||
@@ -511,36 +523,55 @@ export default function BookList() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start justify-center w-full px-4 overflow-x-auto pb-4">
|
<div className="group relative w-full">
|
||||||
{items.map((item, idx) => {
|
<button
|
||||||
if (item.type === 'book' && item.book) {
|
onClick={() => scrollCarousel(category, 'left')}
|
||||||
return (
|
className="absolute left-3 top-1/2 -translate-y-1/2 z-10 bg-primary/80 backdrop-blur-sm hover:bg-primary text-white rounded-2xl w-12 h-12 flex items-center justify-center shadow-xl border border-primary-light/30 transition-all duration-200 opacity-0 group-hover:opacity-100 hover:scale-110"
|
||||||
<div key={item.book.bookId}
|
>
|
||||||
{...(idx === 0 && {'data-guide': 'book-card'})}
|
<FontAwesomeIcon icon={faChevronLeft} className="w-5 h-5"/>
|
||||||
className={`flex-shrink-0 w-64 sm:w-52 md:w-48 lg:w-56 xl:w-64 p-2 box-border ${User.guideTourDone(session.user?.guideTour || [], 'new-first-book') && 'mb-[200px]'}`}>
|
</button>
|
||||||
<BookCard
|
|
||||||
book={item.book}
|
<div
|
||||||
syncStatus={detectBookSyncStatus(item.book.bookId)}
|
ref={(el: HTMLDivElement | null) => { carouselRefs.current[category] = el; }}
|
||||||
onClickCallback={handleBookClick}
|
className="flex items-start w-full overflow-hidden px-4 gap-2 scroll-smooth"
|
||||||
index={idx}
|
>
|
||||||
|
{items.map((item, idx) => {
|
||||||
|
if (item.type === 'book' && item.book) {
|
||||||
|
return (
|
||||||
|
<div key={item.book.bookId}
|
||||||
|
{...(idx === 0 && {'data-guide': 'book-card'})}
|
||||||
|
className={`flex-shrink-0 w-64 sm:w-52 md:w-48 lg:w-56 xl:w-64 p-2 box-border ${User.guideTourDone(session.user?.guideTour || [], 'new-first-book') && 'mb-[200px]'}`}>
|
||||||
|
<BookCard
|
||||||
|
book={item.book}
|
||||||
|
syncStatus={detectBookSyncStatus(item.book.bookId)}
|
||||||
|
onClickCallback={handleBookClick}
|
||||||
|
index={idx}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (item.type === 'series' && item.series) {
|
||||||
|
return (
|
||||||
|
<SeriesCard
|
||||||
|
key={item.series.id}
|
||||||
|
series={item.series}
|
||||||
|
onBookClick={handleBookClick}
|
||||||
|
onSettingsClick={handleSeriesSettingsClick}
|
||||||
|
getSyncStatus={detectBookSyncStatus}
|
||||||
|
seriesSyncStatus={detectSeriesSyncStatus(item.series.id)}
|
||||||
/>
|
/>
|
||||||
</div>
|
);
|
||||||
);
|
}
|
||||||
}
|
return null;
|
||||||
if (item.type === 'series' && item.series) {
|
})}
|
||||||
return (
|
</div>
|
||||||
<SeriesCard
|
|
||||||
key={item.series.id}
|
<button
|
||||||
series={item.series}
|
onClick={() => scrollCarousel(category, 'right')}
|
||||||
onBookClick={handleBookClick}
|
className="absolute right-3 top-1/2 -translate-y-1/2 z-10 bg-primary/80 backdrop-blur-sm hover:bg-primary text-white rounded-2xl w-12 h-12 flex items-center justify-center shadow-xl border border-primary-light/30 transition-all duration-200 opacity-0 group-hover:opacity-100 hover:scale-110"
|
||||||
onSettingsClick={handleSeriesSettingsClick}
|
>
|
||||||
getSyncStatus={detectBookSyncStatus}
|
<FontAwesomeIcon icon={faChevronRight} className="w-5 h-5"/>
|
||||||
seriesSyncStatus={detectSeriesSyncStatus(item.series.id)}
|
</button>
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ const CharacterSettings = lazy(function () {
|
|||||||
const SpellSettings = lazy(function () {
|
const SpellSettings = lazy(function () {
|
||||||
return import('./spells/settings/SpellSettings');
|
return import('./spells/settings/SpellSettings');
|
||||||
});
|
});
|
||||||
|
const ExportSetting = lazy(function () {
|
||||||
|
return import('./ExportSetting');
|
||||||
|
});
|
||||||
|
|
||||||
function LoadingSpinner(): React.JSX.Element {
|
function LoadingSpinner(): React.JSX.Element {
|
||||||
return (
|
return (
|
||||||
@@ -51,7 +54,7 @@ interface SettingRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Settings qui gèrent leur propre save (pas de bouton save parent)
|
// Settings qui gèrent leur propre save (pas de bouton save parent)
|
||||||
const selfManagedSettings: string[] = ['characters', 'spells', 'world', 'worlds', 'locations'];
|
const selfManagedSettings: string[] = ['characters', 'spells', 'world', 'worlds', 'locations', 'export'];
|
||||||
|
|
||||||
export default function BookSettingOption({setting}: BookSettingOptionProps): React.JSX.Element {
|
export default function BookSettingOption({setting}: BookSettingOptionProps): React.JSX.Element {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
@@ -77,6 +80,8 @@ export default function BookSettingOption({setting}: BookSettingOptionProps): Re
|
|||||||
return t("bookSettingOption.characters");
|
return t("bookSettingOption.characters");
|
||||||
case 'spells':
|
case 'spells':
|
||||||
return t("bookSettingOption.spells");
|
return t("bookSettingOption.spells");
|
||||||
|
case 'export':
|
||||||
|
return t("bookSettingOption.export");
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -119,7 +124,10 @@ export default function BookSettingOption({setting}: BookSettingOptionProps): Re
|
|||||||
{setting === 'spells' && (
|
{setting === 'spells' && (
|
||||||
<SpellSettings entityType="book" showToggle={true}/>
|
<SpellSettings entityType="book" showToggle={true}/>
|
||||||
)}
|
)}
|
||||||
{!['basic-information', 'guide-line', 'story', 'world', 'worlds', 'locations', 'characters', 'spells', 'quillsense'].includes(setting) && (
|
{setting === 'export' && (
|
||||||
|
<ExportSetting/>
|
||||||
|
)}
|
||||||
|
{!['basic-information', 'guide-line', 'story', 'world', 'worlds', 'locations', 'characters', 'spells', 'quillsense', 'export'].includes(setting) && (
|
||||||
<div className="text-text-secondary py-4 text-center">
|
<div className="text-text-secondary py-4 text-center">
|
||||||
{t("bookSettingOption.notAvailable")}
|
{t("bookSettingOption.notAvailable")}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {
|
import {
|
||||||
faBook,
|
faBook,
|
||||||
|
faDownload,
|
||||||
faGlobe,
|
faGlobe,
|
||||||
faHatWizard,
|
faHatWizard,
|
||||||
faListAlt,
|
faListAlt,
|
||||||
@@ -74,6 +75,11 @@ export default function BookSettingSidebar(
|
|||||||
name: 'bookSetting.quillsense',
|
name: 'bookSetting.quillsense',
|
||||||
icon: faWandMagicSparkles
|
icon: faWandMagicSparkles
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'export',
|
||||||
|
name: 'bookSetting.export',
|
||||||
|
icon: faDownload
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// id: 'objects',
|
// id: 'objects',
|
||||||
// name: t('bookSetting.objects'),
|
// name: t('bookSetting.objects'),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function ScribeEditor() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingBookContext.Provider value={{bookSettingId, setBookSettingId}}>
|
<SettingBookContext.Provider value={{bookSettingId, setBookSettingId}}>
|
||||||
<div className="flex-1 bg-darkest-background">
|
<div className="flex-1 min-w-0 bg-darkest-background">
|
||||||
{
|
{
|
||||||
chapter ? (
|
chapter ? (
|
||||||
<TextEditor/>
|
<TextEditor/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faBookMedical, faBookOpen, faFeather, faLayerGroup} from "@fortawesome/free-solid-svg-icons";
|
import {faBookMedical, faBookOpen, faFeather, faFileImport, faLayerGroup} from "@fortawesome/free-solid-svg-icons";
|
||||||
import React, {useContext, useEffect, useState} from "react";
|
import React, {useContext, useEffect, useState} from "react";
|
||||||
import {BookContext} from "@/context/BookContext";
|
import {BookContext} from "@/context/BookContext";
|
||||||
import ScribeChapterComponent from "@/components/leftbar/ScribeChapterComponent";
|
import ScribeChapterComponent from "@/components/leftbar/ScribeChapterComponent";
|
||||||
@@ -8,6 +8,7 @@ import {PanelComponent} from "@/lib/models/Editor";
|
|||||||
import AddNewBookForm from "@/components/book/AddNewBookForm";
|
import AddNewBookForm from "@/components/book/AddNewBookForm";
|
||||||
import AddNewSeriesForm from "@/components/series/AddNewSeriesForm";
|
import AddNewSeriesForm from "@/components/series/AddNewSeriesForm";
|
||||||
import ShortStoryGenerator from "@/components/ShortStoryGenerator";
|
import ShortStoryGenerator from "@/components/ShortStoryGenerator";
|
||||||
|
import ImportBookForm from "@/components/book/ImportBookForm";
|
||||||
import {SessionContext} from "@/context/SessionContext";
|
import {SessionContext} from "@/context/SessionContext";
|
||||||
import {useTranslations} from "next-intl";
|
import {useTranslations} from "next-intl";
|
||||||
import OfflineContext from "@/context/OfflineContext";
|
import OfflineContext from "@/context/OfflineContext";
|
||||||
@@ -24,6 +25,7 @@ export default function ScribeLeftBar() {
|
|||||||
const [showAddNewBook, setShowAddNewBook] = useState<boolean>(false);
|
const [showAddNewBook, setShowAddNewBook] = useState<boolean>(false);
|
||||||
const [showAddNewSeries, setShowAddNewSeries] = useState<boolean>(false);
|
const [showAddNewSeries, setShowAddNewSeries] = useState<boolean>(false);
|
||||||
const [showGenerateShortModal, setShowGenerateShortModal] = useState<boolean>(false)
|
const [showGenerateShortModal, setShowGenerateShortModal] = useState<boolean>(false)
|
||||||
|
const [showImportBook, setShowImportBook] = useState<boolean>(false)
|
||||||
const {isCurrentlyOffline} = useContext(OfflineContext)
|
const {isCurrentlyOffline} = useContext(OfflineContext)
|
||||||
|
|
||||||
const editorComponents: PanelComponent[] = [
|
const editorComponents: PanelComponent[] = [
|
||||||
@@ -69,6 +71,12 @@ export default function ScribeLeftBar() {
|
|||||||
icon: faLayerGroup,
|
icon: faLayerGroup,
|
||||||
badge: t("scribeLeftBar.homeComponents.addSeries.badge"),
|
badge: t("scribeLeftBar.homeComponents.addSeries.badge"),
|
||||||
description: t("scribeLeftBar.homeComponents.addSeries.description")
|
description: t("scribeLeftBar.homeComponents.addSeries.description")
|
||||||
|
}, {
|
||||||
|
id: 4,
|
||||||
|
title: t("scribeLeftBar.homeComponents.importBook.title"),
|
||||||
|
icon: faFileImport,
|
||||||
|
badge: t("scribeLeftBar.homeComponents.importBook.badge"),
|
||||||
|
description: t("scribeLeftBar.homeComponents.importBook.description")
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -116,9 +124,9 @@ export default function ScribeLeftBar() {
|
|||||||
)) : (
|
)) : (
|
||||||
homeComponents
|
homeComponents
|
||||||
.filter((component: PanelComponent): boolean => {
|
.filter((component: PanelComponent): boolean => {
|
||||||
// Hide generate story (id: 2) in offline mode (requires AI server)
|
// Hide generate story (id: 2) and import book (id: 4) in offline mode (requires server)
|
||||||
// Series (id: 3) now has dual logic and works offline
|
// Series (id: 3) now has dual logic and works offline
|
||||||
if (isCurrentlyOffline() && component.id === 2) {
|
if (isCurrentlyOffline() && (component.id === 2 || component.id === 4)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -126,7 +134,7 @@ export default function ScribeLeftBar() {
|
|||||||
.map((component: PanelComponent) => (
|
.map((component: PanelComponent) => (
|
||||||
<button
|
<button
|
||||||
key={component.id}
|
key={component.id}
|
||||||
onClick={() => component.id === 1 ? setShowAddNewBook(true) : component.id === 2 ? setShowGenerateShortModal(true) : component.id === 3 ? setShowAddNewSeries(true) : null}
|
onClick={() => component.id === 1 ? setShowAddNewBook(true) : component.id === 2 ? setShowGenerateShortModal(true) : component.id === 3 ? setShowAddNewSeries(true) : component.id === 4 ? setShowImportBook(true) : null}
|
||||||
title={component.title}
|
title={component.title}
|
||||||
className={`group relative p-3 rounded-xl transition-all duration-200 ${panelHidden && currentPanel?.id === component.id
|
className={`group relative p-3 rounded-xl transition-all duration-200 ${panelHidden && currentPanel?.id === component.id
|
||||||
? 'bg-primary text-text-primary shadow-lg shadow-primary/30'
|
? 'bg-primary text-text-primary shadow-lg shadow-primary/30'
|
||||||
@@ -162,6 +170,10 @@ export default function ScribeLeftBar() {
|
|||||||
showGenerateShortModal &&
|
showGenerateShortModal &&
|
||||||
<ShortStoryGenerator onClose={() => setShowGenerateShortModal(false)}/>
|
<ShortStoryGenerator onClose={() => setShowGenerateShortModal(false)}/>
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
showImportBook &&
|
||||||
|
<ImportBookForm setCloseForm={setShowImportBook}/>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3,9 +3,12 @@ import { getUserEncryptionKey } from "../keyManager.js";
|
|||||||
import Book, { CompleteBookData } from "./Book.js";
|
import Book, { CompleteBookData } from "./Book.js";
|
||||||
import ChapterRepo, {
|
import ChapterRepo, {
|
||||||
ActChapterQuery,
|
ActChapterQuery,
|
||||||
|
ChapterExportInfoResult,
|
||||||
ChapterQueryResult,
|
ChapterQueryResult,
|
||||||
|
ChapterSelectionParam,
|
||||||
ChapterStoryQueryResult,
|
ChapterStoryQueryResult,
|
||||||
LastChapterResult
|
LastChapterResult,
|
||||||
|
SelectedChapterContentResult
|
||||||
} from "../repositories/chapter.repository.js";
|
} from "../repositories/chapter.repository.js";
|
||||||
import { ActChapter, ActStory } from "./Act.js";
|
import { ActChapter, ActStory } from "./Act.js";
|
||||||
import ChapterContentRepository, {
|
import ChapterContentRepository, {
|
||||||
@@ -65,6 +68,13 @@ export interface CompleteChapterContent {
|
|||||||
version?: number;
|
version?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ChapterExportInfo {
|
||||||
|
chapterId: string;
|
||||||
|
title: string;
|
||||||
|
chapterOrder: number;
|
||||||
|
availableVersions: number[];
|
||||||
|
}
|
||||||
|
|
||||||
interface TipTapNode {
|
interface TipTapNode {
|
||||||
type?: string;
|
type?: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
@@ -602,4 +612,53 @@ export default class Chapter {
|
|||||||
|
|
||||||
return processedChapters;
|
return processedChapters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getChaptersExportInfo(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterExportInfo[] {
|
||||||
|
const results: ChapterExportInfoResult[] = ChapterRepo.fetchChaptersExportInfo(userId, bookId, lang);
|
||||||
|
const userEncryptionKey: string = getUserEncryptionKey(userId);
|
||||||
|
const exportInfos: ChapterExportInfo[] = [];
|
||||||
|
|
||||||
|
for (const result of results) {
|
||||||
|
if (!result.available_versions) continue;
|
||||||
|
const versions: number[] = result.available_versions
|
||||||
|
.split(',')
|
||||||
|
.map((v: string): number => parseInt(v, 10))
|
||||||
|
.filter((v: number): boolean => !isNaN(v));
|
||||||
|
if (versions.length === 0) continue;
|
||||||
|
exportInfos.push({
|
||||||
|
chapterId: result.chapter_id,
|
||||||
|
title: result.title ? System.decryptDataWithUserKey(result.title, userEncryptionKey) : '',
|
||||||
|
chapterOrder: result.chapter_order,
|
||||||
|
availableVersions: versions.sort((a: number, b: number): number => a - b)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return exportInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getCompleteBookDataWithSelections(userId: string, bookId: string, selections: ChapterSelectionParam[] | null, lang: 'fr' | 'en' = 'fr'): CompleteBookData {
|
||||||
|
if (!selections || selections.length === 0) {
|
||||||
|
return Book.completeBookData(userId, bookId, lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bookData: CompleteBookData = Book.completeBookData(userId, bookId, lang);
|
||||||
|
const selectedResults: SelectedChapterContentResult[] = ChapterRepo.fetchSelectedChaptersContent(bookId, selections, lang);
|
||||||
|
const userEncryptionKey: string = getUserEncryptionKey(userId);
|
||||||
|
const selectedChapters: CompleteChapterContent[] = [];
|
||||||
|
|
||||||
|
for (const result of selectedResults) {
|
||||||
|
selectedChapters.push({
|
||||||
|
id: result.chapter_id,
|
||||||
|
title: result.title ? System.decryptDataWithUserKey(result.title, userEncryptionKey) : '',
|
||||||
|
content: result.content ? System.decryptDataWithUserKey(result.content, userEncryptionKey) : '',
|
||||||
|
order: result.chapter_order,
|
||||||
|
version: result.version
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...bookData,
|
||||||
|
chapters: selectedChapters
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,26 @@ export interface ChapterBookResult extends Record<string, SQLiteValue> {
|
|||||||
content: string | null;
|
content: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ChapterExportInfoResult extends Record<string, SQLiteValue> {
|
||||||
|
chapter_id: string;
|
||||||
|
title: string;
|
||||||
|
chapter_order: number;
|
||||||
|
available_versions: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectedChapterContentResult extends Record<string, SQLiteValue> {
|
||||||
|
chapter_id: string;
|
||||||
|
title: string;
|
||||||
|
chapter_order: number;
|
||||||
|
content: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChapterSelectionParam {
|
||||||
|
chapterId: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
export default class ChapterRepo {
|
export default class ChapterRepo {
|
||||||
/**
|
/**
|
||||||
* Checks if a chapter name already exists for a book.
|
* Checks if a chapter name already exists for a book.
|
||||||
@@ -698,4 +718,38 @@ export default class ChapterRepo {
|
|||||||
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fetchChaptersExportInfo(userId: string, bookId: string, lang: 'fr' | 'en'): ChapterExportInfoResult[] {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
const query: string = `SELECT bc.chapter_id, bc.title, bc.chapter_order, GROUP_CONCAT(DISTINCT bcc.version) AS available_versions FROM book_chapters bc LEFT JOIN book_chapter_content bcc ON bc.chapter_id = bcc.chapter_id WHERE bc.author_id = ? AND bc.book_id = ? GROUP BY bc.chapter_id, bc.title, bc.chapter_order ORDER BY bc.chapter_order`;
|
||||||
|
const params: SQLiteValue[] = [userId, bookId];
|
||||||
|
return db.all(query, params) as ChapterExportInfoResult[];
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.error(`DB Error: ${error.message}`);
|
||||||
|
throw new Error(lang === 'fr' ? "Impossible de récupérer les informations d'export des chapitres." : 'Unable to retrieve chapters export info.');
|
||||||
|
}
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fetchSelectedChaptersContent(bookId: string, selections: ChapterSelectionParam[], lang: 'fr' | 'en'): SelectedChapterContentResult[] {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
const conditions: string[] = selections.map((): string => '(chapter.chapter_id = ? AND content.version = ?)');
|
||||||
|
const query: string = `SELECT chapter.chapter_id, chapter.title, chapter.chapter_order, content.content, content.version FROM book_chapters AS chapter INNER JOIN book_chapter_content AS content ON chapter.chapter_id = content.chapter_id WHERE chapter.book_id = ? AND (${conditions.join(' OR ')}) ORDER BY chapter.chapter_order`;
|
||||||
|
const params: SQLiteValue[] = [bookId];
|
||||||
|
for (const selection of selections) {
|
||||||
|
params.push(selection.chapterId, selection.version);
|
||||||
|
}
|
||||||
|
return db.all(query, params) as SelectedChapterContentResult[];
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.error(`DB Error: ${error.message}`);
|
||||||
|
throw new Error(lang === 'fr' ? 'Impossible de récupérer le contenu des chapitres sélectionnés.' : 'Unable to retrieve selected chapters content.');
|
||||||
|
}
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain, dialog, BrowserWindow } from 'electron';
|
||||||
|
import { writeFile } from 'fs/promises';
|
||||||
import { createHandler } from '../database/LocalSystem.js';
|
import { createHandler } from '../database/LocalSystem.js';
|
||||||
import Book, {BookSyncCompare, CompleteBook, SyncedBook} from '../database/models/Book.js';
|
import Book, {BookSyncCompare, CompleteBook, CompleteBookData, SyncedBook} from '../database/models/Book.js';
|
||||||
import type { BookProps } from '../database/models/Book.js';
|
import type { BookProps } from '../database/models/Book.js';
|
||||||
import Chapter from '../database/models/Chapter.js';
|
import Chapter, {ChapterExportInfo} from '../database/models/Chapter.js';
|
||||||
import type { ChapterProps } from '../database/models/Chapter.js';
|
import type { ChapterProps } from '../database/models/Chapter.js';
|
||||||
|
import {ChapterSelectionParam} from "../database/repositories/chapter.repository.js";
|
||||||
import Act, {ActProps} from "../database/models/Act.js";
|
import Act, {ActProps} from "../database/models/Act.js";
|
||||||
import Issue, {IssueProps} from "../database/models/Issue.js";
|
import Issue, {IssueProps} from "../database/models/Issue.js";
|
||||||
import Sync from "../database/models/Sync.js";
|
import Sync from "../database/models/Sync.js";
|
||||||
@@ -13,6 +15,7 @@ import GuideLine, {GuideLineAI} from "../database/models/GuideLine.js";
|
|||||||
import Incident from "../database/models/Incident.js";
|
import Incident from "../database/models/Incident.js";
|
||||||
import PlotPoint from "../database/models/PlotPoint.js";
|
import PlotPoint from "../database/models/PlotPoint.js";
|
||||||
import World, {WorldListResponse, WorldProps} from "../database/models/World.js";
|
import World, {WorldListResponse, WorldProps} from "../database/models/World.js";
|
||||||
|
import Export, {ExportResult} from "../database/models/Export.js";
|
||||||
|
|
||||||
interface UpdateBookBasicData {
|
interface UpdateBookBasicData {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -434,3 +437,65 @@ ipcMain.handle('db:book:tool:update', createHandler<UpdateBookToolData, boolean>
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// GET /book/export/info - Get chapters export info (available versions)
|
||||||
|
interface ExportInfoData {
|
||||||
|
bookId: string;
|
||||||
|
}
|
||||||
|
ipcMain.handle('db:book:export:info', createHandler<ExportInfoData, ChapterExportInfo[]>(
|
||||||
|
function(userId: string, data: ExportInfoData, lang: 'fr' | 'en'): ChapterExportInfo[] {
|
||||||
|
return Chapter.getChaptersExportInfo(userId, data.bookId, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// POST /book/export - Export book to file (EPUB/PDF/DOCX)
|
||||||
|
type ExportFormat = 'epub' | 'pdf' | 'docx';
|
||||||
|
|
||||||
|
interface ExportRequestData {
|
||||||
|
bookId: string;
|
||||||
|
format: ExportFormat;
|
||||||
|
selections: ChapterSelectionParam[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatExtensions: Record<ExportFormat, {ext: string; filterName: string}> = {
|
||||||
|
epub: {ext: 'epub', filterName: 'EPUB'},
|
||||||
|
pdf: {ext: 'pdf', filterName: 'PDF'},
|
||||||
|
docx: {ext: 'docx', filterName: 'Word Document'}
|
||||||
|
};
|
||||||
|
|
||||||
|
ipcMain.handle('db:book:export', createHandler<ExportRequestData, boolean>(
|
||||||
|
async function(userId: string, data: ExportRequestData, lang: 'fr' | 'en'): Promise<boolean> {
|
||||||
|
const bookData: CompleteBookData = Chapter.getCompleteBookDataWithSelections(userId, data.bookId, data.selections, lang);
|
||||||
|
|
||||||
|
let result: ExportResult;
|
||||||
|
switch (data.format) {
|
||||||
|
case 'epub':
|
||||||
|
result = await Export.transformToEpub(bookData);
|
||||||
|
break;
|
||||||
|
case 'pdf':
|
||||||
|
result = await Export.transformToPDF(bookData);
|
||||||
|
break;
|
||||||
|
case 'docx':
|
||||||
|
result = await Export.transformToDOCX(bookData);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(lang === 'fr' ? 'Format non supporté.' : 'Unsupported format.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatInfo = formatExtensions[data.format];
|
||||||
|
const focusedWindow: BrowserWindow | null = BrowserWindow.getFocusedWindow();
|
||||||
|
const dialogResult = await dialog.showSaveDialog(focusedWindow!, {
|
||||||
|
defaultPath: result.fileName,
|
||||||
|
filters: [{name: formatInfo.filterName, extensions: [formatInfo.ext]}]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dialogResult.canceled || !dialogResult.filePath) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeFile(dialogResult.filePath, result.buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|||||||
@@ -207,6 +207,11 @@
|
|||||||
"title": "Create a series",
|
"title": "Create a series",
|
||||||
"description": "Create a series to group multiple books.",
|
"description": "Create a series to group multiple books.",
|
||||||
"badge": "SERIES"
|
"badge": "SERIES"
|
||||||
|
},
|
||||||
|
"importBook": {
|
||||||
|
"title": "Import a book",
|
||||||
|
"description": "Import a book from a DOCX file.",
|
||||||
|
"badge": "IMPORT"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -640,9 +645,27 @@
|
|||||||
"objectsList": "Objects list",
|
"objectsList": "Objects list",
|
||||||
"bookGoals": "Book goals",
|
"bookGoals": "Book goals",
|
||||||
"quillsense": "QuillSense Settings",
|
"quillsense": "QuillSense Settings",
|
||||||
|
"export": "Export Book",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"notAvailable": "Option not available"
|
"notAvailable": "Option not available"
|
||||||
},
|
},
|
||||||
|
"exportOption": {
|
||||||
|
"title": "Export Your Book",
|
||||||
|
"description": "Choose the format and chapters to export.",
|
||||||
|
"format": "Format",
|
||||||
|
"selectFormat": "Select a format",
|
||||||
|
"chapters": "Chapters",
|
||||||
|
"selectAll": "Select all",
|
||||||
|
"deselectAll": "Deselect all",
|
||||||
|
"version": "Version",
|
||||||
|
"export": "Export",
|
||||||
|
"exporting": "Exporting...",
|
||||||
|
"noChapters": "No chapters available for export.",
|
||||||
|
"success": "Book exported successfully!",
|
||||||
|
"cancelled": "Export cancelled.",
|
||||||
|
"error": "Error exporting the book.",
|
||||||
|
"loadingChapters": "Loading chapters..."
|
||||||
|
},
|
||||||
"noBookHome": {
|
"noBookHome": {
|
||||||
"title": "Your work is waiting for its first words",
|
"title": "Your work is waiting for its first words",
|
||||||
"description": "This work does not have any chapters yet. To start writing, create your first chapter.",
|
"description": "This work does not have any chapters yet. To start writing, create your first chapter.",
|
||||||
@@ -987,6 +1010,7 @@
|
|||||||
"characters": "Characters",
|
"characters": "Characters",
|
||||||
"spells": "Spell Book",
|
"spells": "Spell Book",
|
||||||
"quillsense": "QuillSense (AI)",
|
"quillsense": "QuillSense (AI)",
|
||||||
|
"export": "Export",
|
||||||
"objects": "Objects",
|
"objects": "Objects",
|
||||||
"goals": "Goals"
|
"goals": "Goals"
|
||||||
},
|
},
|
||||||
@@ -1333,5 +1357,51 @@
|
|||||||
"enable_characters": "Enable character management for this book",
|
"enable_characters": "Enable character management for this book",
|
||||||
"enable_worlds": "Enable world management for this book",
|
"enable_worlds": "Enable world management for this book",
|
||||||
"enable_locations": "Enable location management for this book"
|
"enable_locations": "Enable location management for this book"
|
||||||
|
},
|
||||||
|
"importBook": {
|
||||||
|
"header": {
|
||||||
|
"title": "Import a Book"
|
||||||
|
},
|
||||||
|
"pickFile": "Choose a DOCX file",
|
||||||
|
"parsing": "Analyzing file...",
|
||||||
|
"chaptersDetected": "{count} chapters detected",
|
||||||
|
"noChaptersDetected": "No chapters detected in the file",
|
||||||
|
"fields": {
|
||||||
|
"title": {
|
||||||
|
"label": "Book Title",
|
||||||
|
"placeholder": "Enter the title"
|
||||||
|
},
|
||||||
|
"subTitle": {
|
||||||
|
"label": "Subtitle",
|
||||||
|
"placeholder": "Enter the subtitle"
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"label": "Summary",
|
||||||
|
"placeholder": "Enter a summary"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"label": "Book Type"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"label": "Chapter Version"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chapters": {
|
||||||
|
"title": "Chapters to import",
|
||||||
|
"words": "{count} words",
|
||||||
|
"selectAll": "Select all",
|
||||||
|
"deselectAll": "Deselect all"
|
||||||
|
},
|
||||||
|
"submit": "Import",
|
||||||
|
"importing": "Importing...",
|
||||||
|
"success": "Book imported successfully",
|
||||||
|
"error": {
|
||||||
|
"titleRequired": "Book title is required",
|
||||||
|
"typeRequired": "Book type is required",
|
||||||
|
"noChaptersSelected": "Select at least one chapter",
|
||||||
|
"parseFailed": "Error analyzing the file",
|
||||||
|
"importFailed": "Error during import",
|
||||||
|
"invalidFormat": "Invalid format. Only DOCX files are accepted"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,6 +207,11 @@
|
|||||||
"title": "Créer une série",
|
"title": "Créer une série",
|
||||||
"description": "Créez une série pour regrouper plusieurs livres.",
|
"description": "Créez une série pour regrouper plusieurs livres.",
|
||||||
"badge": "SÉRIE"
|
"badge": "SÉRIE"
|
||||||
|
},
|
||||||
|
"importBook": {
|
||||||
|
"title": "Importer une oeuvre",
|
||||||
|
"description": "Importez un livre à partir d'un fichier DOCX.",
|
||||||
|
"badge": "IMPORT"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -631,9 +636,27 @@
|
|||||||
"objectsList": "Liste des objets",
|
"objectsList": "Liste des objets",
|
||||||
"bookGoals": "Objectifs du livre",
|
"bookGoals": "Objectifs du livre",
|
||||||
"quillsense": "Parametres QuillSense",
|
"quillsense": "Parametres QuillSense",
|
||||||
|
"export": "Exporter le livre",
|
||||||
"save": "Sauvegarder",
|
"save": "Sauvegarder",
|
||||||
"notAvailable": "Option non disponible"
|
"notAvailable": "Option non disponible"
|
||||||
},
|
},
|
||||||
|
"exportOption": {
|
||||||
|
"title": "Exporter votre livre",
|
||||||
|
"description": "Choisissez le format et les chapitres à exporter.",
|
||||||
|
"format": "Format",
|
||||||
|
"selectFormat": "Sélectionner un format",
|
||||||
|
"chapters": "Chapitres",
|
||||||
|
"selectAll": "Tout sélectionner",
|
||||||
|
"deselectAll": "Tout désélectionner",
|
||||||
|
"version": "Version",
|
||||||
|
"export": "Exporter",
|
||||||
|
"exporting": "Exportation en cours...",
|
||||||
|
"noChapters": "Aucun chapitre disponible pour l'export.",
|
||||||
|
"success": "Livre exporté avec succès !",
|
||||||
|
"cancelled": "Export annulé.",
|
||||||
|
"error": "Erreur lors de l'exportation du livre.",
|
||||||
|
"loadingChapters": "Chargement des chapitres..."
|
||||||
|
},
|
||||||
"noBookHome": {
|
"noBookHome": {
|
||||||
"title": "Votre œuvre attend ses premiers mots",
|
"title": "Votre œuvre attend ses premiers mots",
|
||||||
"description": "Cette œuvre n'a pas encore de chapitres. Pour commencer à écrire, créez votre premier chapitre.",
|
"description": "Cette œuvre n'a pas encore de chapitres. Pour commencer à écrire, créez votre premier chapitre.",
|
||||||
@@ -953,7 +976,8 @@
|
|||||||
"spells": "Sortilèges",
|
"spells": "Sortilèges",
|
||||||
"objects": "Objets",
|
"objects": "Objets",
|
||||||
"goals": "Buts",
|
"goals": "Buts",
|
||||||
"quillsense": "QuillSense"
|
"quillsense": "QuillSense",
|
||||||
|
"export": "Export"
|
||||||
},
|
},
|
||||||
"basicInformationSetting": {
|
"basicInformationSetting": {
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1336,5 +1360,51 @@
|
|||||||
"enable_characters": "Activer la gestion des personnages pour ce livre",
|
"enable_characters": "Activer la gestion des personnages pour ce livre",
|
||||||
"enable_worlds": "Activer la gestion des mondes pour ce livre",
|
"enable_worlds": "Activer la gestion des mondes pour ce livre",
|
||||||
"enable_locations": "Activer la gestion des lieux pour ce livre"
|
"enable_locations": "Activer la gestion des lieux pour ce livre"
|
||||||
|
},
|
||||||
|
"importBook": {
|
||||||
|
"header": {
|
||||||
|
"title": "Importer un livre"
|
||||||
|
},
|
||||||
|
"pickFile": "Choisir un fichier DOCX",
|
||||||
|
"parsing": "Analyse du fichier en cours...",
|
||||||
|
"chaptersDetected": "{count} chapitres détectés",
|
||||||
|
"noChaptersDetected": "Aucun chapitre détecté dans le fichier",
|
||||||
|
"fields": {
|
||||||
|
"title": {
|
||||||
|
"label": "Titre du livre",
|
||||||
|
"placeholder": "Entrez le titre"
|
||||||
|
},
|
||||||
|
"subTitle": {
|
||||||
|
"label": "Sous-titre",
|
||||||
|
"placeholder": "Entrez le sous-titre"
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"label": "Résumé",
|
||||||
|
"placeholder": "Entrez un résumé"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"label": "Type de livre"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"label": "Version des chapitres"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chapters": {
|
||||||
|
"title": "Chapitres à importer",
|
||||||
|
"words": "{count} mots",
|
||||||
|
"selectAll": "Tout sélectionner",
|
||||||
|
"deselectAll": "Tout désélectionner"
|
||||||
|
},
|
||||||
|
"submit": "Importer",
|
||||||
|
"importing": "Import en cours...",
|
||||||
|
"success": "Livre importé avec succès",
|
||||||
|
"error": {
|
||||||
|
"titleRequired": "Le titre du livre est requis",
|
||||||
|
"typeRequired": "Le type de livre est requis",
|
||||||
|
"noChaptersSelected": "Sélectionnez au moins un chapitre",
|
||||||
|
"parseFailed": "Erreur lors de l'analyse du fichier",
|
||||||
|
"importFailed": "Erreur lors de l'import",
|
||||||
|
"invalidFormat": "Format invalide. Seuls les fichiers DOCX sont acceptés"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,6 +48,21 @@ export type TiptapNode = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type ExportFormat = 'epub' | 'pdf' | 'docx';
|
||||||
|
|
||||||
|
export interface ChapterExportInfo {
|
||||||
|
chapterId: string;
|
||||||
|
title: string;
|
||||||
|
chapterOrder: number;
|
||||||
|
availableVersions: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChapterExportSelection {
|
||||||
|
chapterId: string;
|
||||||
|
version: number;
|
||||||
|
selected: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const chapterVersions: SelectBoxProps[] = [
|
export const chapterVersions: SelectBoxProps[] = [
|
||||||
{value: '1', label: 'chapterVersions.prompt'},
|
{value: '1', label: 'chapterVersions.prompt'},
|
||||||
{value: '2', label: 'chapterVersions.draft'},
|
{value: '2', label: 'chapterVersions.draft'},
|
||||||
|
|||||||
@@ -212,6 +212,38 @@ export default class System{
|
|||||||
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; ${domain} path=/; ${secure} ${sameSite}`;
|
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; ${domain} path=/; ${secure} ${sameSite}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async authUploadFileToServer<T>(url: string, file: File, auth: string, lang: string = "fr"): Promise<T> {
|
||||||
|
try {
|
||||||
|
const formData: FormData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
formData.append('lang', lang);
|
||||||
|
formData.append('plateforme', window.electron.platform);
|
||||||
|
|
||||||
|
const response: AxiosResponse<T> = await axios({
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${auth}`,
|
||||||
|
},
|
||||||
|
url: configs.apiUrl + url,
|
||||||
|
params: {
|
||||||
|
lang: lang,
|
||||||
|
plateforme: window.electron.platform,
|
||||||
|
},
|
||||||
|
data: formData,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (axios.isAxiosError(e)) {
|
||||||
|
const serverMessage: string = e.response?.data?.message || e.response?.data || e.message;
|
||||||
|
throw new Error(serverMessage as string);
|
||||||
|
} else if (e instanceof Error) {
|
||||||
|
throw new Error(e.message);
|
||||||
|
} else {
|
||||||
|
throw new Error('An unexpected error occurred');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async authDeleteToServer<T>(url: string, data: {}, auth: string, lang: string = "fr"): Promise<T> {
|
public static async authDeleteToServer<T>(url: string, data: {}, auth: string, lang: string = "fr"): Promise<T> {
|
||||||
try {
|
try {
|
||||||
const response: AxiosResponse<T> = await axios({
|
const response: AxiosResponse<T> = await axios({
|
||||||
|
|||||||
316
package-lock.json
generated
316
package-lock.json
generated
@@ -26,10 +26,13 @@
|
|||||||
"autoprefixer": "^10.4.22",
|
"autoprefixer": "^10.4.22",
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
|
"docx": "^9.5.3",
|
||||||
"electron-updater": "^6.7.3",
|
"electron-updater": "^6.7.3",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
"next": "^16.0.3",
|
"next": "^16.0.3",
|
||||||
"next-intl": "^4.5.3",
|
"next-intl": "^4.5.3",
|
||||||
"node-sqlite3-wasm": "^0.8.51",
|
"node-sqlite3-wasm": "^0.8.51",
|
||||||
|
"pdfkit": "^0.17.2",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
@@ -39,6 +42,7 @@
|
|||||||
"@electron/notarize": "^3.1.1",
|
"@electron/notarize": "^3.1.1",
|
||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
|
"@types/pdfkit": "^0.17.5",
|
||||||
"@types/react": "^19.2.5",
|
"@types/react": "^19.2.5",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
@@ -2990,6 +2994,16 @@
|
|||||||
"undici-types": "~7.16.0"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/pdfkit": {
|
||||||
|
"version": "0.17.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pdfkit/-/pdfkit-0.17.5.tgz",
|
||||||
|
"integrity": "sha512-T3ZHnvF91HsEco5ClhBCOuBwobZfPcI2jaiSHybkkKYq4KhVIIurod94JVKvDIG0JXT6o3KiERC0X0//m8dyrg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/plist": {
|
"node_modules/@types/plist": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz",
|
||||||
@@ -3504,7 +3518,6 @@
|
|||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -3576,6 +3589,15 @@
|
|||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/brotli": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.28.1",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
|
||||||
@@ -4107,9 +4129,7 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
||||||
"dev": true,
|
"license": "MIT"
|
||||||
"license": "MIT",
|
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/crc": {
|
"node_modules/crc": {
|
||||||
"version": "3.8.0",
|
"version": "3.8.0",
|
||||||
@@ -4175,6 +4195,12 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/crypto-js": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
@@ -4320,6 +4346,12 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/dfa": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/dir-compare": {
|
"node_modules/dir-compare": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz",
|
||||||
@@ -4426,6 +4458,56 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/docx": {
|
||||||
|
"version": "9.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/docx/-/docx-9.5.3.tgz",
|
||||||
|
"integrity": "sha512-uFVrYiN2WKx1an884SS6mRu4JuCO10fpnRGQLYmbYgMLVAqqjrKPc2qMXsGj9JuDVrzdntGRw+y84bSjBxXqew==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^25.2.3",
|
||||||
|
"hash.js": "^1.1.7",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
|
"nanoid": "^5.1.3",
|
||||||
|
"xml": "^1.0.1",
|
||||||
|
"xml-js": "^1.6.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/docx/node_modules/@types/node": {
|
||||||
|
"version": "25.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz",
|
||||||
|
"integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/docx/node_modules/nanoid": {
|
||||||
|
"version": "5.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz",
|
||||||
|
"integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18 || >=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/docx/node_modules/undici-types": {
|
||||||
|
"version": "7.18.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||||
|
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "17.2.4",
|
"version": "17.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.4.tgz",
|
||||||
@@ -4967,7 +5049,6 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-equals": {
|
"node_modules/fast-equals": {
|
||||||
@@ -5067,6 +5148,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fontkit": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.5.12",
|
||||||
|
"brotli": "^1.3.2",
|
||||||
|
"clone": "^2.1.2",
|
||||||
|
"dfa": "^1.2.0",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"restructure": "^3.0.0",
|
||||||
|
"tiny-inflate": "^1.0.3",
|
||||||
|
"unicode-properties": "^1.4.0",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fontkit/node_modules/clone": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/foreground-child": {
|
"node_modules/foreground-child": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||||
@@ -5414,6 +5521,16 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hash.js": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
||||||
|
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"minimalistic-assert": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||||
@@ -5563,6 +5680,12 @@
|
|||||||
],
|
],
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/immediate": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/imurmurhash": {
|
"node_modules/imurmurhash": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
||||||
@@ -5589,7 +5712,6 @@
|
|||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/intl-messageformat": {
|
"node_modules/intl-messageformat": {
|
||||||
@@ -5668,6 +5790,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/isbinaryfile": {
|
"node_modules/isbinaryfile": {
|
||||||
"version": "5.0.7",
|
"version": "5.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
|
||||||
@@ -5753,6 +5881,13 @@
|
|||||||
"node": ">= 20"
|
"node": ">= 20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jpeg-exif": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==",
|
||||||
|
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||||
@@ -5810,6 +5945,48 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jszip": {
|
||||||
|
"version": "3.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||||
|
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||||
|
"license": "(MIT OR GPL-3.0-or-later)",
|
||||||
|
"dependencies": {
|
||||||
|
"lie": "~3.3.0",
|
||||||
|
"pako": "~1.0.2",
|
||||||
|
"readable-stream": "~2.3.6",
|
||||||
|
"setimmediate": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jszip/node_modules/readable-stream": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||||
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jszip/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/jszip/node_modules/string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
@@ -5826,6 +6003,15 @@
|
|||||||
"integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
|
"integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lie": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"immediate": "~3.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lightningcss": {
|
"node_modules/lightningcss": {
|
||||||
"version": "1.30.2",
|
"version": "1.30.2",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
|
||||||
@@ -6075,6 +6261,25 @@
|
|||||||
"url": "https://opencollective.com/parcel"
|
"url": "https://opencollective.com/parcel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/linebreak": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "0.0.8",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/linebreak/node_modules/base64-js": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/linkify-it": {
|
"node_modules/linkify-it": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||||
@@ -6282,6 +6487,12 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minimalistic-assert": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "10.1.2",
|
"version": "10.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz",
|
||||||
@@ -6916,6 +7127,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BlueOak-1.0.0"
|
"license": "BlueOak-1.0.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||||
|
"license": "(MIT AND Zlib)"
|
||||||
|
},
|
||||||
"node_modules/path-is-absolute": {
|
"node_modules/path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
@@ -6960,6 +7177,19 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/pdfkit": {
|
||||||
|
"version": "0.17.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.17.2.tgz",
|
||||||
|
"integrity": "sha512-UnwF5fXy08f0dnp4jchFYAROKMNTaPqb/xgR8GtCzIcqoTnbOqtp3bwKvO4688oHI6vzEEs8Q6vqqEnC5IUELw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
|
"fontkit": "^2.0.4",
|
||||||
|
"jpeg-exif": "^1.1.4",
|
||||||
|
"linebreak": "^1.1.0",
|
||||||
|
"png-js": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pe-library": {
|
"node_modules/pe-library": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz",
|
||||||
@@ -7015,6 +7245,11 @@
|
|||||||
"node": ">=10.4.0"
|
"node": ">=10.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/png-js": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g=="
|
||||||
|
},
|
||||||
"node_modules/po-parser": {
|
"node_modules/po-parser": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.1.1.tgz",
|
||||||
@@ -7095,6 +7330,12 @@
|
|||||||
"node": "^18.17.0 || >=20.5.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/progress": {
|
"node_modules/progress": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||||
@@ -7486,6 +7727,12 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/restructure": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/retry": {
|
"node_modules/retry": {
|
||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
||||||
@@ -7634,6 +7881,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/setimmediate": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/sharp": {
|
"node_modules/sharp": {
|
||||||
"version": "0.34.5",
|
"version": "0.34.5",
|
||||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
||||||
@@ -8129,6 +8382,12 @@
|
|||||||
"semver": "bin/semver"
|
"semver": "bin/semver"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-inflate": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tiny-typed-emitter": {
|
"node_modules/tiny-typed-emitter": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz",
|
||||||
@@ -8238,6 +8497,32 @@
|
|||||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/unicode-properties": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.0",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unicode-trie": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pako": "^0.2.5",
|
||||||
|
"tiny-inflate": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unicode-trie/node_modules/pako": {
|
||||||
|
"version": "0.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
|
||||||
|
"integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/unique-filename": {
|
"node_modules/unique-filename": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz",
|
||||||
@@ -8355,7 +8640,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/verror": {
|
"node_modules/verror": {
|
||||||
@@ -8470,6 +8754,24 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/xml": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/xml-js": {
|
||||||
|
"version": "1.6.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
|
||||||
|
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"xml-js": "bin/cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/xmlbuilder": {
|
"node_modules/xmlbuilder": {
|
||||||
"version": "15.1.1",
|
"version": "15.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "eritorsscribe",
|
"name": "eritorsscribe",
|
||||||
"productName": "ERitors Scribe",
|
"productName": "ERitors Scribe",
|
||||||
"version": "0.3.1",
|
"version": "0.4.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/electron/main.js",
|
"main": "dist/electron/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
"@electron/notarize": "^3.1.1",
|
"@electron/notarize": "^3.1.1",
|
||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
|
"@types/pdfkit": "^0.17.5",
|
||||||
"@types/react": "^19.2.5",
|
"@types/react": "^19.2.5",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
@@ -46,10 +47,13 @@
|
|||||||
"autoprefixer": "^10.4.22",
|
"autoprefixer": "^10.4.22",
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
|
"docx": "^9.5.3",
|
||||||
"electron-updater": "^6.7.3",
|
"electron-updater": "^6.7.3",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
"next": "^16.0.3",
|
"next": "^16.0.3",
|
||||||
"next-intl": "^4.5.3",
|
"next-intl": "^4.5.3",
|
||||||
"node-sqlite3-wasm": "^0.8.51",
|
"node-sqlite3-wasm": "^0.8.51",
|
||||||
|
"pdfkit": "^0.17.2",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user