Multi lingua, hotfixes and design
This commit is contained in:
232
client/dist/assets/index-9IOnhCkb.js
vendored
232
client/dist/assets/index-9IOnhCkb.js
vendored
File diff suppressed because one or more lines are too long
1
client/dist/assets/index-CZFZrYeL.css
vendored
Normal file
1
client/dist/assets/index-CZFZrYeL.css
vendored
Normal file
File diff suppressed because one or more lines are too long
453
client/dist/assets/index-D3_QgWye.js
vendored
Normal file
453
client/dist/assets/index-D3_QgWye.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
client/dist/assets/index-DS0xE7Ko.css
vendored
1
client/dist/assets/index-DS0xE7Ko.css
vendored
File diff suppressed because one or more lines are too long
4
client/dist/index.html
vendored
4
client/dist/index.html
vendored
@@ -8,8 +8,8 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Montserrat:ital,wght@0,100..900;1,100..900&family=Pacifico&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
|
||||
<script type="module" crossorigin src="/assets/index-9IOnhCkb.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DS0xE7Ko.css">
|
||||
<script type="module" crossorigin src="/assets/index-D3_QgWye.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CZFZrYeL.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
86
client/node_modules/.vite/deps/_metadata.json
generated
vendored
86
client/node_modules/.vite/deps/_metadata.json
generated
vendored
@@ -1,100 +1,145 @@
|
||||
{
|
||||
"hash": "deb88d1e",
|
||||
"hash": "e20034a6",
|
||||
"configHash": "e64eb32e",
|
||||
"lockfileHash": "fe4eb8a2",
|
||||
"browserHash": "c9f0ad5b",
|
||||
"lockfileHash": "b223a39a",
|
||||
"browserHash": "4e2530e7",
|
||||
"optimized": {
|
||||
"react": {
|
||||
"src": "../../../../node_modules/react/index.js",
|
||||
"file": "react.js",
|
||||
"fileHash": "80d72dea",
|
||||
"fileHash": "bc47155f",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react-dom": {
|
||||
"src": "../../../../node_modules/react-dom/index.js",
|
||||
"file": "react-dom.js",
|
||||
"fileHash": "3f8ff889",
|
||||
"fileHash": "f3537af4",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react/jsx-dev-runtime": {
|
||||
"src": "../../../../node_modules/react/jsx-dev-runtime.js",
|
||||
"file": "react_jsx-dev-runtime.js",
|
||||
"fileHash": "05119472",
|
||||
"fileHash": "dd4d0058",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react/jsx-runtime": {
|
||||
"src": "../../../../node_modules/react/jsx-runtime.js",
|
||||
"file": "react_jsx-runtime.js",
|
||||
"fileHash": "e0024c91",
|
||||
"fileHash": "49f980be",
|
||||
"needsInterop": true
|
||||
},
|
||||
"@tanstack/react-query": {
|
||||
"src": "../../../../node_modules/@tanstack/react-query/build/modern/index.js",
|
||||
"file": "@tanstack_react-query.js",
|
||||
"fileHash": "4d070117",
|
||||
"fileHash": "cb583fa8",
|
||||
"needsInterop": false
|
||||
},
|
||||
"axios": {
|
||||
"src": "../../../../node_modules/axios/index.js",
|
||||
"file": "axios.js",
|
||||
"fileHash": "a4419c2e",
|
||||
"fileHash": "b1a573d1",
|
||||
"needsInterop": false
|
||||
},
|
||||
"lucide-react": {
|
||||
"src": "../../../../node_modules/lucide-react/dist/esm/lucide-react.js",
|
||||
"file": "lucide-react.js",
|
||||
"fileHash": "92a1456b",
|
||||
"fileHash": "14fb8e72",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-dom/client": {
|
||||
"src": "../../../../node_modules/react-dom/client.js",
|
||||
"file": "react-dom_client.js",
|
||||
"fileHash": "86bf4335",
|
||||
"fileHash": "5d2ebd48",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react-konva": {
|
||||
"src": "../../../../node_modules/react-konva/es/ReactKonva.js",
|
||||
"file": "react-konva.js",
|
||||
"fileHash": "56221f90",
|
||||
"fileHash": "a0e0f67f",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-router-dom": {
|
||||
"src": "../../../../node_modules/react-router-dom/dist/index.js",
|
||||
"file": "react-router-dom.js",
|
||||
"fileHash": "457f2587",
|
||||
"fileHash": "fec8af05",
|
||||
"needsInterop": false
|
||||
},
|
||||
"zustand": {
|
||||
"src": "../../../../node_modules/zustand/esm/index.mjs",
|
||||
"file": "zustand.js",
|
||||
"fileHash": "848a6c38",
|
||||
"fileHash": "35ad42af",
|
||||
"needsInterop": false
|
||||
},
|
||||
"i18next": {
|
||||
"src": "../../../../node_modules/i18next/dist/esm/i18next.js",
|
||||
"file": "i18next.js",
|
||||
"fileHash": "3d2d9026",
|
||||
"fileHash": "9acaef61",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-i18next": {
|
||||
"src": "../../../../node_modules/react-i18next/dist/es/index.js",
|
||||
"file": "react-i18next.js",
|
||||
"fileHash": "78d9a217",
|
||||
"fileHash": "b0e3e0fb",
|
||||
"needsInterop": false
|
||||
},
|
||||
"i18next-browser-languagedetector": {
|
||||
"src": "../../../../node_modules/i18next-browser-languagedetector/dist/esm/i18nextBrowserLanguageDetector.js",
|
||||
"file": "i18next-browser-languagedetector.js",
|
||||
"fileHash": "09be5c13",
|
||||
"fileHash": "4cc94160",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@tiptap/react": {
|
||||
"src": "../../../../node_modules/@tiptap/react/dist/index.js",
|
||||
"file": "@tiptap_react.js",
|
||||
"fileHash": "b3a13a61",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@tiptap/starter-kit": {
|
||||
"src": "../../../../node_modules/@tiptap/starter-kit/dist/index.js",
|
||||
"file": "@tiptap_starter-kit.js",
|
||||
"fileHash": "a44103b6",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@tiptap/extension-underline": {
|
||||
"src": "../../../../node_modules/@tiptap/extension-underline/dist/index.js",
|
||||
"file": "@tiptap_extension-underline.js",
|
||||
"fileHash": "1e7a61a1",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@tiptap/extension-text-align": {
|
||||
"src": "../../../../node_modules/@tiptap/extension-text-align/dist/index.js",
|
||||
"file": "@tiptap_extension-text-align.js",
|
||||
"fileHash": "cd5adcf1",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@tiptap/extension-placeholder": {
|
||||
"src": "../../../../node_modules/@tiptap/extension-placeholder/dist/index.js",
|
||||
"file": "@tiptap_extension-placeholder.js",
|
||||
"fileHash": "8fd72abc",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
"chunk-DRQXKEUS": {
|
||||
"file": "chunk-DRQXKEUS.js"
|
||||
},
|
||||
"chunk-JYSI5OBP": {
|
||||
"file": "chunk-JYSI5OBP.js"
|
||||
},
|
||||
"chunk-6FQSNAE3": {
|
||||
"file": "chunk-6FQSNAE3.js"
|
||||
},
|
||||
"chunk-CITXGXPZ": {
|
||||
"file": "chunk-CITXGXPZ.js"
|
||||
},
|
||||
"chunk-DRQXKEUS": {
|
||||
"file": "chunk-DRQXKEUS.js"
|
||||
"chunk-KMJOTAPQ": {
|
||||
"file": "chunk-KMJOTAPQ.js"
|
||||
},
|
||||
"chunk-N2AN2IIV": {
|
||||
"file": "chunk-N2AN2IIV.js"
|
||||
},
|
||||
"chunk-DBGWLXJ5": {
|
||||
"file": "chunk-DBGWLXJ5.js"
|
||||
},
|
||||
"chunk-TJ6I66L4": {
|
||||
"file": "chunk-TJ6I66L4.js"
|
||||
@@ -102,9 +147,6 @@
|
||||
"chunk-RMSUKPN2": {
|
||||
"file": "chunk-RMSUKPN2.js"
|
||||
},
|
||||
"chunk-JYSI5OBP": {
|
||||
"file": "chunk-JYSI5OBP.js"
|
||||
},
|
||||
"chunk-7URR3GLA": {
|
||||
"file": "chunk-7URR3GLA.js"
|
||||
},
|
||||
|
||||
6
client/node_modules/.vite/deps/react-i18next.js
generated
vendored
6
client/node_modules/.vite/deps/react-i18next.js
generated
vendored
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
require_shim
|
||||
} from "./chunk-CITXGXPZ.js";
|
||||
import {
|
||||
keysFromSelector
|
||||
} from "./chunk-DRQXKEUS.js";
|
||||
import {
|
||||
require_shim
|
||||
} from "./chunk-CITXGXPZ.js";
|
||||
import {
|
||||
require_react
|
||||
} from "./chunk-7URR3GLA.js";
|
||||
|
||||
98
client/node_modules/.vite/deps/zustand.js
generated
vendored
98
client/node_modules/.vite/deps/zustand.js
generated
vendored
@@ -1,93 +1,14 @@
|
||||
import {
|
||||
require_shim
|
||||
} from "./chunk-CITXGXPZ.js";
|
||||
require_with_selector
|
||||
} from "./chunk-6FQSNAE3.js";
|
||||
import "./chunk-CITXGXPZ.js";
|
||||
import {
|
||||
require_react
|
||||
} from "./chunk-7URR3GLA.js";
|
||||
import {
|
||||
__commonJS,
|
||||
__toESM
|
||||
} from "./chunk-4MBMRILA.js";
|
||||
|
||||
// ../node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js
|
||||
var require_with_selector_development = __commonJS({
|
||||
"../node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js"(exports) {
|
||||
"use strict";
|
||||
(function() {
|
||||
function is(x, y) {
|
||||
return x === y && (0 !== x || 1 / x === 1 / y) || x !== x && y !== y;
|
||||
}
|
||||
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
|
||||
var React = require_react(), shim = require_shim(), objectIs = "function" === typeof Object.is ? Object.is : is, useSyncExternalStore = shim.useSyncExternalStore, useRef = React.useRef, useEffect = React.useEffect, useMemo = React.useMemo, useDebugValue2 = React.useDebugValue;
|
||||
exports.useSyncExternalStoreWithSelector = function(subscribe, getSnapshot, getServerSnapshot, selector, isEqual) {
|
||||
var instRef = useRef(null);
|
||||
if (null === instRef.current) {
|
||||
var inst = { hasValue: false, value: null };
|
||||
instRef.current = inst;
|
||||
} else inst = instRef.current;
|
||||
instRef = useMemo(
|
||||
function() {
|
||||
function memoizedSelector(nextSnapshot) {
|
||||
if (!hasMemo) {
|
||||
hasMemo = true;
|
||||
memoizedSnapshot = nextSnapshot;
|
||||
nextSnapshot = selector(nextSnapshot);
|
||||
if (void 0 !== isEqual && inst.hasValue) {
|
||||
var currentSelection = inst.value;
|
||||
if (isEqual(currentSelection, nextSnapshot))
|
||||
return memoizedSelection = currentSelection;
|
||||
}
|
||||
return memoizedSelection = nextSnapshot;
|
||||
}
|
||||
currentSelection = memoizedSelection;
|
||||
if (objectIs(memoizedSnapshot, nextSnapshot))
|
||||
return currentSelection;
|
||||
var nextSelection = selector(nextSnapshot);
|
||||
if (void 0 !== isEqual && isEqual(currentSelection, nextSelection))
|
||||
return memoizedSnapshot = nextSnapshot, currentSelection;
|
||||
memoizedSnapshot = nextSnapshot;
|
||||
return memoizedSelection = nextSelection;
|
||||
}
|
||||
var hasMemo = false, memoizedSnapshot, memoizedSelection, maybeGetServerSnapshot = void 0 === getServerSnapshot ? null : getServerSnapshot;
|
||||
return [
|
||||
function() {
|
||||
return memoizedSelector(getSnapshot());
|
||||
},
|
||||
null === maybeGetServerSnapshot ? void 0 : function() {
|
||||
return memoizedSelector(maybeGetServerSnapshot());
|
||||
}
|
||||
];
|
||||
},
|
||||
[getSnapshot, getServerSnapshot, selector, isEqual]
|
||||
);
|
||||
var value = useSyncExternalStore(subscribe, instRef[0], instRef[1]);
|
||||
useEffect(
|
||||
function() {
|
||||
inst.hasValue = true;
|
||||
inst.value = value;
|
||||
},
|
||||
[value]
|
||||
);
|
||||
useDebugValue2(value);
|
||||
return value;
|
||||
};
|
||||
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
|
||||
})();
|
||||
}
|
||||
});
|
||||
|
||||
// ../node_modules/use-sync-external-store/shim/with-selector.js
|
||||
var require_with_selector = __commonJS({
|
||||
"../node_modules/use-sync-external-store/shim/with-selector.js"(exports, module) {
|
||||
"use strict";
|
||||
if (false) {
|
||||
module.exports = null;
|
||||
} else {
|
||||
module.exports = require_with_selector_development();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ../node_modules/zustand/esm/vanilla.mjs
|
||||
var createStoreImpl = (createState) => {
|
||||
let state;
|
||||
@@ -170,17 +91,4 @@ export {
|
||||
react as default,
|
||||
useStore
|
||||
};
|
||||
/*! Bundled license information:
|
||||
|
||||
use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js:
|
||||
(**
|
||||
* @license React
|
||||
* use-sync-external-store-shim/with-selector.development.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
*/
|
||||
//# sourceMappingURL=zustand.js.map
|
||||
|
||||
8
client/node_modules/.vite/deps/zustand.js.map
generated
vendored
8
client/node_modules/.vite/deps/zustand.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@@ -14,6 +14,12 @@
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toast": "^1.1.5",
|
||||
"@tanstack/react-query": "^5.17.0",
|
||||
"@tiptap/extension-placeholder": "^3.22.2",
|
||||
"@tiptap/extension-text-align": "^3.22.2",
|
||||
"@tiptap/extension-underline": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2",
|
||||
"@tiptap/react": "^3.22.2",
|
||||
"@tiptap/starter-kit": "^3.22.2",
|
||||
"axios": "^1.6.5",
|
||||
"clsx": "^2.1.0",
|
||||
"i18next": "^26.0.3",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import { BookOpen, Image, Settings } from 'lucide-react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -7,6 +7,7 @@ import AuthModal from './components/auth/AuthModal';
|
||||
import CoverDesigner from './components/cover-designer/CoverDesigner';
|
||||
import BookGenerator from './components/book-generator/BookGenerator';
|
||||
import SettingsPage from './components/SettingsPage';
|
||||
import OnboardingPage from './components/OnboardingPage';
|
||||
|
||||
function App() {
|
||||
const { t } = useTranslation();
|
||||
@@ -23,12 +24,12 @@ function App() {
|
||||
<header className="bg-white border-b border-gray-200 sticky top-0 z-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center justify-between h-16">
|
||||
<div className="flex items-center gap-2">
|
||||
<a href="/" className="flex items-center gap-2 no-underline">
|
||||
<div className="w-8 h-8 bg-gradient-to-br from-primary-500 to-accent-500 rounded-lg flex items-center justify-center">
|
||||
<BookOpen className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<h1 className="text-xl font-bold text-gray-900">CreaBook</h1>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="flex items-center gap-1">
|
||||
@@ -84,7 +85,7 @@ function App() {
|
||||
{/* Main Content */}
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<Routes>
|
||||
<Route path="/" element={<Navigate to="/covers" replace />} />
|
||||
<Route path="/" element={<OnboardingPage />} />
|
||||
<Route path="/covers" element={<CoverDesigner />} />
|
||||
<Route path="/books" element={<BookGenerator />} />
|
||||
<Route path="/settings" element={<SettingsPage />} />
|
||||
|
||||
124
client/src/components/OnboardingPage.tsx
Normal file
124
client/src/components/OnboardingPage.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Globe, BookOpen, Wand2, Edit3 } from 'lucide-react';
|
||||
import { booksApi } from '../services/api';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
export default function OnboardingPage() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [idea, setIdea] = useState('');
|
||||
const [genre, setGenre] = useState('fiction');
|
||||
|
||||
const { data: genresData } = useQuery({
|
||||
queryKey: ['genres'],
|
||||
queryFn: async () => {
|
||||
const response = await booksApi.getGenres();
|
||||
return response.data.genres;
|
||||
},
|
||||
});
|
||||
|
||||
const handleLanguageChange = (lang: string) => {
|
||||
i18n.changeLanguage(lang);
|
||||
};
|
||||
|
||||
const startJourney = (useAI: boolean) => {
|
||||
navigate('/books', { state: { autoBook: { idea, genre, useAI } } });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-3xl mx-auto py-12 px-6">
|
||||
<div className="text-center mb-12">
|
||||
<h1 className="text-4xl font-extrabold text-gray-900 mb-4">{t('onboarding.title')}</h1>
|
||||
<p className="text-lg text-gray-600">{t('onboarding.subtitle')}</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-12">
|
||||
{/* Step 1: Language */}
|
||||
<section className="card">
|
||||
<h2 className="text-xl font-bold flex items-center gap-2 mb-4">
|
||||
<Globe className="text-primary-500 w-6 h-6" />
|
||||
{t('onboarding.step1')}
|
||||
</h2>
|
||||
<div className="flex gap-4">
|
||||
<button
|
||||
onClick={() => handleLanguageChange('en')}
|
||||
className={`flex-1 py-3 px-4 rounded-lg font-medium transition-all ${
|
||||
i18n.language === 'en'
|
||||
? 'bg-primary-500 text-white shadow-md'
|
||||
: 'bg-white text-gray-700 border border-gray-200 hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
English
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleLanguageChange('es')}
|
||||
className={`flex-1 py-3 px-4 rounded-lg font-medium transition-all ${
|
||||
i18n.language === 'es'
|
||||
? 'bg-primary-500 text-white shadow-md'
|
||||
: 'bg-white text-gray-700 border border-gray-200 hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
Español
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Step 2: Book Idea */}
|
||||
<section className="card">
|
||||
<h2 className="text-xl font-bold flex items-center gap-2 mb-4">
|
||||
<BookOpen className="text-primary-500 w-6 h-6" />
|
||||
{t('onboarding.step2')}
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="label">{t('onboarding.genreLabel')}</label>
|
||||
<select
|
||||
value={genre}
|
||||
onChange={(e) => setGenre(e.target.value)}
|
||||
className="input bg-white"
|
||||
>
|
||||
{genresData?.map((g: any) => (
|
||||
<option key={g.id} value={g.id}>
|
||||
{String(t(`genres.${g.id}.name`, g.name))}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<textarea
|
||||
rows={4}
|
||||
value={idea}
|
||||
onChange={(e) => setIdea(e.target.value)}
|
||||
placeholder={t('onboarding.ideaPlaceholder')}
|
||||
className="input text-lg resize-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Step 3: Progression */}
|
||||
<section className="text-center pt-4">
|
||||
<h2 className="text-xl font-bold mb-6">{t('onboarding.step3')}</h2>
|
||||
<div className="grid sm:grid-cols-2 gap-4">
|
||||
<button
|
||||
onClick={() => startJourney(false)}
|
||||
className="flex flex-col items-center justify-center p-6 bg-white border-2 border-gray-200 hover:border-gray-400 rounded-xl transition-all"
|
||||
>
|
||||
<Edit3 className="w-10 h-10 text-gray-500 mb-3" />
|
||||
<span className="font-semibold text-gray-800 text-lg">{t('onboarding.btnFree')}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => startJourney(true)}
|
||||
className="flex flex-col items-center justify-center p-6 bg-primary-50 border-2 border-primary-500 hover:bg-primary-100 rounded-xl transition-all transform hover:scale-[1.02]"
|
||||
>
|
||||
<Wand2 className="w-10 h-10 text-primary-600 mb-3" />
|
||||
<span className="font-semibold text-primary-800 text-lg">{t('onboarding.btnAI')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
219
client/src/components/RichTextEditor.tsx
Normal file
219
client/src/components/RichTextEditor.tsx
Normal file
@@ -0,0 +1,219 @@
|
||||
import { useEditor, EditorContent } from '@tiptap/react';
|
||||
import StarterKit from '@tiptap/starter-kit';
|
||||
import Underline from '@tiptap/extension-underline';
|
||||
import TextAlign from '@tiptap/extension-text-align';
|
||||
import Placeholder from '@tiptap/extension-placeholder';
|
||||
import {
|
||||
Bold, Italic, Underline as UnderlineIcon, Strikethrough,
|
||||
List, ListOrdered, AlignLeft, AlignCenter, AlignRight,
|
||||
Undo2, Redo2, Heading1, Heading2, Heading3, Quote, Minus,
|
||||
} from 'lucide-react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
interface RichTextEditorProps {
|
||||
content: string;
|
||||
onChange: (html: string) => void;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
function ToolbarButton({
|
||||
onClick,
|
||||
active,
|
||||
children,
|
||||
title,
|
||||
}: {
|
||||
onClick: () => void;
|
||||
active?: boolean;
|
||||
children: React.ReactNode;
|
||||
title: string;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
title={title}
|
||||
className={`p-1.5 rounded transition-colors ${
|
||||
active
|
||||
? 'bg-primary-100 text-primary-700'
|
||||
: 'text-gray-500 hover:bg-gray-100 hover:text-gray-700'
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default function RichTextEditor({ content, onChange, placeholder }: RichTextEditorProps) {
|
||||
const editor = useEditor({
|
||||
extensions: [
|
||||
StarterKit.configure({
|
||||
heading: { levels: [1, 2, 3] },
|
||||
}),
|
||||
Underline,
|
||||
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
||||
Placeholder.configure({
|
||||
placeholder: placeholder || 'Start writing...',
|
||||
}),
|
||||
],
|
||||
content,
|
||||
onUpdate: ({ editor }) => {
|
||||
onChange(editor.getHTML());
|
||||
},
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class:
|
||||
'prose prose-lg max-w-none min-h-[500px] focus:outline-none p-6 font-serif leading-relaxed text-center',
|
||||
},
|
||||
},
|
||||
onCreate: ({ editor }) => {
|
||||
// Set default text alignment to center for completely new/empty content
|
||||
if (!content || content.trim() === '' || content.trim() === '<p></p>') {
|
||||
editor.chain().focus().setTextAlign('center').run();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Sync external content changes (e.g. AI-generated content)
|
||||
useEffect(() => {
|
||||
if (editor && content !== editor.getHTML()) {
|
||||
editor.commands.setContent(content);
|
||||
}
|
||||
}, [content]);
|
||||
|
||||
if (!editor) return null;
|
||||
|
||||
return (
|
||||
<div className="border border-gray-200 rounded-lg overflow-hidden">
|
||||
{/* Toolbar */}
|
||||
<div className="flex flex-wrap items-center gap-0.5 px-3 py-2 bg-gray-50 border-b border-gray-200">
|
||||
{/* Undo / Redo */}
|
||||
<ToolbarButton onClick={() => editor.chain().focus().undo().run()} title="Undo">
|
||||
<Undo2 className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton onClick={() => editor.chain().focus().redo().run()} title="Redo">
|
||||
<Redo2 className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
|
||||
<div className="w-px h-5 bg-gray-300 mx-1" />
|
||||
|
||||
{/* Headings */}
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
||||
active={editor.isActive('heading', { level: 1 })}
|
||||
title="Heading 1"
|
||||
>
|
||||
<Heading1 className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
||||
active={editor.isActive('heading', { level: 2 })}
|
||||
title="Heading 2"
|
||||
>
|
||||
<Heading2 className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
||||
active={editor.isActive('heading', { level: 3 })}
|
||||
title="Heading 3"
|
||||
>
|
||||
<Heading3 className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
|
||||
<div className="w-px h-5 bg-gray-300 mx-1" />
|
||||
|
||||
{/* Inline formatting */}
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
active={editor.isActive('bold')}
|
||||
title="Bold"
|
||||
>
|
||||
<Bold className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
active={editor.isActive('italic')}
|
||||
title="Italic"
|
||||
>
|
||||
<Italic className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().toggleUnderline().run()}
|
||||
active={editor.isActive('underline')}
|
||||
title="Underline"
|
||||
>
|
||||
<UnderlineIcon className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().toggleStrike().run()}
|
||||
active={editor.isActive('strike')}
|
||||
title="Strikethrough"
|
||||
>
|
||||
<Strikethrough className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
|
||||
<div className="w-px h-5 bg-gray-300 mx-1" />
|
||||
|
||||
{/* Lists */}
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
||||
active={editor.isActive('bulletList')}
|
||||
title="Bullet List"
|
||||
>
|
||||
<List className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().toggleOrderedList().run()}
|
||||
active={editor.isActive('orderedList')}
|
||||
title="Ordered List"
|
||||
>
|
||||
<ListOrdered className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
|
||||
<div className="w-px h-5 bg-gray-300 mx-1" />
|
||||
|
||||
{/* Alignment */}
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().setTextAlign('left').run()}
|
||||
active={editor.isActive({ textAlign: 'left' })}
|
||||
title="Align Left"
|
||||
>
|
||||
<AlignLeft className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().setTextAlign('center').run()}
|
||||
active={editor.isActive({ textAlign: 'center' })}
|
||||
title="Align Center"
|
||||
>
|
||||
<AlignCenter className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().setTextAlign('right').run()}
|
||||
active={editor.isActive({ textAlign: 'right' })}
|
||||
title="Align Right"
|
||||
>
|
||||
<AlignRight className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
|
||||
<div className="w-px h-5 bg-gray-300 mx-1" />
|
||||
|
||||
{/* Block elements */}
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().toggleBlockquote().run()}
|
||||
active={editor.isActive('blockquote')}
|
||||
title="Blockquote"
|
||||
>
|
||||
<Quote className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
onClick={() => editor.chain().focus().setHorizontalRule().run()}
|
||||
title="Horizontal Rule"
|
||||
>
|
||||
<Minus className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
|
||||
{/* Editor content */}
|
||||
<EditorContent editor={editor} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,18 +1,21 @@
|
||||
import { useState } from 'react';
|
||||
import { Wand2, ChevronLeft, ChevronRight, Sparkles } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useBookStore } from '../../stores/bookStore';
|
||||
import { booksApi } from '../../services/api';
|
||||
import RichTextEditor from '../RichTextEditor';
|
||||
|
||||
export default function BookEditor() {
|
||||
const { activeBook, updateChapterContent, setCurrentChapter } = useBookStore();
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const [showAIHelp, setShowAIHelp] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!activeBook || !activeBook.outline) {
|
||||
return (
|
||||
<div className="card text-center py-12">
|
||||
<p className="text-gray-500">
|
||||
Generate an outline first to start writing your book.
|
||||
{t('bookEditor.noOutline')}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@@ -51,6 +54,7 @@ export default function BookEditor() {
|
||||
previousChapter?.content
|
||||
);
|
||||
|
||||
console.log('Chapter generation response:', response.data);
|
||||
updateChapterContent(activeBook.currentChapter, response.data.content);
|
||||
} catch (error) {
|
||||
console.error('Failed to generate chapter:', error);
|
||||
@@ -69,7 +73,7 @@ export default function BookEditor() {
|
||||
{/* Chapter Navigation */}
|
||||
<div className="space-y-4">
|
||||
<div className="card">
|
||||
<h3 className="font-semibold text-gray-900 mb-4">Chapters</h3>
|
||||
<h3 className="font-semibold text-gray-900 mb-4">{t('bookEditor.chapters')}</h3>
|
||||
<div className="space-y-2">
|
||||
{activeBook.outline.chapters.map((chapter) => (
|
||||
<button
|
||||
@@ -88,7 +92,7 @@ export default function BookEditor() {
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium truncate">{chapter.title}</p>
|
||||
{chapter.content && (
|
||||
<span className="text-xs text-green-600">✓ Written</span>
|
||||
<span className="text-xs text-green-600">✓ {t('bookEditor.written')}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -99,7 +103,7 @@ export default function BookEditor() {
|
||||
|
||||
{/* Chapter Info */}
|
||||
<div className="card">
|
||||
<h4 className="font-medium text-gray-900 mb-2">Chapter Summary</h4>
|
||||
<h4 className="font-medium text-gray-900 mb-2">{t('bookEditor.chapterSummary')}</h4>
|
||||
<p className="text-sm text-gray-600">{currentChapter?.summary}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -122,7 +126,7 @@ export default function BookEditor() {
|
||||
{currentChapter?.title}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500">
|
||||
Chapter {activeBook.currentChapter} of {activeBook.outline.chapters.length}
|
||||
{t('bookEditor.chapterOf', { current: activeBook.currentChapter, total: activeBook.outline.chapters.length })}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
@@ -140,7 +144,7 @@ export default function BookEditor() {
|
||||
className="btn-secondary flex items-center gap-2"
|
||||
>
|
||||
<Sparkles className="w-4 h-4" />
|
||||
AI Assist
|
||||
{t('bookEditor.aiAssist')}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleGenerateChapter}
|
||||
@@ -148,7 +152,7 @@ export default function BookEditor() {
|
||||
className="btn-primary flex items-center gap-2"
|
||||
>
|
||||
<Wand2 className="w-4 h-4" />
|
||||
{isGenerating ? 'Generating...' : 'Generate Chapter'}
|
||||
{isGenerating ? t('bookEditor.generating') : t('bookEditor.generateChapter')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -156,22 +160,22 @@ export default function BookEditor() {
|
||||
{/* AI Help Panel */}
|
||||
{showAIHelp && (
|
||||
<div className="mb-6 bg-gradient-to-r from-accent-50 to-primary-50 rounded-lg p-4 border border-accent-200">
|
||||
<h4 className="font-medium text-gray-900 mb-3">AI Writing Assistant</h4>
|
||||
<h4 className="font-medium text-gray-900 mb-3">{t('bookEditor.aiWritingAssistant')}</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button
|
||||
onClick={handleExpandText}
|
||||
className="text-sm px-3 py-1.5 bg-white rounded-lg border border-gray-200 hover:border-accent-300 transition-colors"
|
||||
>
|
||||
✨ Expand this section
|
||||
✨ {t('bookEditor.expandSection')}
|
||||
</button>
|
||||
<button className="text-sm px-3 py-1.5 bg-white rounded-lg border border-gray-200 hover:border-accent-300 transition-colors">
|
||||
📝 Improve prose
|
||||
📝 {t('bookEditor.improveProse')}
|
||||
</button>
|
||||
<button className="text-sm px-3 py-1.5 bg-white rounded-lg border border-gray-200 hover:border-accent-300 transition-colors">
|
||||
💡 Add description
|
||||
💡 {t('bookEditor.addDescription')}
|
||||
</button>
|
||||
<button className="text-sm px-3 py-1.5 bg-white rounded-lg border border-gray-200 hover:border-accent-300 transition-colors">
|
||||
🔄 Rewrite paragraph
|
||||
🔄 {t('bookEditor.rewriteParagraph')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -179,24 +183,13 @@ export default function BookEditor() {
|
||||
|
||||
{/* Editor */}
|
||||
<div className="min-h-[500px]">
|
||||
{currentChapter?.content ? (
|
||||
<textarea
|
||||
value={currentChapter.content}
|
||||
onChange={(e) =>
|
||||
updateChapterContent(activeBook.currentChapter, e.target.value)
|
||||
<RichTextEditor
|
||||
content={currentChapter?.content || ''}
|
||||
onChange={(html) =>
|
||||
updateChapterContent(activeBook.currentChapter, html)
|
||||
}
|
||||
className="w-full h-[600px] p-6 font-serif text-lg leading-relaxed border-0 focus:ring-0 resize-y outline-none"
|
||||
placeholder="Start writing or use AI to generate this chapter..."
|
||||
placeholder={t('bookEditor.editorPlaceholder')}
|
||||
/>
|
||||
) : (
|
||||
<div className="h-[500px] flex items-center justify-center text-gray-400">
|
||||
<div className="text-center">
|
||||
<Wand2 className="w-12 h-12 mx-auto mb-3 opacity-50" />
|
||||
<p>Click "Generate Chapter" to create content with AI</p>
|
||||
<p className="text-sm mt-1">Or start writing manually</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Word Count */}
|
||||
@@ -205,10 +198,10 @@ export default function BookEditor() {
|
||||
{currentChapter?.content
|
||||
? currentChapter.content.split(/\s+/).length
|
||||
: 0}{' '}
|
||||
words
|
||||
{t('bookEditor.words')}
|
||||
</span>
|
||||
<span>
|
||||
Last updated:{' '}
|
||||
{t('bookEditor.lastUpdated')}{' '}
|
||||
{new Date(activeBook.updatedAt).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { BookOpen, Wand2, Edit3, Users, FileText } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import GenreSelector from './GenreSelector';
|
||||
import BookOutlineGenerator from './BookOutlineGenerator';
|
||||
import BookEditor from './BookEditor';
|
||||
@@ -10,6 +12,31 @@ import { useQuery } from '@tanstack/react-query';
|
||||
export default function BookGenerator() {
|
||||
const [activeTab, setActiveTab] = useState<'genre' | 'outline' | 'editor' | 'characters'>('genre');
|
||||
const { activeBook, setActiveBook, setOutline } = useBookStore();
|
||||
const location = useLocation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Auto-initialize from onboarding page state
|
||||
useEffect(() => {
|
||||
const state = location.state as { autoBook?: { idea: string; genre: string; useAI: boolean } } | null;
|
||||
if (state?.autoBook) {
|
||||
const { idea, genre, useAI } = state.autoBook;
|
||||
const newBook: Book = {
|
||||
id: `book-${Date.now()}`,
|
||||
title: idea.substring(0, 50) || 'Untitled',
|
||||
genre,
|
||||
idea,
|
||||
outline: null,
|
||||
characters: [],
|
||||
currentChapter: 1,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
setActiveBook(newBook);
|
||||
setActiveTab(useAI ? 'outline' : 'editor');
|
||||
// Clear the state so refreshing doesn't re-trigger
|
||||
window.history.replaceState({}, document.title);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const { data: genresData } = useQuery({
|
||||
queryKey: ['genres'],
|
||||
@@ -46,9 +73,9 @@ export default function BookGenerator() {
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Book Generator</h2>
|
||||
<h2 className="text-2xl font-bold text-gray-900">{t('bookGenerator.title')}</h2>
|
||||
<p className="text-gray-500 mt-1">
|
||||
Generate book ideas and write with AI assistance
|
||||
{t('bookGenerator.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -64,7 +91,7 @@ export default function BookGenerator() {
|
||||
disabled={!activeBook && activeTab !== 'genre'}
|
||||
>
|
||||
<BookOpen className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Genre</span>
|
||||
<span className="hidden sm:inline">{t('bookGenerator.tabGenre')}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('outline')}
|
||||
@@ -76,7 +103,7 @@ export default function BookGenerator() {
|
||||
}`}
|
||||
>
|
||||
<FileText className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Outline</span>
|
||||
<span className="hidden sm:inline">{t('bookGenerator.tabOutline')}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('editor')}
|
||||
@@ -88,7 +115,7 @@ export default function BookGenerator() {
|
||||
}`}
|
||||
>
|
||||
<Edit3 className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Write</span>
|
||||
<span className="hidden sm:inline">{t('bookGenerator.tabWrite')}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('characters')}
|
||||
@@ -100,7 +127,7 @@ export default function BookGenerator() {
|
||||
}`}
|
||||
>
|
||||
<Users className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Characters</span>
|
||||
<span className="hidden sm:inline">{t('bookGenerator.tabCharacters')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -133,6 +160,7 @@ export default function BookGenerator() {
|
||||
function CharacterGenerator() {
|
||||
const { activeBook, addCharacter } = useBookStore();
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleGenerateCharacters = async () => {
|
||||
if (!activeBook) return;
|
||||
@@ -166,7 +194,7 @@ function CharacterGenerator() {
|
||||
<div className="card">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
Character Development
|
||||
{t('characters.title')}
|
||||
</h3>
|
||||
<button
|
||||
onClick={handleGenerateCharacters}
|
||||
@@ -174,7 +202,7 @@ function CharacterGenerator() {
|
||||
className="btn-primary flex items-center gap-2"
|
||||
>
|
||||
<Wand2 className="w-4 h-4" />
|
||||
{isGenerating ? 'Generating...' : 'Generate Characters'}
|
||||
{isGenerating ? t('characters.generating') : t('characters.generate')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -182,7 +210,7 @@ function CharacterGenerator() {
|
||||
<div className="text-center py-8">
|
||||
<Users className="w-12 h-12 text-gray-300 mx-auto mb-3" />
|
||||
<p className="text-gray-500">
|
||||
No characters yet. Click "Generate Characters" to create AI-suggested characters.
|
||||
{t('characters.empty')}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -206,11 +234,11 @@ function CharacterGenerator() {
|
||||
</div>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div>
|
||||
<span className="text-gray-500">Traits:</span>
|
||||
<span className="text-gray-500">{t('characters.traits')}:</span>
|
||||
<p className="text-gray-700">{character.traits?.join(', ') || 'N/A'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">Motivation:</span>
|
||||
<span className="text-gray-500">{t('characters.motivation')}:</span>
|
||||
<p className="text-gray-700">{character.motivation || 'N/A'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { Wand2, CheckCircle, Loader2 } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Book, useBookStore } from '../../stores/bookStore';
|
||||
import { booksApi } from '../../services/api';
|
||||
|
||||
@@ -12,6 +13,7 @@ export default function BookOutlineGenerator({ book, onComplete }: BookOutlineGe
|
||||
const { activeBook } = useBookStore();
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const [error, setError] = useState<string>('');
|
||||
const { t } = useTranslation();
|
||||
|
||||
const outline = activeBook?.outline;
|
||||
|
||||
@@ -22,9 +24,12 @@ export default function BookOutlineGenerator({ book, onComplete }: BookOutlineGe
|
||||
try {
|
||||
const response = await booksApi.generateOutline(book.genre, book.idea, book.title);
|
||||
const generatedOutline = response.data.outline;
|
||||
console.log('Generated outline:', generatedOutline);
|
||||
console.log('Outline chapters:', generatedOutline?.chapters);
|
||||
onComplete(generatedOutline);
|
||||
} catch (err: any) {
|
||||
setError(err.response?.data?.error || 'Failed to generate outline');
|
||||
console.error('Outline generation error:', err);
|
||||
setError(err.response?.data?.error || t('bookOutline.failedToGenerate'));
|
||||
} finally {
|
||||
setIsGenerating(false);
|
||||
}
|
||||
@@ -36,8 +41,8 @@ export default function BookOutlineGenerator({ book, onComplete }: BookOutlineGe
|
||||
<div className="card bg-gradient-to-r from-primary-50 to-accent-50">
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900">{book.title || 'Untitled Book'}</h3>
|
||||
<p className="text-sm text-gray-600 mt-1 capitalize">Genre: {book.genre}</p>
|
||||
<h3 className="font-semibold text-gray-900">{book.title || t('bookOutline.untitledBook')}</h3>
|
||||
<p className="text-sm text-gray-600 mt-1 capitalize">{t('bookOutline.genre')}: {book.genre}</p>
|
||||
<p className="text-sm text-gray-500 mt-2 line-clamp-2">{book.idea}</p>
|
||||
</div>
|
||||
{!outline && (
|
||||
@@ -49,12 +54,12 @@ export default function BookOutlineGenerator({ book, onComplete }: BookOutlineGe
|
||||
{isGenerating ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
Generating...
|
||||
{t('bookOutline.generatingOutline')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Wand2 className="w-4 h-4" />
|
||||
Generate Outline
|
||||
{t('bookOutline.generateOutline')}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
@@ -72,22 +77,37 @@ export default function BookOutlineGenerator({ book, onComplete }: BookOutlineGe
|
||||
{/* Outline Display */}
|
||||
{outline && (
|
||||
<div className="card">
|
||||
{outline.error ? (
|
||||
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-4">
|
||||
<h4 className="text-red-800 font-medium mb-2">Generation Error</h4>
|
||||
<p className="text-red-700 text-sm">{outline.error}</p>
|
||||
{outline.raw && (
|
||||
<details className="mt-2">
|
||||
<summary className="text-red-600 cursor-pointer">Raw AI Response</summary>
|
||||
<pre className="text-xs text-gray-600 mt-2 whitespace-pre-wrap bg-gray-100 p-2 rounded">
|
||||
{outline.raw}
|
||||
</pre>
|
||||
</details>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
<CheckCircle className="w-5 h-5 text-green-500" />
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
Generated Outline: {outline.title || book.title}
|
||||
{t('bookOutline.generatedOutline')}: {outline.title || book.title}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{outline.logline && (
|
||||
<div className="mb-6 pb-6 border-b border-gray-200">
|
||||
<h4 className="text-sm font-medium text-gray-500 mb-2">Logline</h4>
|
||||
<h4 className="text-sm font-medium text-gray-500 mb-2">{t('bookOutline.logline')}</h4>
|
||||
<p className="text-gray-900 italic">{outline.logline}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-gray-500 mb-4">Chapter Outline</h4>
|
||||
<h4 className="text-sm font-medium text-gray-500 mb-4">{t('bookOutline.chapterOutline')}</h4>
|
||||
<div className="space-y-3">
|
||||
{outline.chapters?.map((chapter: any, index: number) => (
|
||||
<div
|
||||
@@ -100,10 +120,10 @@ export default function BookOutlineGenerator({ book, onComplete }: BookOutlineGe
|
||||
</span>
|
||||
<div className="flex-1">
|
||||
<h5 className="font-medium text-gray-900">
|
||||
{chapter.title || `Chapter ${chapter.number || index + 1}`}
|
||||
{chapter.title || `${t('bookOutline.chapter')} ${chapter.number || index + 1}`}
|
||||
</h5>
|
||||
<p className="text-sm text-gray-600 mt-1">
|
||||
{chapter.summary || 'No summary available'}
|
||||
{chapter.summary || t('bookOutline.noSummary')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,9 +134,11 @@ export default function BookOutlineGenerator({ book, onComplete }: BookOutlineGe
|
||||
|
||||
<div className="mt-6 flex items-center justify-end gap-3">
|
||||
<p className="text-sm text-gray-500">
|
||||
Ready to start writing! Navigate to the Write tab to begin.
|
||||
{t('bookOutline.readyToWrite')}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -124,10 +146,9 @@ export default function BookOutlineGenerator({ book, onComplete }: BookOutlineGe
|
||||
{!outline && !isGenerating && (
|
||||
<div className="card text-center py-12">
|
||||
<Wand2 className="w-12 h-12 text-gray-300 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900">Ready to Generate</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900">{t('bookOutline.readyToGenerate')}</h3>
|
||||
<p className="text-gray-500 mt-2 max-w-md mx-auto">
|
||||
Click "Generate Outline" to create a detailed chapter outline based on your genre and book idea.
|
||||
The AI will create a structure following genre-specific patterns.
|
||||
{t('bookOutline.readyToGenerateDesc')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { BookOpen, Sparkles } from 'lucide-react';
|
||||
|
||||
interface Genre {
|
||||
@@ -14,6 +15,7 @@ interface GenreSelectorProps {
|
||||
}
|
||||
|
||||
export default function GenreSelector({ genres, onSelect }: GenreSelectorProps) {
|
||||
const { t } = useTranslation();
|
||||
const [selectedGenre, setSelectedGenre] = useState<string>('');
|
||||
const [title, setTitle] = useState('');
|
||||
const [idea, setIdea] = useState('');
|
||||
@@ -32,7 +34,7 @@ export default function GenreSelector({ genres, onSelect }: GenreSelectorProps)
|
||||
<div className="card">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
|
||||
<BookOpen className="w-5 h-5" />
|
||||
Select Your Genre
|
||||
{t('genreSelector.selectGenre')}
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
@@ -47,9 +49,11 @@ export default function GenreSelector({ genres, onSelect }: GenreSelectorProps)
|
||||
}`}
|
||||
>
|
||||
<span className="text-2xl mb-2 block">{genre.icon}</span>
|
||||
<h4 className="font-medium text-gray-900">{genre.name}</h4>
|
||||
<h4 className="font-medium text-gray-900">
|
||||
{t(`genres.${genre.id}.name`, genre.name)}
|
||||
</h4>
|
||||
<p className="text-xs text-gray-500 mt-1 line-clamp-2">
|
||||
{genre.description}
|
||||
{t(`genres.${genre.id}.description`, genre.description)}
|
||||
</p>
|
||||
</button>
|
||||
))}
|
||||
@@ -62,29 +66,29 @@ export default function GenreSelector({ genres, onSelect }: GenreSelectorProps)
|
||||
<div className="card sticky top-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
|
||||
<Sparkles className="w-5 h-5" />
|
||||
Book Details
|
||||
{t('genreSelector.bookDetails')}
|
||||
</h3>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label className="label">Book Title (optional)</label>
|
||||
<label className="label">{t('genreSelector.bookTitle')}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
placeholder="My Amazing Book"
|
||||
placeholder={t('genreSelector.bookTitlePlaceholder')}
|
||||
className="input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="label">
|
||||
Core Idea <span className="text-red-500">*</span>
|
||||
{t('genreSelector.coreIdea')} <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<textarea
|
||||
value={idea}
|
||||
onChange={(e) => setIdea(e.target.value)}
|
||||
placeholder="Describe your book idea... What's the story about? Who are the main characters? What conflict drives the narrative?"
|
||||
placeholder={t('genreSelector.coreIdeaPlaceholder')}
|
||||
className="input min-h-[150px] resize-y"
|
||||
required
|
||||
/>
|
||||
@@ -92,7 +96,7 @@ export default function GenreSelector({ genres, onSelect }: GenreSelectorProps)
|
||||
|
||||
<div className="bg-amber-50 border border-amber-200 rounded-lg p-3">
|
||||
<p className="text-xs text-amber-700">
|
||||
<strong>Tip:</strong> The more details you provide, the better the AI can generate your outline and content.
|
||||
<strong>Tip:</strong> {t('genreSelector.tip')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -101,7 +105,7 @@ export default function GenreSelector({ genres, onSelect }: GenreSelectorProps)
|
||||
disabled={!selectedGenre || !idea.trim()}
|
||||
className="btn-primary w-full py-3"
|
||||
>
|
||||
Generate Outline
|
||||
{t('genreSelector.generateOutline')}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { Image, Wand2, Type } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import CoverGallery from './CoverGallery';
|
||||
import CoverEditor from './CoverEditor';
|
||||
import { useCoverStore, Cover } from '../../stores/coverStore';
|
||||
@@ -10,6 +11,8 @@ export default function CoverDesigner() {
|
||||
const [activeTab, setActiveTab] = useState<'gallery' | 'editor' | 'ai'>('gallery');
|
||||
const { activeCover, setActiveCover } = useCoverStore();
|
||||
const queryClient = useQueryClient();
|
||||
const { t: tMain } = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { data: coversData } = useQuery({
|
||||
queryKey: ['covers'],
|
||||
@@ -37,8 +40,8 @@ export default function CoverDesigner() {
|
||||
const handleSelectCover = (coverUrl: string) => {
|
||||
const newCover: Cover = {
|
||||
id: `cover-${Date.now()}`,
|
||||
title: 'New Book',
|
||||
author: 'Author Name',
|
||||
title: tMain('coverDesigner.newBookTitle'),
|
||||
author: tMain('coverDesigner.authorName'),
|
||||
backgroundImage: coverUrl,
|
||||
backgroundColor: '#ffffff',
|
||||
width: 1600,
|
||||
@@ -55,9 +58,9 @@ export default function CoverDesigner() {
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Cover Designer</h2>
|
||||
<h2 className="text-2xl font-bold text-gray-900">{tMain('coverDesigner.title')}</h2>
|
||||
<p className="text-gray-500 mt-1">
|
||||
Design stunning book covers with AI or manual editing
|
||||
{tMain('coverDesigner.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -72,7 +75,7 @@ export default function CoverDesigner() {
|
||||
}`}
|
||||
>
|
||||
<Image className="w-4 h-4" />
|
||||
Gallery
|
||||
{tMain('coverDesigner.tabGallery')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('editor')}
|
||||
@@ -83,7 +86,7 @@ export default function CoverDesigner() {
|
||||
}`}
|
||||
>
|
||||
<Type className="w-4 h-4" />
|
||||
Editor
|
||||
{tMain('coverDesigner.tabEditor')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('ai')}
|
||||
@@ -94,7 +97,7 @@ export default function CoverDesigner() {
|
||||
}`}
|
||||
>
|
||||
<Wand2 className="w-4 h-4" />
|
||||
AI Generate
|
||||
{tMain('coverDesigner.tabAIGenerate')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -131,36 +134,37 @@ function AIGenerator({
|
||||
}) {
|
||||
const [prompt, setPrompt] = useState('');
|
||||
const [selectedStyle, setSelectedStyle] = useState('modern');
|
||||
const { t } = useTranslation();
|
||||
|
||||
const styles = [
|
||||
{ id: 'modern', name: 'Modern', icon: '✨' },
|
||||
{ id: 'minimal', name: 'Minimalist', icon: '🎯' },
|
||||
{ id: 'vintage', name: 'Vintage', icon: '📜' },
|
||||
{ id: 'bold', name: 'Bold', icon: '🔥' },
|
||||
{ id: 'elegant', name: 'Elegant', icon: '💎' },
|
||||
{ id: 'playful', name: 'Playful', icon: '🎨' },
|
||||
{ id: 'modern', name: t('coverDesigner.aiGenerator.modern'), icon: '✨' },
|
||||
{ id: 'minimalist', name: t('coverDesigner.aiGenerator.minimalist'), icon: '🎯' },
|
||||
{ id: 'vintage', name: t('coverDesigner.aiGenerator.vintage'), icon: '📜' },
|
||||
{ id: 'bold', name: t('coverDesigner.aiGenerator.bold'), icon: '🔥' },
|
||||
{ id: 'elegant', name: t('coverDesigner.aiGenerator.elegant'), icon: '💎' },
|
||||
{ id: 'playful', name: t('coverDesigner.aiGenerator.playful'), icon: '🎨' },
|
||||
];
|
||||
|
||||
const genrePresets = [
|
||||
{
|
||||
genre: 'Mystery',
|
||||
prompt: 'A mysterious book cover with dark shadows, silhouette of a detective, foggy street scene, noir atmosphere',
|
||||
genre: t('coverDesigner.aiGenerator.mystery'),
|
||||
prompt: t('coverDesigner.aiGenerator.mysteryPrompt'),
|
||||
},
|
||||
{
|
||||
genre: 'Romance',
|
||||
prompt: 'A romantic book cover with soft pastel colors, couple silhouette at sunset, dreamy and emotional',
|
||||
genre: t('coverDesigner.aiGenerator.romance'),
|
||||
prompt: t('coverDesigner.aiGenerator.romancePrompt'),
|
||||
},
|
||||
{
|
||||
genre: 'Fantasy',
|
||||
prompt: 'An epic fantasy book cover with magical elements, dragon, castle in the background, mystical glowing effects',
|
||||
genre: t('coverDesigner.aiGenerator.fantasy'),
|
||||
prompt: t('coverDesigner.aiGenerator.fantasyPrompt'),
|
||||
},
|
||||
{
|
||||
genre: 'Sci-Fi',
|
||||
prompt: 'A futuristic sci-fi book cover with spaceships, neon lights, cyberpunk city, high-tech atmosphere',
|
||||
genre: t('coverDesigner.aiGenerator.scifi'),
|
||||
prompt: t('coverDesigner.aiGenerator.scifiPrompt'),
|
||||
},
|
||||
{
|
||||
genre: 'Self-Help',
|
||||
prompt: 'A clean self-help book cover with inspiring imagery, mountain peak or sunrise, professional and motivating',
|
||||
genre: t('coverDesigner.aiGenerator.selfhelp'),
|
||||
prompt: t('coverDesigner.aiGenerator.selfhelpPrompt'),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -179,12 +183,12 @@ function AIGenerator({
|
||||
<div className="card space-y-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
AI Cover Generator
|
||||
{t('coverDesigner.aiGenerator.title')}
|
||||
</h3>
|
||||
|
||||
{/* Style Selection */}
|
||||
<div className="mb-6">
|
||||
<label className="label">Select Style</label>
|
||||
<label className="label">{t('coverDesigner.aiGenerator.selectStyle')}</label>
|
||||
<div className="grid grid-cols-3 sm:grid-cols-6 gap-2 mt-2">
|
||||
{styles.map((style) => (
|
||||
<button
|
||||
@@ -205,18 +209,18 @@ function AIGenerator({
|
||||
|
||||
{/* Prompt Input */}
|
||||
<div className="mb-6">
|
||||
<label className="label">Describe Your Cover</label>
|
||||
<label className="label">{t('coverDesigner.aiGenerator.describeCover')}</label>
|
||||
<textarea
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
placeholder="Describe the book cover you want to generate... e.g., 'A mysterious forest with glowing eyes in the darkness'"
|
||||
placeholder={t('coverDesigner.aiGenerator.coverPlaceholder')}
|
||||
className="input min-h-[120px] resize-y"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Genre Presets */}
|
||||
<div className="mb-6">
|
||||
<label className="label">Quick Presets by Genre</label>
|
||||
<label className="label">{t('coverDesigner.aiGenerator.quickPresets')}</label>
|
||||
<div className="grid gap-2 mt-2">
|
||||
{genrePresets.map((preset) => (
|
||||
<button
|
||||
@@ -238,7 +242,7 @@ function AIGenerator({
|
||||
className="btn-primary w-full py-3 flex items-center justify-center gap-2"
|
||||
>
|
||||
<Wand2 className="w-5 h-5" />
|
||||
{isGenerating ? 'Generating...' : 'Generate Cover'}
|
||||
{isGenerating ? t('coverDesigner.aiGenerator.generating') : t('coverDesigner.aiGenerator.generateCover')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Stage, Layer, Rect, Text, Transformer, Image as KonvaImage } from 'reac
|
||||
import type Konva from 'konva';
|
||||
import { useCoverStore } from '../../stores/coverStore';
|
||||
import { Type, Download } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const BackgroundImage = ({ url, width, height }: { url: string; width: number; height: number }) => {
|
||||
const [image, setImage] = useState<HTMLImageElement | null>(null);
|
||||
@@ -21,13 +22,13 @@ const BackgroundImage = ({ url, width, height }: { url: string; width: number; h
|
||||
};
|
||||
|
||||
const FONTS = [
|
||||
{ name: 'Roboto', label: 'Modern (Roboto)' },
|
||||
{ name: 'Montserrat', label: 'Minimalist (Montserrat)' },
|
||||
{ name: 'Playfair Display', label: 'Elegant (Playfair Display)' },
|
||||
{ name: 'Bebas Neue', label: 'Bold (Bebas Neue)' },
|
||||
{ name: 'Pacifico', label: 'Playful (Pacifico)' },
|
||||
{ name: 'Georgia', label: 'Classic (Georgia)' },
|
||||
{ name: 'Arial', label: 'Standard (Arial)' },
|
||||
{ name: 'Roboto', labelKey: 'coverDesigner.modernFont' },
|
||||
{ name: 'Montserrat', labelKey: 'coverDesigner.minimalistFont' },
|
||||
{ name: 'Playfair Display', labelKey: 'coverDesigner.elegantFont' },
|
||||
{ name: 'Bebas Neue', labelKey: 'coverDesigner.boldFont' },
|
||||
{ name: 'Pacifico', labelKey: 'coverDesigner.playfulFont' },
|
||||
{ name: 'Georgia', labelKey: 'coverDesigner.classicFont' },
|
||||
{ name: 'Arial', labelKey: 'coverDesigner.standardFont' },
|
||||
];
|
||||
|
||||
export default function CoverEditor() {
|
||||
@@ -37,11 +38,12 @@ export default function CoverEditor() {
|
||||
const [textTool, setTextTool] = useState<'title' | 'author'>('title');
|
||||
const [newText, setNewText] = useState('');
|
||||
const [selectedFont, setSelectedFont] = useState('Roboto');
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!activeCover) {
|
||||
return (
|
||||
<div className="card text-center py-12">
|
||||
<p className="text-gray-500">Select a cover from the gallery to edit</p>
|
||||
<p className="text-gray-500">{t('coverDesigner.selectCover')}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -86,7 +88,7 @@ export default function CoverEditor() {
|
||||
<div className="lg:col-span-3 card p-4 bg-gray-100">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="font-semibold text-gray-900">
|
||||
Editing: {activeCover.title}
|
||||
{t('coverDesigner.editing')}: {activeCover.title}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
@@ -94,7 +96,7 @@ export default function CoverEditor() {
|
||||
className="btn-primary flex items-center gap-2"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
Export
|
||||
{t('coverDesigner.export')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -185,12 +187,12 @@ export default function CoverEditor() {
|
||||
<div className="card">
|
||||
<h4 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
||||
<Type className="w-4 h-4" />
|
||||
Add Text
|
||||
{t('coverDesigner.addText')}
|
||||
</h4>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="label">Text Type</label>
|
||||
<label className="label">{t('coverDesigner.textType')}</label>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setTextTool('title')}
|
||||
@@ -200,7 +202,7 @@ export default function CoverEditor() {
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
Title
|
||||
{t('coverDesigner.titleText')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setTextTool('author')}
|
||||
@@ -210,13 +212,13 @@ export default function CoverEditor() {
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
Author
|
||||
{t('coverDesigner.authorText')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="label">Font</label>
|
||||
<label className="label">{t('coverDesigner.font')}</label>
|
||||
<select
|
||||
value={selectedFont}
|
||||
onChange={(e) => setSelectedFont(e.target.value)}
|
||||
@@ -224,19 +226,19 @@ export default function CoverEditor() {
|
||||
>
|
||||
{FONTS.map((font) => (
|
||||
<option key={font.name} value={font.name}>
|
||||
{font.label}
|
||||
{t(font.labelKey)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="label">Text Content</label>
|
||||
<label className="label">{t('coverDesigner.textContent')}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={newText}
|
||||
onChange={(e) => setNewText(e.target.value)}
|
||||
placeholder={textTool === 'title' ? 'Book Title' : 'Author Name'}
|
||||
placeholder={textTool === 'title' ? t('coverDesigner.bookTitlePlaceholder') : t('coverDesigner.authorNamePlaceholder')}
|
||||
className="input"
|
||||
onKeyPress={(e) => e.key === 'Enter' && handleAddText()}
|
||||
/>
|
||||
@@ -247,7 +249,7 @@ export default function CoverEditor() {
|
||||
disabled={!newText.trim()}
|
||||
className="btn-primary w-full"
|
||||
>
|
||||
Add Text Layer
|
||||
{t('coverDesigner.addTextLayer')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -269,7 +271,7 @@ export default function CoverEditor() {
|
||||
>
|
||||
{FONTS.map((font) => (
|
||||
<option key={font.name} value={font.name}>
|
||||
{font.label}
|
||||
{t(font.labelKey)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Image, Eye, Trash2, Download } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface CoverData {
|
||||
id: string;
|
||||
@@ -14,13 +15,15 @@ interface CoverGalleryProps {
|
||||
}
|
||||
|
||||
export default function CoverGallery({ covers, onSelect, onDelete }: CoverGalleryProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (covers.length === 0) {
|
||||
return (
|
||||
<div className="card text-center py-12">
|
||||
<Image className="w-16 h-16 text-gray-300 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900">No covers yet</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900">{t('coverDesigner.noCovers')}</h3>
|
||||
<p className="text-gray-500 mt-2">
|
||||
Upload a cover image or generate one with AI to get started
|
||||
{t('coverDesigner.noCoversDesc')}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@@ -43,7 +46,7 @@ export default function CoverGallery({ covers, onSelect, onDelete }: CoverGaller
|
||||
<button
|
||||
onClick={() => onSelect(cover.url)}
|
||||
className="p-2 bg-white rounded-lg hover:bg-gray-100 transition-colors"
|
||||
title="Edit"
|
||||
title={t('coverDesigner.edit')}
|
||||
>
|
||||
<Eye className="w-5 h-5 text-gray-700" />
|
||||
</button>
|
||||
@@ -55,14 +58,14 @@ export default function CoverGallery({ covers, onSelect, onDelete }: CoverGaller
|
||||
link.click();
|
||||
}}
|
||||
className="p-2 bg-white rounded-lg hover:bg-gray-100 transition-colors"
|
||||
title="Download"
|
||||
title={t('coverDesigner.download')}
|
||||
>
|
||||
<Download className="w-5 h-5 text-gray-700" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onDelete(cover.id)}
|
||||
className="p-2 bg-red-500 rounded-lg hover:bg-red-600 transition-colors"
|
||||
title="Delete"
|
||||
title={t('coverDesigner.delete')}
|
||||
>
|
||||
<Trash2 className="w-5 h-5 text-white" />
|
||||
</button>
|
||||
|
||||
@@ -35,3 +35,71 @@ body {
|
||||
@apply block text-sm font-medium text-gray-700 mb-1;
|
||||
}
|
||||
}
|
||||
|
||||
/* TipTap WYSIWYG Editor Styles */
|
||||
.tiptap {
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.tiptap p.is-editor-empty:first-child::before {
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
color: #adb5bd;
|
||||
pointer-events: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* Book chapter formatting */
|
||||
.tiptap p {
|
||||
text-indent: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.tiptap p:first-child {
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
.tiptap h1, .tiptap h2, .tiptap h3 {
|
||||
text-align: center;
|
||||
text-indent: 0;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.tiptap blockquote {
|
||||
text-align: left;
|
||||
text-indent: 0;
|
||||
margin-left: 2rem;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
.tiptap ul, .tiptap ol {
|
||||
text-align: left;
|
||||
text-indent: 0;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.tiptap h1 { font-size: 2em; font-weight: 700; margin: 0.67em 0; }
|
||||
.tiptap h2 { font-size: 1.5em; font-weight: 600; margin: 0.75em 0; }
|
||||
.tiptap h3 { font-size: 1.25em; font-weight: 600; margin: 0.83em 0; }
|
||||
|
||||
.tiptap ul { list-style: disc; padding-left: 1.5em; margin: 0.5em 0; }
|
||||
.tiptap ol { list-style: decimal; padding-left: 1.5em; margin: 0.5em 0; }
|
||||
.tiptap li { margin: 0.25em 0; }
|
||||
|
||||
.tiptap blockquote {
|
||||
border-left: 3px solid #e5e7eb;
|
||||
padding-left: 1em;
|
||||
margin: 1em 0;
|
||||
color: #6b7280;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.tiptap hr {
|
||||
border: none;
|
||||
border-top: 2px solid #e5e7eb;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
|
||||
.tiptap p { margin: 0.5em 0; }
|
||||
|
||||
@@ -24,5 +24,148 @@
|
||||
"languageSelect": "Select your preferred language",
|
||||
"english": "English",
|
||||
"spanish": "Español"
|
||||
},
|
||||
"onboarding": {
|
||||
"title": "Welcome to CreaBook",
|
||||
"subtitle": "Let's personalize your creative journey.",
|
||||
"step1": "1. Select your language",
|
||||
"step2": "2. What kind of book do you want to create?",
|
||||
"ideaPlaceholder": "E.g., A sci-fi thriller about a detective on Mars...",
|
||||
"genreLabel": "Genre (optional)",
|
||||
"step3": "3. How do you want to start?",
|
||||
"btnFree": "Move Freely (Blank Canvas)",
|
||||
"btnAI": "Use AI to Generate Outline"
|
||||
},
|
||||
"genres": {
|
||||
"fiction": { "name": "Fiction", "description": "General literary fiction with focus on character development and narrative" },
|
||||
"mystery": { "name": "Mystery", "description": "Puzzle-driven narratives with clues, suspects, and a satisfying reveal" },
|
||||
"romance": { "name": "Romance", "description": "Love-centered stories with emotional intimacy and satisfying relationship resolution" },
|
||||
"scifi": { "name": "Science Fiction", "description": "Speculative fiction exploring technology, space, time, and their impact on humanity" },
|
||||
"fantasy": { "name": "Fantasy", "description": "Magical worlds with supernatural elements, quests, and epic stakes" },
|
||||
"horror": { "name": "Horror", "description": "Fear-driven narratives designed to unsettle, frighten, and provoke dread" },
|
||||
"thriller": { "name": "Thriller", "description": "High-stakes, fast-paced narratives with constant tension and danger" },
|
||||
"children": { "name": "Children's Book", "description": "Age-appropriate stories with clear morals, simple language, and engaging characters" },
|
||||
"nonfiction": { "name": "Non-Fiction", "description": "Factual, informative content organized around a central topic or argument" },
|
||||
"selfhelp": { "name": "Self-Help", "description": "Practical guidance for personal improvement and growth" },
|
||||
"business": { "name": "Business", "description": "Professional insights, strategies, and case studies for business success" },
|
||||
"memoir": { "name": "Memoir", "description": "Personal life stories focused on transformation and universal themes" }
|
||||
},
|
||||
"genreSelector": {
|
||||
"selectGenre": "Select Your Genre",
|
||||
"bookDetails": "Book Details",
|
||||
"bookTitle": "Book Title (optional)",
|
||||
"bookTitlePlaceholder": "My Amazing Book",
|
||||
"coreIdea": "Core Idea",
|
||||
"coreIdeaPlaceholder": "Describe your book idea... What's the story about? Who are the main characters? What conflict drives the narrative?",
|
||||
"tip": "The more details you provide, the better the AI can generate your outline and content.",
|
||||
"generateOutline": "Generate Outline"
|
||||
},
|
||||
"bookGenerator": {
|
||||
"title": "Book Generator",
|
||||
"subtitle": "Generate book ideas and write with AI assistance",
|
||||
"tabGenre": "Genre",
|
||||
"tabOutline": "Outline",
|
||||
"tabWrite": "Write",
|
||||
"tabCharacters": "Characters"
|
||||
},
|
||||
"bookEditor": {
|
||||
"chapters": "Chapters",
|
||||
"written": "Written",
|
||||
"chapterSummary": "Chapter Summary",
|
||||
"chapterOf": "Chapter {{current}} of {{total}}",
|
||||
"aiAssist": "AI Assist",
|
||||
"generating": "Generating...",
|
||||
"generateChapter": "Generate Chapter",
|
||||
"aiWritingAssistant": "AI Writing Assistant",
|
||||
"expandSection": "Expand this section",
|
||||
"improveProse": "Improve prose",
|
||||
"addDescription": "Add description",
|
||||
"rewriteParagraph": "Rewrite paragraph",
|
||||
"words": "words",
|
||||
"lastUpdated": "Last updated",
|
||||
"noOutline": "Generate an outline first to start writing your book.",
|
||||
"editorPlaceholder": "Start writing or use AI to generate this chapter...",
|
||||
"written": "Written"
|
||||
},
|
||||
"bookOutline": {
|
||||
"untitledBook": "Untitled Book",
|
||||
"genre": "Genre",
|
||||
"generatingOutline": "Generating...",
|
||||
"generateOutline": "Generate Outline",
|
||||
"generatedOutline": "Generated Outline",
|
||||
"logline": "Logline",
|
||||
"chapterOutline": "Chapter Outline",
|
||||
"chapter": "Chapter",
|
||||
"noSummary": "No summary available",
|
||||
"readyToWrite": "Ready to start writing! Navigate to the Write tab to begin.",
|
||||
"readyToGenerate": "Ready to Generate",
|
||||
"readyToGenerateDesc": "Click \"Generate Outline\" to create a detailed chapter outline based on your genre and book idea. The AI will create a structure following genre-specific patterns.",
|
||||
"failedToGenerate": "Failed to generate outline"
|
||||
},
|
||||
"characters": {
|
||||
"title": "Character Development",
|
||||
"generating": "Generating...",
|
||||
"generate": "Generate Characters",
|
||||
"empty": "No characters yet. Click \"Generate Characters\" to create AI-suggested characters.",
|
||||
"traits": "Traits",
|
||||
"motivation": "Motivation"
|
||||
},
|
||||
"coverDesigner": {
|
||||
"title": "Cover Designer",
|
||||
"subtitle": "Design stunning book covers with AI or manual editing",
|
||||
"tabGallery": "Gallery",
|
||||
"tabEditor": "Editor",
|
||||
"tabAIGenerate": "AI Generate",
|
||||
"newBookTitle": "New Book",
|
||||
"authorName": "Author Name",
|
||||
"noCovers": "No covers yet",
|
||||
"noCoversDesc": "Upload a cover image or generate one with AI to get started",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"download": "Download",
|
||||
"selectCover": "Select a cover from the gallery to edit",
|
||||
"editing": "Editing",
|
||||
"export": "Export",
|
||||
"addText": "Add Text",
|
||||
"textType": "Text Type",
|
||||
"titleText": "Title",
|
||||
"authorText": "Author",
|
||||
"font": "Font",
|
||||
"modernFont": "Modern (Roboto)",
|
||||
"minimalistFont": "Minimalist (Montserrat)",
|
||||
"elegantFont": "Elegant (Playfair Display)",
|
||||
"boldFont": "Bold (Bebas Neue)",
|
||||
"playfulFont": "Playful (Pacifico)",
|
||||
"classicFont": "Classic (Georgia)",
|
||||
"standardFont": "Standard (Arial)",
|
||||
"textContent": "Text Content",
|
||||
"bookTitlePlaceholder": "Book Title",
|
||||
"authorNamePlaceholder": "Author Name",
|
||||
"addTextLayer": "Add Text Layer",
|
||||
"aiGenerator": {
|
||||
"title": "AI Cover Generator",
|
||||
"selectStyle": "Select Style",
|
||||
"modern": "Modern",
|
||||
"minimalist": "Minimalist",
|
||||
"vintage": "Vintage",
|
||||
"bold": "Bold",
|
||||
"elegant": "Elegant",
|
||||
"playful": "Playful",
|
||||
"describeCover": "Describe Your Cover",
|
||||
"coverPlaceholder": "Describe the book cover you want to generate... e.g., 'A mysterious forest with glowing eyes in the darkness'",
|
||||
"quickPresets": "Quick Presets by Genre",
|
||||
"mystery": "Mystery",
|
||||
"mysteryPrompt": "A mysterious book cover with dark shadows, silhouette of a detective, foggy street scene, noir atmosphere",
|
||||
"romance": "Romance",
|
||||
"romancePrompt": "A romantic book cover with soft pastel colors, couple silhouette at sunset, dreamy and emotional",
|
||||
"fantasy": "Fantasy",
|
||||
"fantasyPrompt": "An epic fantasy book cover with magical elements, dragon, castle in the background, mystical glowing effects",
|
||||
"scifi": "Sci-Fi",
|
||||
"scifiPrompt": "A futuristic sci-fi book cover with spaceships, neon lights, cyberpunk city, high-tech atmosphere",
|
||||
"selfhelp": "Self-Help",
|
||||
"selfhelpPrompt": "A clean self-help book cover with inspiring imagery, mountain peak or sunrise, professional and motivating",
|
||||
"generating": "Generating...",
|
||||
"generateCover": "Generate Cover"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,5 +24,148 @@
|
||||
"languageSelect": "Selecciona tu idioma preferido",
|
||||
"english": "Inglés",
|
||||
"spanish": "Español"
|
||||
},
|
||||
"onboarding": {
|
||||
"title": "Bienvenido a CreaBook",
|
||||
"subtitle": "Vamos a personalizar tu viaje creativo.",
|
||||
"step1": "1. Selecciona tu idioma",
|
||||
"step2": "2. ¿Qué tipo de libro quieres crear?",
|
||||
"ideaPlaceholder": "Ej. Un thriller de ciencia ficción sobre un detective en Marte...",
|
||||
"genreLabel": "Género (opcional)",
|
||||
"step3": "3. ¿Cómo quieres empezar?",
|
||||
"btnFree": "Moverse libremente (Lienzo en blanco)",
|
||||
"btnAI": "Usar IA para generar tu esquema"
|
||||
},
|
||||
"genres": {
|
||||
"fiction": { "name": "Ficción", "description": "Ficción literaria general centrada en el desarrollo de personajes y la narrativa" },
|
||||
"mystery": { "name": "Misterio", "description": "Narrativas basadas en enigmas con pistas, sospechosos y una revelación satisfactoria" },
|
||||
"romance": { "name": "Romance", "description": "Historias centradas en el amor con intimidad emocional y una resolución satisfactoria" },
|
||||
"scifi": { "name": "Ciencia Ficción", "description": "Ficción especulativa que explora la tecnología, el espacio, el tiempo y su impacto en la humanidad" },
|
||||
"fantasy": { "name": "Fantasía", "description": "Mundos mágicos con elementos sobrenaturales, misiones y grandes desafíos" },
|
||||
"horror": { "name": "Terror", "description": "Narrativas de miedo diseñadas para inquietar, asustar y provocar pavor" },
|
||||
"thriller": { "name": "Thriller", "description": "Narrativas de alto riesgo y ritmo rápido con tensión y peligro constantes" },
|
||||
"children": { "name": "Libro Infantil", "description": "Historias apropiadas para niños con moralejas claras, lenguaje sencillo y personajes atractivos" },
|
||||
"nonfiction": { "name": "No Ficción", "description": "Contenido informativo y factual organizado en torno a un tema o argumento central" },
|
||||
"selfhelp": { "name": "Autoayuda", "description": "Guía práctica para la mejora y el crecimiento personal" },
|
||||
"business": { "name": "Negocios", "description": "Perspectivas profesionales, estrategias y casos de estudio para el éxito empresarial" },
|
||||
"memoir": { "name": "Memorias", "description": "Historias de vida personales centradas en la transformación y temas universales" }
|
||||
},
|
||||
"genreSelector": {
|
||||
"selectGenre": "Selecciona tu Género",
|
||||
"bookDetails": "Detalles del Libro",
|
||||
"bookTitle": "Título del Libro (opcional)",
|
||||
"bookTitlePlaceholder": "Mi Libro Increíble",
|
||||
"coreIdea": "Idea Principal",
|
||||
"coreIdeaPlaceholder": "Describe tu idea para el libro... ¿De qué trata la historia? ¿Quiénes son los personajes principales? ¿Qué conflicto impulsa la narrativa?",
|
||||
"tip": "Cuantos más detalles proporciones, mejor podrá la IA generar tu esquema y contenido.",
|
||||
"generateOutline": "Generar Esquema"
|
||||
},
|
||||
"bookGenerator": {
|
||||
"title": "Generador de Libros",
|
||||
"subtitle": "Genera ideas de libros y escribe con asistencia de IA",
|
||||
"tabGenre": "Género",
|
||||
"tabOutline": "Esquema",
|
||||
"tabWrite": "Escribir",
|
||||
"tabCharacters": "Personajes"
|
||||
},
|
||||
"bookEditor": {
|
||||
"chapters": "Capítulos",
|
||||
"written": "Escrito",
|
||||
"chapterSummary": "Resumen del Capítulo",
|
||||
"chapterOf": "Capítulo {{current}} de {{total}}",
|
||||
"aiAssist": "Asistente IA",
|
||||
"generating": "Generando...",
|
||||
"generateChapter": "Generar Capítulo",
|
||||
"aiWritingAssistant": "Asistente de Escritura IA",
|
||||
"expandSection": "Expandir esta sección",
|
||||
"improveProse": "Mejorar prosa",
|
||||
"addDescription": "Añadir descripción",
|
||||
"rewriteParagraph": "Reescribir párrafo",
|
||||
"words": "palabras",
|
||||
"lastUpdated": "Última actualización",
|
||||
"noOutline": "Genera un esquema primero para empezar a escribir tu libro.",
|
||||
"editorPlaceholder": "Empieza a escribir o usa la IA para generar este capítulo...",
|
||||
"written": "Escrito"
|
||||
},
|
||||
"bookOutline": {
|
||||
"untitledBook": "Libro sin título",
|
||||
"genre": "Género",
|
||||
"generatingOutline": "Generando...",
|
||||
"generateOutline": "Generar Esquema",
|
||||
"generatedOutline": "Esquema Generado",
|
||||
"logline": "Sinopsis",
|
||||
"chapterOutline": "Esquema de Capítulos",
|
||||
"chapter": "Capítulo",
|
||||
"noSummary": "Sin resumen disponible",
|
||||
"readyToWrite": "¡Listo para escribir! Navega a la pestaña Escribir para comenzar.",
|
||||
"readyToGenerate": "Listo para Generar",
|
||||
"readyToGenerateDesc": "Haz clic en \"Generar Esquema\" para crear un esquema detallado de capítulos basado en tu género e idea. La IA creará una estructura siguiendo patrones específicos del género.",
|
||||
"failedToGenerate": "Error al generar el esquema"
|
||||
},
|
||||
"characters": {
|
||||
"title": "Desarrollo de Personajes",
|
||||
"generating": "Generando...",
|
||||
"generate": "Generar Personajes",
|
||||
"empty": "Aún no hay personajes. Haz clic en \"Generar Personajes\" para crear personajes sugeridos por la IA.",
|
||||
"traits": "Rasgos",
|
||||
"motivation": "Motivación"
|
||||
},
|
||||
"coverDesigner": {
|
||||
"title": "Diseñador de Portadas",
|
||||
"subtitle": "Diseña portadas de libros impresionantes con IA o edición manual",
|
||||
"tabGallery": "Galería",
|
||||
"tabEditor": "Editor",
|
||||
"tabAIGenerate": "Generar con IA",
|
||||
"newBookTitle": "Nuevo Libro",
|
||||
"authorName": "Nombre del Autor",
|
||||
"noCovers": "Aún no hay portadas",
|
||||
"noCoversDesc": "Sube una imagen de portada o genera una con IA para comenzar",
|
||||
"edit": "Editar",
|
||||
"delete": "Eliminar",
|
||||
"download": "Descargar",
|
||||
"selectCover": "Selecciona una portada de la galería para editar",
|
||||
"editing": "Editando",
|
||||
"export": "Exportar",
|
||||
"addText": "Añadir Texto",
|
||||
"textType": "Tipo de Texto",
|
||||
"titleText": "Título",
|
||||
"authorText": "Autor",
|
||||
"font": "Fuente",
|
||||
"modernFont": "Moderno (Roboto)",
|
||||
"minimalistFont": "Minimalista (Montserrat)",
|
||||
"elegantFont": "Elegante (Playfair Display)",
|
||||
"boldFont": "Audaz (Bebas Neue)",
|
||||
"playfulFont": "Juguetón (Pacifico)",
|
||||
"classicFont": "Clásico (Georgia)",
|
||||
"standardFont": "Estándar (Arial)",
|
||||
"textContent": "Contenido del Texto",
|
||||
"bookTitlePlaceholder": "Título del Libro",
|
||||
"authorNamePlaceholder": "Nombre del Autor",
|
||||
"addTextLayer": "Añadir Capa de Texto",
|
||||
"aiGenerator": {
|
||||
"title": "Generador de Portadas con IA",
|
||||
"selectStyle": "Seleccionar Estilo",
|
||||
"modern": "Moderno",
|
||||
"minimalist": "Minimalista",
|
||||
"vintage": "Antiguo",
|
||||
"bold": "Audaz",
|
||||
"elegant": "Elegante",
|
||||
"playful": "Juguetón",
|
||||
"describeCover": "Describe tu Portada",
|
||||
"coverPlaceholder": "Describe la portada del libro que quieres generar... ej. 'Un bosque misterioso con ojos brillantes en la oscuridad'",
|
||||
"quickPresets": "Preajustes Rápidos por Género",
|
||||
"mystery": "Misterio",
|
||||
"mysteryPrompt": "Una portada de libro misteriosa con sombras oscuras, silueta de un detective, escena de calle brumosa, atmósfera noir",
|
||||
"romance": "Romance",
|
||||
"romancePrompt": "Una portada de libro romántica con colores pastel suaves, silueta de pareja al atardecer, soñadora y emocional",
|
||||
"fantasy": "Fantasía",
|
||||
"fantasyPrompt": "Una portada de libro de fantasía épica con elementos mágicos, dragón, castillo al fondo, efectos luminosos místicos",
|
||||
"scifi": "Ciencia Ficción",
|
||||
"scifiPrompt": "Una portada de libro de ciencia ficción futurista con naves espaciales, luces de neón, ciudad cyberpunk, atmósfera high-tech",
|
||||
"selfhelp": "Autoayuda",
|
||||
"selfhelpPrompt": "Una portada de libro de autoayuda limpia con imágenes inspiradoras, cima de montaña o amanecer, profesional y motivadora",
|
||||
"generating": "Generando...",
|
||||
"generateCover": "Generar Portada"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ export interface BookOutline {
|
||||
genre: string;
|
||||
logline: string;
|
||||
chapters: Chapter[];
|
||||
// Error handling properties
|
||||
error?: string;
|
||||
raw?: string;
|
||||
}
|
||||
|
||||
export interface Character {
|
||||
|
||||
812
node_modules/.package-lock.json
generated
vendored
812
node_modules/.package-lock.json
generated
vendored
@@ -13,6 +13,12 @@
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toast": "^1.1.5",
|
||||
"@tanstack/react-query": "^5.17.0",
|
||||
"@tiptap/extension-placeholder": "^3.22.2",
|
||||
"@tiptap/extension-text-align": "^3.22.2",
|
||||
"@tiptap/extension-underline": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2",
|
||||
"@tiptap/react": "^3.22.2",
|
||||
"@tiptap/starter-kit": "^3.22.2",
|
||||
"axios": "^1.6.5",
|
||||
"clsx": "^2.1.0",
|
||||
"i18next": "^26.0.3",
|
||||
@@ -1544,6 +1550,12 @@
|
||||
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@remirror/core-constants": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
|
||||
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
|
||||
@@ -1609,6 +1621,466 @@
|
||||
"react": "^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/core": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.22.2.tgz",
|
||||
"integrity": "sha512-atq35NkpeEphH6vNYJ0pTLLBA73FAbvTV9Ovd3AaTC5s99/KF5Q86zVJXvml8xPRcMGM6dLp+eSSd06oTscMSA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-blockquote": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.22.2.tgz",
|
||||
"integrity": "sha512-iTdlmGFcgxi4LKaOW2Rc9/yD83qTXgRm5BN3vCHWy5+TbEnReYxYqU5qKsbtTbKy30sO8TJTdAXTZ29uomShQQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bold": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.22.2.tgz",
|
||||
"integrity": "sha512-bqsPJyKcT/RWse4e16U2EKhraR8a2+98TUuk1amG3yCyFJZStoO/j+pN0IqZdZZjr3WtxFyvwWp7Kc59UN+jUA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bubble-menu": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.22.2.tgz",
|
||||
"integrity": "sha512-5hbyDOSkJwA2uh0v9Mm0Dd9bb9inx6tHBEDSH2tCB9Rm23poz3yOreB7SNX8xDMe5L0/PQesfWC14RitcmhKPg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bullet-list": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.22.2.tgz",
|
||||
"integrity": "sha512-llrTJnA72RGcWLLO+ro0QN4sjHynhaCerhpV+GZE/ATd8BqV/ekQFdBLJrvC/09My2XQfCwLsyCh92NPXUdELA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.22.2.tgz",
|
||||
"integrity": "sha512-iYFY+yzfYA9MKt7nupyW/PzqL9XC2D0mC8l1z2Y10i0/fGL8NbqIYjhNUAyXGqH3QWcI+DirI66842y2OadPOg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code-block": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.22.2.tgz",
|
||||
"integrity": "sha512-PEwFlDyvtKF19WCrOFg77qJV9WqhvjCY4ZoXlHP9Hx0KTcOA8W39mtw8d4NWU5pLRK94yHKF1DVVL8UUkEOnww==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-document": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.22.2.tgz",
|
||||
"integrity": "sha512-yPw9pQeVC4QDh86TuyKCZxxM4g0NAw7mEtGnAo6EpxaBQr1wyBr9yFpys+QTsQpRTmyTf1VHp4iTTLuWHMljIw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-dropcursor": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.22.2.tgz",
|
||||
"integrity": "sha512-sDv3fv4LtX0X4nqwh9Gn3C/aZXT+C2JlK7tJovPOpaYP/a6hr03Sn35X5moAfgMCSiWFygEvlTriqwmCsJuxog==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-floating-menu": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.22.2.tgz",
|
||||
"integrity": "sha512-r0ZTeh9rNtj9Api+G0YyaB+tAKPDn7aYWg+qSrmAC5EyUPee6Zjn3zlw0q4renCeQflvNRK20xHM8zokC41jOA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-gapcursor": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.22.2.tgz",
|
||||
"integrity": "sha512-rR2OLrl/k2kj7xehaZHq0Y7T+1wy2DOTabir9LsTrktTFEcklrh9qY1KC6rEBkwMKaWrmignR1l39kS6RlKFNw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-hard-break": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.22.2.tgz",
|
||||
"integrity": "sha512-ChsoqF4XRp6EWatTRlXL4LMFh/ggwRVCyt09brSfjJV5knFaXlECSa5/+rKLMLMULaj6dVlJqoAD15exgu2HHA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-heading": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.22.2.tgz",
|
||||
"integrity": "sha512-QPHLef+ikAyf7RVc4EdGeKxH4OEGb3ueCEwJ41RcYPtZ1BX9ueei7FC936guTdL1U7w3vQ65qfy86HznzkYgvw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-horizontal-rule": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.22.2.tgz",
|
||||
"integrity": "sha512-Oz8KN5KJAWV1mFNE9UIWXdMD6xa5zPf/0yLsT8V4sgaRm+VsdFKllN58BY9qCZf/kIZbaOez5KkaoeAcm0MAZg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-italic": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.22.2.tgz",
|
||||
"integrity": "sha512-fmtQu2HDnV3sOZPdz0+1lOLI7UtrIhusohJj2UwOLQxG8qqhLwbvWx2OQTlfblgY0z+CjLRr6ANbNDxOTIblfg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-link": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.22.2.tgz",
|
||||
"integrity": "sha512-TXfSoKmng5pecvQUZqdsx6ICeob5V5hhYOj2vCEtjfcjWsyCndqFIl1w+Nt/yI5ehrFNOVPyj3ZvcELuuAW6pw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"linkifyjs": "^4.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.22.2.tgz",
|
||||
"integrity": "sha512-Vq9xScgkA2A3Zj9dQ4WUBKK7u7UCzeSFRz9FcKTQVZHRPbZoqFGnlRUVngqsE7JXrCOthXQ1dXxgk40nAsBFRw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-item": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.22.2.tgz",
|
||||
"integrity": "sha512-Mk+iiLIFh8Pfuarr6mWfTO7QJbd2ZQd0nGNhNWXlGAO7DJCb4BP9nj4bEIJ17SbcykGRjsi4WMqY50z4MHXqKQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-keymap": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.22.2.tgz",
|
||||
"integrity": "sha512-TozU9V2vldMUPpTXnfLCO33EO06jLxn7uEJTMBnN4iX/dLV3cBVCbE4kHyDKS0sLd7joUeekS06vYP9uQb1hFw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-ordered-list": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.22.2.tgz",
|
||||
"integrity": "sha512-K7qxoBKmsVkAd3kW64ZRCUPFrDcNGpXRDUBx9YgAO/bTfsfxtH2oil+igsUWGXPczpP4yoHPKjTfhpBpLjGl6Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-paragraph": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.22.2.tgz",
|
||||
"integrity": "sha512-EHZZzxVhvzEPDPWtRBF1YKhB+WCUjd1C2NhjHfL3Dl71PBqM3ZWA6qN7NDGPyNyGGWauui/NR/4X+5AfPqlHyA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-placeholder": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-3.22.2.tgz",
|
||||
"integrity": "sha512-xYw733CmSeG7MyYBDdV5NFiwlBdXXzw4Mvjb2t4QRXagkDbHeNY/LtKTcrtcMNfO4Jx0mwivGQZUIEC8oAfvxg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-strike": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.22.2.tgz",
|
||||
"integrity": "sha512-YFC3elKU1L8PiGbcB6tqd/7vWPF5IbydJz0POJpHzSjstX+VfT8VsvS7ubxVuSIWQ11kGkH3mzX6LX8JHsHZxg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.22.2.tgz",
|
||||
"integrity": "sha512-J1w7JwijfSD7ah0WfiwZ/DVWCIGT9x369RM4RJc57i44mIBElj7tl1dh+N5KPGOXKUup4gr7sSJAE38lgeaDMg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text-align": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-3.22.2.tgz",
|
||||
"integrity": "sha512-pgqyXzVHo4WmDhK26rDwhK2lxQwnjl/9DP816C2k3To/fZRK1eW7q0pSAYteHWmKkaYAxwj/0UvCU0nXKlPujw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-underline": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.22.2.tgz",
|
||||
"integrity": "sha512-BaV6WOowxdkGTLWiU7DdZ3Twh633O4RGqwUM5dDas5LvaqL8AMWGTO8Wg9yAaaKXzd9MtKI1ZCqS/+MtzusgkQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extensions": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.22.2.tgz",
|
||||
"integrity": "sha512-s7MZmm2Xdq+8feIXgY3v7gVpQ5ClqBZi20KheouS7KSbBlrY4fu2irYR1EGc6r1UUVaHMxEa+cx5knhx+mIPUw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/pm": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.22.2.tgz",
|
||||
"integrity": "sha512-G2ENwIazoSKkAnN5MN5yN91TIZNFm6TxB74kPf3Empr2k9W51Hkcier70jHGpArhgcEaL4BVreuU1PRDRwCeGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-changeset": "^2.3.0",
|
||||
"prosemirror-collab": "^1.3.1",
|
||||
"prosemirror-commands": "^1.6.2",
|
||||
"prosemirror-dropcursor": "^1.8.1",
|
||||
"prosemirror-gapcursor": "^1.3.2",
|
||||
"prosemirror-history": "^1.4.1",
|
||||
"prosemirror-inputrules": "^1.4.0",
|
||||
"prosemirror-keymap": "^1.2.2",
|
||||
"prosemirror-markdown": "^1.13.1",
|
||||
"prosemirror-menu": "^1.2.4",
|
||||
"prosemirror-model": "^1.24.1",
|
||||
"prosemirror-schema-basic": "^1.2.3",
|
||||
"prosemirror-schema-list": "^1.5.0",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-tables": "^1.6.4",
|
||||
"prosemirror-trailing-node": "^3.0.0",
|
||||
"prosemirror-transform": "^1.10.2",
|
||||
"prosemirror-view": "^1.38.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/react": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.22.2.tgz",
|
||||
"integrity": "sha512-tyGKG69e/MkpoD/JTpVPz0XydEHxh1MSAYnLb3gRvyvBDv2r/veLea+cApkmjQaCfkKC/CWwTFXBYlOB0caSBA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/use-sync-external-store": "^0.0.6",
|
||||
"fast-equals": "^5.3.3",
|
||||
"use-sync-external-store": "^1.4.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tiptap/extension-bubble-menu": "^3.22.2",
|
||||
"@tiptap/extension-floating-menu": "^3.22.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/starter-kit": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.22.2.tgz",
|
||||
"integrity": "sha512-+CCKX8tOQ/ZPb2k/z6em4AQCFYAcdd8+0TOzPWiuLxRyCHRPBBVhnPsXOKgKwE4OO3E8BsezquuYRYRwsyzCqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/extension-blockquote": "^3.22.2",
|
||||
"@tiptap/extension-bold": "^3.22.2",
|
||||
"@tiptap/extension-bullet-list": "^3.22.2",
|
||||
"@tiptap/extension-code": "^3.22.2",
|
||||
"@tiptap/extension-code-block": "^3.22.2",
|
||||
"@tiptap/extension-document": "^3.22.2",
|
||||
"@tiptap/extension-dropcursor": "^3.22.2",
|
||||
"@tiptap/extension-gapcursor": "^3.22.2",
|
||||
"@tiptap/extension-hard-break": "^3.22.2",
|
||||
"@tiptap/extension-heading": "^3.22.2",
|
||||
"@tiptap/extension-horizontal-rule": "^3.22.2",
|
||||
"@tiptap/extension-italic": "^3.22.2",
|
||||
"@tiptap/extension-link": "^3.22.2",
|
||||
"@tiptap/extension-list": "^3.22.2",
|
||||
"@tiptap/extension-list-item": "^3.22.2",
|
||||
"@tiptap/extension-list-keymap": "^3.22.2",
|
||||
"@tiptap/extension-ordered-list": "^3.22.2",
|
||||
"@tiptap/extension-paragraph": "^3.22.2",
|
||||
"@tiptap/extension-strike": "^3.22.2",
|
||||
"@tiptap/extension-text": "^3.22.2",
|
||||
"@tiptap/extension-underline": "^3.22.2",
|
||||
"@tiptap/extensions": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
@@ -1741,6 +2213,28 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/markdown-it": {
|
||||
"version": "14.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
|
||||
"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/linkify-it": "^5",
|
||||
"@types/mdurl": "^2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mdurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||
@@ -1807,7 +2301,6 @@
|
||||
"version": "18.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
|
||||
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^18.0.0"
|
||||
@@ -1855,6 +2348,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
@@ -1974,6 +2473,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/aria-hidden": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
|
||||
@@ -2662,6 +3167,12 @@
|
||||
"resolved": "server",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz",
|
||||
@@ -2938,6 +3449,18 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/env-paths": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz",
|
||||
@@ -3053,6 +3576,18 @@
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
@@ -3195,6 +3730,15 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz",
|
||||
"integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
@@ -4037,6 +4581,21 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"uc.micro": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/linkifyjs": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz",
|
||||
"integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
@@ -4138,6 +4697,23 @@
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it": {
|
||||
"version": "14.1.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
|
||||
"integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1",
|
||||
"entities": "^4.4.0",
|
||||
"linkify-it": "^5.0.0",
|
||||
"mdurl": "^2.0.0",
|
||||
"punycode.js": "^2.3.1",
|
||||
"uc.micro": "^2.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"markdown-it": "bin/markdown-it.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@@ -4147,6 +4723,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mdurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@@ -4505,6 +5087,12 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/orderedmap": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
|
||||
"integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
@@ -4836,6 +5424,201 @@
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/prosemirror-changeset": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.0.tgz",
|
||||
"integrity": "sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-collab": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz",
|
||||
"integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-commands": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
|
||||
"integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-dropcursor": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz",
|
||||
"integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0",
|
||||
"prosemirror-view": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-gapcursor": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.1.tgz",
|
||||
"integrity": "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.0.0",
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-view": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-history": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz",
|
||||
"integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.2.2",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.31.0",
|
||||
"rope-sequence": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-inputrules": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz",
|
||||
"integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-keymap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz",
|
||||
"integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"w3c-keyname": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-markdown": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.4.tgz",
|
||||
"integrity": "sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/markdown-it": "^14.0.0",
|
||||
"markdown-it": "^14.0.0",
|
||||
"prosemirror-model": "^1.25.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-menu": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.3.0.tgz",
|
||||
"integrity": "sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"crelt": "^1.0.0",
|
||||
"prosemirror-commands": "^1.0.0",
|
||||
"prosemirror-history": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-model": {
|
||||
"version": "1.25.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
||||
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"orderedmap": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-schema-basic": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz",
|
||||
"integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.25.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-schema-list": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
|
||||
"integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-state": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
||||
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.27.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-tables": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz",
|
||||
"integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.2.3",
|
||||
"prosemirror-model": "^1.25.4",
|
||||
"prosemirror-state": "^1.4.4",
|
||||
"prosemirror-transform": "^1.10.5",
|
||||
"prosemirror-view": "^1.41.4"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-trailing-node": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
|
||||
"integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remirror/core-constants": "3.0.0",
|
||||
"escape-string-regexp": "^4.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prosemirror-model": "^1.22.1",
|
||||
"prosemirror-state": "^1.4.2",
|
||||
"prosemirror-view": "^1.33.8"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-transform": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.12.0.tgz",
|
||||
"integrity": "sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-view": {
|
||||
"version": "1.41.8",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.8.tgz",
|
||||
"integrity": "sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.20.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -4858,6 +5641,15 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode.js": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pure-rand": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
|
||||
@@ -5331,6 +6123,12 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rope-sequence": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
|
||||
"integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@@ -6034,6 +6832,12 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
|
||||
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||
@@ -6307,6 +7111,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
|
||||
812
package-lock.json
generated
812
package-lock.json
generated
@@ -24,6 +24,12 @@
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toast": "^1.1.5",
|
||||
"@tanstack/react-query": "^5.17.0",
|
||||
"@tiptap/extension-placeholder": "^3.22.2",
|
||||
"@tiptap/extension-text-align": "^3.22.2",
|
||||
"@tiptap/extension-underline": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2",
|
||||
"@tiptap/react": "^3.22.2",
|
||||
"@tiptap/starter-kit": "^3.22.2",
|
||||
"axios": "^1.6.5",
|
||||
"clsx": "^2.1.0",
|
||||
"i18next": "^26.0.3",
|
||||
@@ -2434,6 +2440,12 @@
|
||||
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@remirror/core-constants": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
|
||||
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
|
||||
@@ -2871,6 +2883,466 @@
|
||||
"react": "^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/core": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.22.2.tgz",
|
||||
"integrity": "sha512-atq35NkpeEphH6vNYJ0pTLLBA73FAbvTV9Ovd3AaTC5s99/KF5Q86zVJXvml8xPRcMGM6dLp+eSSd06oTscMSA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-blockquote": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.22.2.tgz",
|
||||
"integrity": "sha512-iTdlmGFcgxi4LKaOW2Rc9/yD83qTXgRm5BN3vCHWy5+TbEnReYxYqU5qKsbtTbKy30sO8TJTdAXTZ29uomShQQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bold": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.22.2.tgz",
|
||||
"integrity": "sha512-bqsPJyKcT/RWse4e16U2EKhraR8a2+98TUuk1amG3yCyFJZStoO/j+pN0IqZdZZjr3WtxFyvwWp7Kc59UN+jUA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bubble-menu": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.22.2.tgz",
|
||||
"integrity": "sha512-5hbyDOSkJwA2uh0v9Mm0Dd9bb9inx6tHBEDSH2tCB9Rm23poz3yOreB7SNX8xDMe5L0/PQesfWC14RitcmhKPg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bullet-list": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.22.2.tgz",
|
||||
"integrity": "sha512-llrTJnA72RGcWLLO+ro0QN4sjHynhaCerhpV+GZE/ATd8BqV/ekQFdBLJrvC/09My2XQfCwLsyCh92NPXUdELA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.22.2.tgz",
|
||||
"integrity": "sha512-iYFY+yzfYA9MKt7nupyW/PzqL9XC2D0mC8l1z2Y10i0/fGL8NbqIYjhNUAyXGqH3QWcI+DirI66842y2OadPOg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code-block": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.22.2.tgz",
|
||||
"integrity": "sha512-PEwFlDyvtKF19WCrOFg77qJV9WqhvjCY4ZoXlHP9Hx0KTcOA8W39mtw8d4NWU5pLRK94yHKF1DVVL8UUkEOnww==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-document": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.22.2.tgz",
|
||||
"integrity": "sha512-yPw9pQeVC4QDh86TuyKCZxxM4g0NAw7mEtGnAo6EpxaBQr1wyBr9yFpys+QTsQpRTmyTf1VHp4iTTLuWHMljIw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-dropcursor": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.22.2.tgz",
|
||||
"integrity": "sha512-sDv3fv4LtX0X4nqwh9Gn3C/aZXT+C2JlK7tJovPOpaYP/a6hr03Sn35X5moAfgMCSiWFygEvlTriqwmCsJuxog==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-floating-menu": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.22.2.tgz",
|
||||
"integrity": "sha512-r0ZTeh9rNtj9Api+G0YyaB+tAKPDn7aYWg+qSrmAC5EyUPee6Zjn3zlw0q4renCeQflvNRK20xHM8zokC41jOA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-gapcursor": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.22.2.tgz",
|
||||
"integrity": "sha512-rR2OLrl/k2kj7xehaZHq0Y7T+1wy2DOTabir9LsTrktTFEcklrh9qY1KC6rEBkwMKaWrmignR1l39kS6RlKFNw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-hard-break": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.22.2.tgz",
|
||||
"integrity": "sha512-ChsoqF4XRp6EWatTRlXL4LMFh/ggwRVCyt09brSfjJV5knFaXlECSa5/+rKLMLMULaj6dVlJqoAD15exgu2HHA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-heading": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.22.2.tgz",
|
||||
"integrity": "sha512-QPHLef+ikAyf7RVc4EdGeKxH4OEGb3ueCEwJ41RcYPtZ1BX9ueei7FC936guTdL1U7w3vQ65qfy86HznzkYgvw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-horizontal-rule": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.22.2.tgz",
|
||||
"integrity": "sha512-Oz8KN5KJAWV1mFNE9UIWXdMD6xa5zPf/0yLsT8V4sgaRm+VsdFKllN58BY9qCZf/kIZbaOez5KkaoeAcm0MAZg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-italic": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.22.2.tgz",
|
||||
"integrity": "sha512-fmtQu2HDnV3sOZPdz0+1lOLI7UtrIhusohJj2UwOLQxG8qqhLwbvWx2OQTlfblgY0z+CjLRr6ANbNDxOTIblfg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-link": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.22.2.tgz",
|
||||
"integrity": "sha512-TXfSoKmng5pecvQUZqdsx6ICeob5V5hhYOj2vCEtjfcjWsyCndqFIl1w+Nt/yI5ehrFNOVPyj3ZvcELuuAW6pw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"linkifyjs": "^4.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.22.2.tgz",
|
||||
"integrity": "sha512-Vq9xScgkA2A3Zj9dQ4WUBKK7u7UCzeSFRz9FcKTQVZHRPbZoqFGnlRUVngqsE7JXrCOthXQ1dXxgk40nAsBFRw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-item": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.22.2.tgz",
|
||||
"integrity": "sha512-Mk+iiLIFh8Pfuarr6mWfTO7QJbd2ZQd0nGNhNWXlGAO7DJCb4BP9nj4bEIJ17SbcykGRjsi4WMqY50z4MHXqKQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-keymap": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.22.2.tgz",
|
||||
"integrity": "sha512-TozU9V2vldMUPpTXnfLCO33EO06jLxn7uEJTMBnN4iX/dLV3cBVCbE4kHyDKS0sLd7joUeekS06vYP9uQb1hFw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-ordered-list": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.22.2.tgz",
|
||||
"integrity": "sha512-K7qxoBKmsVkAd3kW64ZRCUPFrDcNGpXRDUBx9YgAO/bTfsfxtH2oil+igsUWGXPczpP4yoHPKjTfhpBpLjGl6Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-paragraph": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.22.2.tgz",
|
||||
"integrity": "sha512-EHZZzxVhvzEPDPWtRBF1YKhB+WCUjd1C2NhjHfL3Dl71PBqM3ZWA6qN7NDGPyNyGGWauui/NR/4X+5AfPqlHyA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-placeholder": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-3.22.2.tgz",
|
||||
"integrity": "sha512-xYw733CmSeG7MyYBDdV5NFiwlBdXXzw4Mvjb2t4QRXagkDbHeNY/LtKTcrtcMNfO4Jx0mwivGQZUIEC8oAfvxg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-strike": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.22.2.tgz",
|
||||
"integrity": "sha512-YFC3elKU1L8PiGbcB6tqd/7vWPF5IbydJz0POJpHzSjstX+VfT8VsvS7ubxVuSIWQ11kGkH3mzX6LX8JHsHZxg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.22.2.tgz",
|
||||
"integrity": "sha512-J1w7JwijfSD7ah0WfiwZ/DVWCIGT9x369RM4RJc57i44mIBElj7tl1dh+N5KPGOXKUup4gr7sSJAE38lgeaDMg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text-align": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-3.22.2.tgz",
|
||||
"integrity": "sha512-pgqyXzVHo4WmDhK26rDwhK2lxQwnjl/9DP816C2k3To/fZRK1eW7q0pSAYteHWmKkaYAxwj/0UvCU0nXKlPujw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-underline": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.22.2.tgz",
|
||||
"integrity": "sha512-BaV6WOowxdkGTLWiU7DdZ3Twh633O4RGqwUM5dDas5LvaqL8AMWGTO8Wg9yAaaKXzd9MtKI1ZCqS/+MtzusgkQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extensions": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.22.2.tgz",
|
||||
"integrity": "sha512-s7MZmm2Xdq+8feIXgY3v7gVpQ5ClqBZi20KheouS7KSbBlrY4fu2irYR1EGc6r1UUVaHMxEa+cx5knhx+mIPUw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/pm": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.22.2.tgz",
|
||||
"integrity": "sha512-G2ENwIazoSKkAnN5MN5yN91TIZNFm6TxB74kPf3Empr2k9W51Hkcier70jHGpArhgcEaL4BVreuU1PRDRwCeGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-changeset": "^2.3.0",
|
||||
"prosemirror-collab": "^1.3.1",
|
||||
"prosemirror-commands": "^1.6.2",
|
||||
"prosemirror-dropcursor": "^1.8.1",
|
||||
"prosemirror-gapcursor": "^1.3.2",
|
||||
"prosemirror-history": "^1.4.1",
|
||||
"prosemirror-inputrules": "^1.4.0",
|
||||
"prosemirror-keymap": "^1.2.2",
|
||||
"prosemirror-markdown": "^1.13.1",
|
||||
"prosemirror-menu": "^1.2.4",
|
||||
"prosemirror-model": "^1.24.1",
|
||||
"prosemirror-schema-basic": "^1.2.3",
|
||||
"prosemirror-schema-list": "^1.5.0",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-tables": "^1.6.4",
|
||||
"prosemirror-trailing-node": "^3.0.0",
|
||||
"prosemirror-transform": "^1.10.2",
|
||||
"prosemirror-view": "^1.38.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/react": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.22.2.tgz",
|
||||
"integrity": "sha512-tyGKG69e/MkpoD/JTpVPz0XydEHxh1MSAYnLb3gRvyvBDv2r/veLea+cApkmjQaCfkKC/CWwTFXBYlOB0caSBA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/use-sync-external-store": "^0.0.6",
|
||||
"fast-equals": "^5.3.3",
|
||||
"use-sync-external-store": "^1.4.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tiptap/extension-bubble-menu": "^3.22.2",
|
||||
"@tiptap/extension-floating-menu": "^3.22.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/starter-kit": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.22.2.tgz",
|
||||
"integrity": "sha512-+CCKX8tOQ/ZPb2k/z6em4AQCFYAcdd8+0TOzPWiuLxRyCHRPBBVhnPsXOKgKwE4OO3E8BsezquuYRYRwsyzCqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tiptap/core": "^3.22.2",
|
||||
"@tiptap/extension-blockquote": "^3.22.2",
|
||||
"@tiptap/extension-bold": "^3.22.2",
|
||||
"@tiptap/extension-bullet-list": "^3.22.2",
|
||||
"@tiptap/extension-code": "^3.22.2",
|
||||
"@tiptap/extension-code-block": "^3.22.2",
|
||||
"@tiptap/extension-document": "^3.22.2",
|
||||
"@tiptap/extension-dropcursor": "^3.22.2",
|
||||
"@tiptap/extension-gapcursor": "^3.22.2",
|
||||
"@tiptap/extension-hard-break": "^3.22.2",
|
||||
"@tiptap/extension-heading": "^3.22.2",
|
||||
"@tiptap/extension-horizontal-rule": "^3.22.2",
|
||||
"@tiptap/extension-italic": "^3.22.2",
|
||||
"@tiptap/extension-link": "^3.22.2",
|
||||
"@tiptap/extension-list": "^3.22.2",
|
||||
"@tiptap/extension-list-item": "^3.22.2",
|
||||
"@tiptap/extension-list-keymap": "^3.22.2",
|
||||
"@tiptap/extension-ordered-list": "^3.22.2",
|
||||
"@tiptap/extension-paragraph": "^3.22.2",
|
||||
"@tiptap/extension-strike": "^3.22.2",
|
||||
"@tiptap/extension-text": "^3.22.2",
|
||||
"@tiptap/extension-underline": "^3.22.2",
|
||||
"@tiptap/extensions": "^3.22.2",
|
||||
"@tiptap/pm": "^3.22.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
@@ -3003,6 +3475,28 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/markdown-it": {
|
||||
"version": "14.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
|
||||
"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/linkify-it": "^5",
|
||||
"@types/mdurl": "^2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mdurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||
@@ -3069,7 +3563,6 @@
|
||||
"version": "18.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
|
||||
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^18.0.0"
|
||||
@@ -3117,6 +3610,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
@@ -3236,6 +3735,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/aria-hidden": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
|
||||
@@ -3924,6 +4429,12 @@
|
||||
"resolved": "server",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz",
|
||||
@@ -4200,6 +4711,18 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/env-paths": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz",
|
||||
@@ -4315,6 +4838,18 @@
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
@@ -4457,6 +4992,15 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz",
|
||||
"integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
@@ -5314,6 +5858,21 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"uc.micro": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/linkifyjs": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz",
|
||||
"integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
@@ -5415,6 +5974,23 @@
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it": {
|
||||
"version": "14.1.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
|
||||
"integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1",
|
||||
"entities": "^4.4.0",
|
||||
"linkify-it": "^5.0.0",
|
||||
"mdurl": "^2.0.0",
|
||||
"punycode.js": "^2.3.1",
|
||||
"uc.micro": "^2.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"markdown-it": "bin/markdown-it.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@@ -5424,6 +6000,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mdurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@@ -5784,6 +6366,12 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/orderedmap": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
|
||||
"integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
@@ -6115,6 +6703,201 @@
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/prosemirror-changeset": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.0.tgz",
|
||||
"integrity": "sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-collab": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz",
|
||||
"integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-commands": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
|
||||
"integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-dropcursor": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz",
|
||||
"integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0",
|
||||
"prosemirror-view": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-gapcursor": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.1.tgz",
|
||||
"integrity": "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.0.0",
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-view": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-history": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz",
|
||||
"integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.2.2",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.31.0",
|
||||
"rope-sequence": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-inputrules": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz",
|
||||
"integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-keymap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz",
|
||||
"integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"w3c-keyname": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-markdown": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.4.tgz",
|
||||
"integrity": "sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/markdown-it": "^14.0.0",
|
||||
"markdown-it": "^14.0.0",
|
||||
"prosemirror-model": "^1.25.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-menu": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.3.0.tgz",
|
||||
"integrity": "sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"crelt": "^1.0.0",
|
||||
"prosemirror-commands": "^1.0.0",
|
||||
"prosemirror-history": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-model": {
|
||||
"version": "1.25.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
||||
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"orderedmap": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-schema-basic": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz",
|
||||
"integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.25.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-schema-list": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
|
||||
"integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-state": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
||||
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.27.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-tables": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz",
|
||||
"integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.2.3",
|
||||
"prosemirror-model": "^1.25.4",
|
||||
"prosemirror-state": "^1.4.4",
|
||||
"prosemirror-transform": "^1.10.5",
|
||||
"prosemirror-view": "^1.41.4"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-trailing-node": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
|
||||
"integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remirror/core-constants": "3.0.0",
|
||||
"escape-string-regexp": "^4.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prosemirror-model": "^1.22.1",
|
||||
"prosemirror-state": "^1.4.2",
|
||||
"prosemirror-view": "^1.33.8"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-transform": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.12.0.tgz",
|
||||
"integrity": "sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-view": {
|
||||
"version": "1.41.8",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.8.tgz",
|
||||
"integrity": "sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.20.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -6137,6 +6920,15 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode.js": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pure-rand": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
|
||||
@@ -6610,6 +7402,12 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rope-sequence": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
|
||||
"integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@@ -7313,6 +8111,12 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
|
||||
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||
@@ -7960,6 +8764,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
|
||||
55
server/dist/api/books.js
vendored
55
server/dist/api/books.js
vendored
@@ -46,16 +46,22 @@ Core Idea: ${idea}
|
||||
|
||||
Generate a detailed chapter outline following the structure: ${template.structure.join(' → ')}
|
||||
|
||||
IMPORTANT: The response MUST be written in ${targetLang}. All text values (title, logline, chapter summaries, etc) must be translated to ${targetLang}. Keep the exact JSON KEYS in English.
|
||||
CRITICAL INSTRUCTIONS:
|
||||
- You MUST respond with VALID JSON only
|
||||
- Do NOT include any text before or after the JSON
|
||||
- Do NOT wrap the JSON in markdown code blocks
|
||||
- All text content (titles, summaries, etc.) MUST be in ${targetLang}
|
||||
- JSON keys MUST remain in English
|
||||
- Ensure the JSON is properly formatted and parseable
|
||||
|
||||
Return the response in JSON format:
|
||||
Required JSON format:
|
||||
{
|
||||
"title": "Book Title",
|
||||
"title": "Book Title in ${targetLang}",
|
||||
"genre": "${genre}",
|
||||
"logline": "One sentence summary",
|
||||
"logline": "One sentence summary in ${targetLang}",
|
||||
"chapters": [
|
||||
{"number": 1, "title": "Chapter Title", "summary": "Brief description"},
|
||||
...
|
||||
{"number": 1, "title": "Chapter Title in ${targetLang}", "summary": "Brief description in ${targetLang}"},
|
||||
{"number": 2, "title": "Chapter Title in ${targetLang}", "summary": "Brief description in ${targetLang}"}
|
||||
]
|
||||
}`;
|
||||
const response = await axios_1.default.post('https://openrouter.ai/api/v1/chat/completions', {
|
||||
@@ -74,22 +80,29 @@ Return the response in JSON format:
|
||||
const content = response.data.choices[0].message.content;
|
||||
console.log('=== Outline AI Response ===');
|
||||
console.log('Raw content:', content);
|
||||
// Try to parse JSON from response
|
||||
console.log('Target language:', targetLang);
|
||||
// Try to parse JSON from response - improved parsing for multilingual content
|
||||
let outline;
|
||||
try {
|
||||
// First try to find JSON object
|
||||
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
||||
if (jsonMatch) {
|
||||
try {
|
||||
outline = JSON.parse(jsonMatch[0]);
|
||||
// Clean the JSON string to handle potential encoding issues
|
||||
let jsonString = jsonMatch[0];
|
||||
// Remove any markdown formatting that might interfere
|
||||
jsonString = jsonString.replace(/```json\s*/g, '').replace(/```\s*$/g, '');
|
||||
outline = JSON.parse(jsonString);
|
||||
console.log('Parsed outline:', outline);
|
||||
}
|
||||
catch (parseError) {
|
||||
console.error('JSON parse error:', parseError);
|
||||
outline = { raw: content, error: 'Failed to parse JSON' };
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.error('No JSON object found in response');
|
||||
outline = { raw: content, error: 'No JSON found' };
|
||||
outline = { raw: content, error: 'No JSON found in AI response' };
|
||||
}
|
||||
}
|
||||
catch (parseError) {
|
||||
console.error('JSON parse error:', parseError);
|
||||
console.error('Failed content:', content);
|
||||
outline = { raw: content, error: 'Failed to parse JSON from AI response' };
|
||||
}
|
||||
res.json({ outline });
|
||||
}
|
||||
@@ -117,8 +130,11 @@ Summary: ${chapterSummary}
|
||||
Tone: ${template.defaults.tone}
|
||||
POV: ${template.defaults.pov}
|
||||
|
||||
IMPORTANT: The entire chapter content MUST be written strictly in ${targetLang}.
|
||||
`;
|
||||
CRITICAL: Write the ENTIRE chapter content in ${targetLang} only. Do NOT include any English text.
|
||||
Write a complete, engaging chapter that matches the summary above.
|
||||
The chapter should be between 1500-3000 words.
|
||||
|
||||
IMPORTANT: Respond with the chapter content directly, without any introduction, conclusion, or formatting markers. Just the narrative text in ${targetLang}.`;
|
||||
if (previousContent) {
|
||||
prompt += `\n\nPrevious content for context:\n${previousContent.substring(0, 2000)}...`;
|
||||
}
|
||||
@@ -136,6 +152,11 @@ IMPORTANT: The entire chapter content MUST be written strictly in ${targetLang}.
|
||||
}
|
||||
});
|
||||
const content = response.data.choices[0].message.content;
|
||||
console.log('=== Chapter AI Response ===');
|
||||
console.log('Target language:', targetLang);
|
||||
console.log('Chapter title:', chapterTitle);
|
||||
console.log('Content length:', content.length);
|
||||
console.log('Content preview:', content.substring(0, 200) + '...');
|
||||
res.json({
|
||||
content,
|
||||
chapterTitle
|
||||
|
||||
2
server/dist/api/books.js.map
vendored
2
server/dist/api/books.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -49,16 +49,22 @@ Core Idea: ${idea}
|
||||
|
||||
Generate a detailed chapter outline following the structure: ${template.structure.join(' → ')}
|
||||
|
||||
IMPORTANT: The response MUST be written in ${targetLang}. All text values (title, logline, chapter summaries, etc) must be translated to ${targetLang}. Keep the exact JSON KEYS in English.
|
||||
CRITICAL INSTRUCTIONS:
|
||||
- You MUST respond with VALID JSON only
|
||||
- Do NOT include any text before or after the JSON
|
||||
- Do NOT wrap the JSON in markdown code blocks
|
||||
- All text content (titles, summaries, etc.) MUST be in ${targetLang}
|
||||
- JSON keys MUST remain in English
|
||||
- Ensure the JSON is properly formatted and parseable
|
||||
|
||||
Return the response in JSON format:
|
||||
Required JSON format:
|
||||
{
|
||||
"title": "Book Title",
|
||||
"title": "Book Title in ${targetLang}",
|
||||
"genre": "${genre}",
|
||||
"logline": "One sentence summary",
|
||||
"logline": "One sentence summary in ${targetLang}",
|
||||
"chapters": [
|
||||
{"number": 1, "title": "Chapter Title", "summary": "Brief description"},
|
||||
...
|
||||
{"number": 1, "title": "Chapter Title in ${targetLang}", "summary": "Brief description in ${targetLang}"},
|
||||
{"number": 2, "title": "Chapter Title in ${targetLang}", "summary": "Brief description in ${targetLang}"}
|
||||
]
|
||||
}`;
|
||||
|
||||
@@ -80,22 +86,28 @@ Return the response in JSON format:
|
||||
|
||||
console.log('=== Outline AI Response ===');
|
||||
console.log('Raw content:', content);
|
||||
console.log('Target language:', targetLang);
|
||||
|
||||
// Try to parse JSON from response
|
||||
// Try to parse JSON from response - improved parsing for multilingual content
|
||||
let outline;
|
||||
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
||||
|
||||
if (jsonMatch) {
|
||||
try {
|
||||
outline = JSON.parse(jsonMatch[0]);
|
||||
// First try to find JSON object
|
||||
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
||||
if (jsonMatch) {
|
||||
// Clean the JSON string to handle potential encoding issues
|
||||
let jsonString = jsonMatch[0];
|
||||
// Remove any markdown formatting that might interfere
|
||||
jsonString = jsonString.replace(/```json\s*/g, '').replace(/```\s*$/g, '');
|
||||
outline = JSON.parse(jsonString);
|
||||
console.log('Parsed outline:', outline);
|
||||
} catch (parseError) {
|
||||
console.error('JSON parse error:', parseError);
|
||||
outline = { raw: content, error: 'Failed to parse JSON' };
|
||||
}
|
||||
} else {
|
||||
console.error('No JSON object found in response');
|
||||
outline = { raw: content, error: 'No JSON found' };
|
||||
outline = { raw: content, error: 'No JSON found in AI response' };
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.error('JSON parse error:', parseError);
|
||||
console.error('Failed content:', content);
|
||||
outline = { raw: content, error: 'Failed to parse JSON from AI response' };
|
||||
}
|
||||
|
||||
res.json({ outline });
|
||||
@@ -127,8 +139,11 @@ Summary: ${chapterSummary}
|
||||
Tone: ${template.defaults.tone}
|
||||
POV: ${template.defaults.pov}
|
||||
|
||||
IMPORTANT: The entire chapter content MUST be written strictly in ${targetLang}.
|
||||
`;
|
||||
CRITICAL: Write the ENTIRE chapter content in ${targetLang} only. Do NOT include any English text.
|
||||
Write a complete, engaging chapter that matches the summary above.
|
||||
The chapter should be between 1500-3000 words.
|
||||
|
||||
IMPORTANT: Respond with the chapter content directly, without any introduction, conclusion, or formatting markers. Just the narrative text in ${targetLang}.`;
|
||||
|
||||
if (previousContent) {
|
||||
prompt += `\n\nPrevious content for context:\n${previousContent.substring(0, 2000)}...`;
|
||||
@@ -150,6 +165,12 @@ IMPORTANT: The entire chapter content MUST be written strictly in ${targetLang}.
|
||||
|
||||
const content = response.data.choices[0].message.content;
|
||||
|
||||
console.log('=== Chapter AI Response ===');
|
||||
console.log('Target language:', targetLang);
|
||||
console.log('Chapter title:', chapterTitle);
|
||||
console.log('Content length:', content.length);
|
||||
console.log('Content preview:', content.substring(0, 200) + '...');
|
||||
|
||||
res.json({
|
||||
content,
|
||||
chapterTitle
|
||||
|
||||
Reference in New Issue
Block a user