diff --git a/app/api/generate/scene-content/route.ts b/app/api/generate/scene-content/route.ts index db9b772e..1825d3d8 100644 --- a/app/api/generate/scene-content/route.ts +++ b/app/api/generate/scene-content/route.ts @@ -67,7 +67,7 @@ export async function POST(req: NextRequest) { // Ensure outline has language from stageInfo (fallback for older outlines) const outline: SceneOutline = { ...rawOutline, - language: rawOutline.language || (stageInfo?.language as 'zh-CN' | 'en-US') || 'zh-CN', + language: rawOutline.language || (stageInfo?.language as 'zh-CN' | 'en-US' | 'ru-RU') || 'zh-CN', }; // ── Model resolution from request headers ── diff --git a/app/page.tsx b/app/page.tsx index 80dfbd85..ecfbba77 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -56,7 +56,7 @@ const RECENT_OPEN_STORAGE_KEY = 'recentClassroomsOpen'; interface FormState { pdfFile: File | null; requirement: string; - language: 'zh-CN' | 'en-US'; + language: 'zh-CN' | 'en-US' | 'ru-RU'; webSearch: boolean; } @@ -101,10 +101,10 @@ function HomePage() { const savedLanguage = localStorage.getItem(LANGUAGE_STORAGE_KEY); const updates: Partial = {}; if (savedWebSearch === 'true') updates.webSearch = true; - if (savedLanguage === 'zh-CN' || savedLanguage === 'en-US') { + if (savedLanguage === 'zh-CN' || savedLanguage === 'en-US' || savedLanguage === 'ru-RU') { updates.language = savedLanguage; } else { - const detected = navigator.language?.startsWith('zh') ? 'zh-CN' : 'en-US'; + const detected = navigator.language?.startsWith('zh') ? 'zh-CN' : navigator.language?.startsWith('ru') ? 'ru-RU' : 'en-US'; updates.language = detected; } if (Object.keys(updates).length > 0) { @@ -338,7 +338,7 @@ function HomePage() { }} className="flex items-center gap-1 px-3 py-1.5 rounded-full text-xs font-bold text-gray-500 dark:text-gray-400 hover:bg-white dark:hover:bg-gray-700 hover:text-gray-800 dark:hover:text-gray-200 hover:shadow-sm transition-all" > - {locale === 'zh-CN' ? 'CN' : 'EN'} + {locale === 'zh-CN' ? 'CN' : locale === 'ru-RU' ? 'RU' : 'EN'} {languageOpen && (
@@ -366,7 +366,20 @@ function HomePage() { 'bg-purple-50 dark:bg-purple-900/20 text-purple-600 dark:text-purple-400', )} > - English + English + +
)} diff --git a/components/generation/generation-toolbar.tsx b/components/generation/generation-toolbar.tsx index 27301bbd..139619bf 100644 --- a/components/generation/generation-toolbar.tsx +++ b/components/generation/generation-toolbar.tsx @@ -28,8 +28,8 @@ const MAX_PDF_SIZE_BYTES = MAX_PDF_SIZE_MB * 1024 * 1024; // ─── Types ─────────────────────────────────────────────────── export interface GenerationToolbarProps { - language: 'zh-CN' | 'en-US'; - onLanguageChange: (lang: 'zh-CN' | 'en-US') => void; + language: 'zh-CN' | 'en-US' | 'ru-RU'; + onLanguageChange: (lang: 'zh-CN' | 'en-US' | 'ru-RU') => void; webSearch: boolean; onWebSearchChange: (v: boolean) => void; onSettingsOpen: (section?: SettingsSection) => void; @@ -361,11 +361,14 @@ export function GenerationToolbar({ {t('toolbar.languageHint')} diff --git a/lib/generation/scene-generator.ts b/lib/generation/scene-generator.ts index 1dc22937..268a3a86 100644 --- a/lib/generation/scene-generator.ts +++ b/lib/generation/scene-generator.ts @@ -735,7 +735,7 @@ function normalizeQuizAnswer(question: Record): string[] | unde async function generateInteractiveContent( outline: SceneOutline, aiCall: AICallFn, - language: 'zh-CN' | 'en-US' = 'zh-CN', + language: 'zh-CN' | 'en-US' | 'ru-RU' = 'zh-CN', ): Promise { const config = outline.interactiveConfig!; diff --git a/lib/hooks/use-i18n.tsx b/lib/hooks/use-i18n.tsx index 4e642f4c..27722794 100644 --- a/lib/hooks/use-i18n.tsx +++ b/lib/hooks/use-i18n.tsx @@ -10,7 +10,7 @@ type I18nContextType = { }; const LOCALE_STORAGE_KEY = 'locale'; -const VALID_LOCALES: Locale[] = ['zh-CN', 'en-US']; +const VALID_LOCALES: Locale[] = ['zh-CN', 'en-US', 'ru-RU']; const I18nContext = createContext(undefined); @@ -26,7 +26,7 @@ export function I18nProvider({ children }: { children: ReactNode }) { setLocaleState(stored as Locale); return; } - const detected = navigator.language?.startsWith('zh') ? 'zh-CN' : 'en-US'; + const detected = navigator.language?.startsWith('zh') ? 'zh-CN' : navigator.language?.startsWith('ru') ? 'ru-RU' : 'en-US'; localStorage.setItem(LOCALE_STORAGE_KEY, detected); setLocaleState(detected); } catch { diff --git a/lib/i18n/chat.ts b/lib/i18n/chat.ts index 1bb535d3..5aba6e38 100644 --- a/lib/i18n/chat.ts +++ b/lib/i18n/chat.ts @@ -145,3 +145,77 @@ export const chatEnUS = { stopListening: 'Stop recording', }, } as const; + +export const chatRuRU = { + chat: { + lecture: 'Лекция', + noConversations: 'Нет диалогов', + startConversation: 'Введите сообщение, чтобы начать диалог', + noMessages: 'Пока нет сообщений', + ended: 'завершено', + unknown: 'Неизвестно', + stopDiscussion: 'Завершить обсуждение', + endQA: 'Завершить вопросы и ответы', + tabs: { + lecture: 'Заметки', + chat: 'Чат', + }, + lectureNotes: { + empty: 'Заметки появятся здесь после воспроизведения лекции', + emptyHint: 'Нажмите воспроизведение для начала лекции', + pageLabel: 'Страница {n}', + currentPage: 'Текущая', + }, + badge: { + qa: 'Вопросы', + discussion: 'ДИСКУС', + lecture: 'ЛЕКЦ', + }, + }, + actions: { + names: { + spotlight: 'Прожектор', + laser: 'Лазерная указка', + wb_open: 'Открыть доску', + wb_draw_text: 'Текст на доске', + wb_draw_shape: 'Фигура на доске', + wb_draw_chart: 'График на доске', + wb_draw_latex: 'Формула на доске', + wb_draw_table: 'Таблица на доске', + wb_draw_line: 'Линия на доске', + wb_clear: 'Очистить доску', + wb_delete: 'Удалить элемент', + wb_close: 'Закрыть доску', + discussion: 'Обсуждение', + }, + status: { + inputStreaming: 'Ожидание', + inputAvailable: 'Выполняется', + outputAvailable: 'Завершено', + outputError: 'Ошибка', + outputDenied: 'Отклонено', + running: 'Выполняется', + result: 'Завершено', + error: 'Ошибка', + }, + }, + agentBar: { + readyToLearn: 'Готовы учиться вместе?', + expandedTitle: 'Настройка ролей в классе', + configTooltip: 'Нажмите для настройки ролей в классе', + voiceLabel: 'Голос', + voiceLoading: 'Загрузка...', + voiceAutoAssign: 'Голоса будут назначены автоматически', + }, + proactiveCard: { + discussion: 'Обсуждение', + join: 'Присоединиться', + skip: 'Пропустить', + pause: 'Пауза', + resume: 'Продолжить', + }, + voice: { + startListening: 'Голосовой ввод', + stopListening: 'Остановить запись', + }, +} as const; diff --git a/lib/i18n/common.ts b/lib/i18n/common.ts index 1bceb5d6..6fcb96f5 100644 --- a/lib/i18n/common.ts +++ b/lib/i18n/common.ts @@ -79,3 +79,44 @@ export const commonEnUS = { exportFailed: 'Export failed', }, } as const; + +export const commonRuRU = { + common: { + you: 'Вы', + confirm: 'Подтвердить', + cancel: 'Отмена', + loading: 'Загрузка...', + }, + home: { + slogan: 'Generative Learning in Multi-Agent Interactive Classroom', + greeting: 'Привет, ', + }, + toolbar: { + languageHint: 'Курс будет сгенерирован на этом языке', + pdfParser: 'Парсер', + pdfUpload: 'Загрузить PDF', + removePdf: 'Удалить файл', + webSearchOn: 'Включено', + webSearchOff: 'Нажмите для включения', + webSearchDesc: 'Поиск актуальной информации в интернете перед генерацией', + webSearchProvider: 'Поисковый движок', + webSearchNoProvider: 'Настройте API-ключ поиска в Настройках', + selectProvider: 'Выбрать провайдера', + configureProvider: 'Настроить модель', + configureProviderHint: 'Настройте хотя бы одного провайдера моделей для генерации курсов', + enterClassroom: 'Войти в класс', + advancedSettings: 'Расширенные настройки', + ttsTitle: 'Синтез речи', + ttsHint: 'Выберите голос для AI-учителя', + ttsPreview: 'Прослушать', + ttsPreviewing: 'Воспроизведение...', + }, + export: { + pptx: 'Экспорт PPTX', + resourcePack: 'Экспорт ресурсного пакета', + resourcePackDesc: 'PPTX + интерактивные страницы', + exporting: 'Экспорт...', + exportSuccess: 'Экспорт успешен', + exportFailed: 'Ошибка экспорта', + }, +} as const; diff --git a/lib/i18n/generation.ts b/lib/i18n/generation.ts index 98694c23..0c9d9671 100644 --- a/lib/i18n/generation.ts +++ b/lib/i18n/generation.ts @@ -133,3 +133,65 @@ export const generationEnUS = { webSearchFailed: 'Web search failed', }, } as const; + +export const generationRuRU = { + upload: { + pdfSizeLimit: 'PDF до 50 МБ', + fileTooLarge: 'Файл слишком большой (макс. 50 МБ)', + requirementPlaceholder: 'Опишите что вы хотите изучить, и мы создадим для вас интерактивный курс...', + requirementRequired: 'Пожалуйста, укажите требования к курсу', + generateFailed: 'Ошибка генерации, попробуйте снова', + }, + generation: { + generating: 'Генерация курса...', + step1: 'Этап 1: Анализ требований', + step2: 'Этап 2: Генерация структуры сцен', + step3: 'Этап 3: Генерация содержимого', + progress: 'Прогресс: {progress}%', + scenesGenerated: 'Сгенерировано {count} сцен', + complete: 'Генерация завершена!', + failed: 'Ошибка генерации', + retry: 'Повторить', + }, + preview: { + title: 'Предпросмотр', + sceneCount: '{count} сцен', + startClassroom: 'Войти в класс', + regenerate: 'Сгенерировать заново', + editing: 'Редактирование', + sceneTitle: 'Сцена {index}: {title}', + slideType: 'Слайд', + quizType: 'Тест', + interactiveType: 'Интерактив', + pblType: 'Проект', + editOutlines: 'Изменить структуру', + applyOutlines: 'Применить изменения', + cancelOutlines: 'Отменить', + addOutline: 'Добавить сцену', + moveUp: 'Выше', + moveDown: 'Ниже', + deleteOutline: 'Удалить', + outlinesChanged: 'Структура изменена', + keyPoints: 'Ключевые моменты', + description: 'Описание', + autoSaved: 'Автосохранено', + lastEdited: 'Последнее редактирование', + }, + classroom: { + today: 'Сегодня', + yesterday: 'Вчера', + daysAgo: 'дн. назад', + recentClassrooms: 'Недавние классы', + untitled: 'Без названия', + deleteConfirm: 'Удалить этот класс?', + deleteHint: 'Это действие нельзя отменить', + delete: 'Удалить', + }, + controls: { + play: 'Воспроизвести / Пауза', + prev: 'Предыдущая', + next: 'Следующая', + fullscreen: 'Полный экран', + exitFullscreen: 'Развернуть', + }, +} as const; diff --git a/lib/i18n/index.ts b/lib/i18n/index.ts index 5fd70da5..190388b5 100644 --- a/lib/i18n/index.ts +++ b/lib/i18n/index.ts @@ -1,10 +1,10 @@ import { defaultLocale, type Locale } from './types'; export { type Locale, defaultLocale } from './types'; -import { commonZhCN, commonEnUS } from './common'; -import { stageZhCN, stageEnUS } from './stage'; -import { chatZhCN, chatEnUS } from './chat'; -import { generationZhCN, generationEnUS } from './generation'; -import { settingsZhCN, settingsEnUS } from './settings'; +import { commonZhCN, commonEnUS, commonRuRU } from './common'; +import { stageZhCN, stageEnUS, stageRuRU } from './stage'; +import { chatZhCN, chatEnUS, chatRuRU } from './chat'; +import { generationZhCN, generationEnUS, generationRuRU } from './generation'; +import { settingsZhCN, settingsEnUS, settingsRuRU } from './settings'; export const translations = { 'zh-CN': { @@ -21,6 +21,13 @@ export const translations = { ...generationEnUS, ...settingsEnUS, }, + 'ru-RU': { + ...commonRuRU, + ...stageRuRU, + ...chatRuRU, + ...generationRuRU, + ...settingsRuRU, + }, } as const; export type TranslationKey = keyof (typeof translations)[typeof defaultLocale]; @@ -40,7 +47,7 @@ export function getClientTranslation(key: string): string { if (typeof window !== 'undefined') { try { const storedLocale = localStorage.getItem('locale'); - if (storedLocale === 'zh-CN' || storedLocale === 'en-US') { + if (storedLocale === 'zh-CN' || storedLocale === 'en-US' || storedLocale === 'ru-RU') { locale = storedLocale; } } catch { @@ -50,3 +57,4 @@ export function getClientTranslation(key: string): string { return translate(locale, key); } + diff --git a/lib/i18n/settings.ts b/lib/i18n/settings.ts index 3ba0be4f..ff7c5cef 100644 --- a/lib/i18n/settings.ts +++ b/lib/i18n/settings.ts @@ -1178,3 +1178,428 @@ export const settingsEnUS = { language: 'Language', }, } as const; + +export const settingsRuRU = { + settings: { + title: 'Настройки', + description: 'Настройка параметров приложения', + language: 'Язык', + languageDesc: 'Выберите язык интерфейса', + theme: 'Тема', + themeDesc: 'Выберите тему оформления (Светлая/Тёмная/Системная)', + themeOptions: { + light: 'Светлая', + dark: 'Тёмная', + system: 'Системная', + }, + apiKey: 'API-ключ', + apiKeyDesc: 'Настройте ваш API-ключ', + selectModel: 'Выберите модель', + modelCount: 'моделей', + selectProvider: 'Выберите провайдера', + enterApiKey: 'Введите API-ключ', + enterBaseUrl: 'Введите Base URL', + enterCustomBaseUrl: 'Введите пользовательский Base URL', + confirmChangeProvider: 'Сменить провайдера?', + confirmChangeProviderText: 'Текущие настройки будут сброшены. Продолжить?', + save: 'Сохранить', + cancel: 'Отмена', + // Sections + providers: 'Провайдеры моделей', + audio: 'Аудио', + audioSettings: 'Настройки аудио', + audioDescription: 'Настройка синтеза и распознавания речи', + advanced: 'Дополнительно', + // AI Provider settings + defaultModelGroup: 'Основные модели', + apiKeyPlaceholder: 'Введите API-ключ', + apiKeyServerConfigured: 'Серверный ключ настроен, можно ввести свой', + baseUrlPlaceholder: 'Введите Base URL', + baseUrlOptional: 'Base URL (необязательно)', + baseUrlDefault: 'По умолчанию', + customModelPlaceholder: 'Или введите произвольный ID модели', + addCustomModel: 'Добавить модель', + addedCustomModel: 'Модель добавлена', + removedCustomModel: 'Модель удалена', + // Audio settings + ttsProvider: 'Провайдер TTS', + ttsModel: 'Модель TTS', + ttsVoice: 'Голос', + ttsSpeed: 'Скорость', + ttsBaseUrl: 'Base URL', + ttsApiKey: 'API-ключ', + asrProvider: 'Провайдер ASR', + asrLanguage: 'Язык распознавания', + asrBaseUrl: 'Base URL', + asrApiKey: 'API-ключ', + browserNativeNote: 'Встроенный ASR браузера не требует настройки и полностью бесплатен', + // Audio provider names + providerOpenAITTS: 'OpenAI TTS (gpt-4o-mini-tts)', + providerAzureTTS: 'Azure TTS', + providerGLMTTS: 'GLM TTS', + providerQwenTTS: 'Qwen TTS (Alibaba Cloud Bailian)', + providerElevenLabsTTS: 'ElevenLabs TTS', + providerBrowserNativeTTS: 'Встроенный TTS браузера', + providerOpenAIWhisper: 'OpenAI ASR (gpt-4o-mini-transcribe)', + providerBrowserNative: 'Встроенный ASR браузера', + providerQwenASR: 'Qwen ASR (Alibaba Cloud Bailian)', + providerUnpdf: 'unpdf (встроенный)', + providerMinerU: 'MinerU', + browserNativeTTSNote: + 'Встроенный TTS браузера не требует настройки и полностью бесплатен, использует системные голоса', + testTTS: 'Тест TTS', + testASR: 'Тест ASR', + testSuccess: 'Тест пройден', + testFailed: 'Тест не пройден', + ttsTestText: 'Текст для TTS-теста', + ttsTestSuccess: 'TTS-тест пройден, аудио воспроизведено', + ttsTestFailed: 'TTS-тест не пройден', + asrTestSuccess: 'Распознавание речи успешно', + asrTestFailed: 'Распознавание речи не удалось', + asrResult: 'Результат распознавания', + asrNotSupported: 'Браузер не поддерживает Speech Recognition API', + browserTTSNotSupported: 'Браузер не поддерживает Speech Synthesis API', + browserTTSNoVoices: 'В текущем браузере нет доступных голосов TTS', + microphoneAccessDenied: 'Доступ к микрофону запрещён', + microphoneAccessFailed: 'Не удалось получить доступ к микрофону', + asrResultPlaceholder: 'Результат распознавания появится после записи', + useThisProvider: 'Использовать этого провайдера', + fetchVoices: 'Загрузить список голосов', + fetchingVoices: 'Загрузка...', + voicesFetched: 'Голоса загружены', + fetchVoicesFailed: 'Не удалось загрузить голоса', + voiceApiKeyRequired: 'Требуется API-ключ', + voiceBaseUrlRequired: 'Требуется Base URL', + ttsTestTextPlaceholder: 'Введите текст для озвучивания', + ttsTestTextDefault: 'Привет, это тестовая речь.', + startRecording: 'Начать запись', + stopRecording: 'Остановить запись', + recording: 'Запись...', + transcribing: 'Транскрибирование...', + transcriptionResult: 'Результат транскрибирования', + noTranscriptionResult: 'Нет результата транскрибирования', + defaultValue: 'По умолчанию', + // TTS Voice descriptions (OpenAI) + voiceMarin: 'Рекомендуется — лучшее качество', + voiceCedar: 'Рекомендуется — лучшее качество', + voiceAlloy: 'Нейтральный, сбалансированный', + voiceAsh: 'Спокойный, профессиональный', + voiceBallad: 'Элегантный, лиричный', + voiceCoral: 'Тёплый, дружелюбный', + voiceEcho: 'Мужской, чёткий', + voiceFable: 'Повествовательный, яркий', + voiceNova: 'Женский, яркий', + voiceOnyx: 'Мужской, глубокий', + voiceSage: 'Мудрый, уравновешенный', + voiceShimmer: 'Женский, мягкий', + voiceVerse: 'Естественный, плавный', + // TTS Voice descriptions (GLM) + glmVoiceTongtong: 'Голос по умолчанию', + glmVoiceChuichui: 'Голос Chuichui', + glmVoiceXiaochen: 'Голос Xiaochen', + glmVoiceJam: 'Голос Jam', + glmVoiceKazi: 'Голос Kazi', + glmVoiceDouji: 'Голос Douji', + glmVoiceLuodo: 'Голос Luodo', + // TTS Voice descriptions (Qwen) + qwenVoiceCherry: 'Солнечный, тёплый и естественный', + qwenVoiceSerena: 'Нежный и мягкий', + qwenVoiceEthan: 'Энергичный и живой', + qwenVoiceChelsie: 'Аниме-виртуальная подруга', + qwenVoiceMomo: 'Игривый и весёлый', + qwenVoiceVivian: 'Милый и дерзкий', + qwenVoiceMoon: 'Крутой и красивый', + qwenVoiceMaia: 'Интеллектуальный и нежный', + qwenVoiceKai: 'Спа для ваших ушей', + qwenVoiceNofish: 'Дизайнер с особым произношением', + qwenVoiceBella: 'Маленькая лоли', + qwenVoiceJennifer: 'Кинематографический американский женский голос', + qwenVoiceRyan: 'Быстрый, драматичный', + qwenVoiceKaterina: 'Зрелая леди с запоминающимся ритмом', + qwenVoiceAiden: 'Американский парень', + qwenVoiceEldricSage: 'Спокойный и мудрый старейшина', + qwenVoiceMia: 'Нежная как весенняя вода', + qwenVoiceMochi: 'Умный малыш с детской невинностью', + qwenVoiceBellona: 'Громкий голос, чёткое произношение', + qwenVoiceVincent: 'Уникальный хриплый голос', + qwenVoiceBunny: 'Супер-милая лоли', + qwenVoiceNeil: 'Профессиональный диктор', + qwenVoiceElias: 'Профессиональный инструктор', + qwenVoiceArthur: 'Простой голос, пропитанный годами', + qwenVoiceNini: 'Мягкий и липкий голос', + qwenVoiceEbona: 'Её шёпот как ржавый ключ', + qwenVoiceSeren: 'Нежный и успокаивающий голос', + qwenVoicePip: 'Озорной, но полный детской невинности', + qwenVoiceStella: 'Сладкий девичий голос', + qwenVoiceBodega: 'Энтузиастичный испанский дядя', + qwenVoiceSonrisa: 'Энтузиастичная латиноамериканка', + qwenVoiceAlek: 'Холод, но теплота под шерстяным пальто', + qwenVoiceDolce: 'Ленивый итальянский дядя', + qwenVoiceSohee: 'Нежная, весёлая кореянка', + qwenVoiceOnoAnna: 'Шаловливая подруга детства', + qwenVoiceLenn: 'Рациональный немецкий юноша', + qwenVoiceEmilien: 'Романтический французский брат', + qwenVoiceAndre: 'Магнетический, естественный мужской голос', + qwenVoiceRadioGol: 'Футбольный поэт Rádio Gol!', + qwenVoiceJada: 'Живая шанхайская леди', + qwenVoiceDylan: 'Пекинский парень', + qwenVoiceLi: 'Терпеливый инструктор йоги', + qwenVoiceMarcus: 'Твёрдое сердце — вкус старого Шаньси', + qwenVoiceRoy: 'Юморной тайваньский парень', + qwenVoicePeter: 'Тяньцзиньский комик', + qwenVoiceSunny: 'Милая сычуаньская девушка', + qwenVoiceEric: 'Чэндуский джентльмен', + qwenVoiceRocky: 'Юморной гонконгский парень', + qwenVoiceKiki: 'Милая гонконгская девушка', + // ASR Language names (native forms) + lang_auto: 'Авто-определение', + lang_zh: '中文', + lang_yue: '粤語', + lang_en: 'English', + lang_ja: '日本語', + lang_ko: '한국어', + lang_es: 'Español', + lang_fr: 'Français', + lang_de: 'Deutsch', + lang_ru: 'Русский', + lang_ar: 'العربية', + lang_pt: 'Português', + lang_it: 'Italiano', + lang_af: 'Afrikaans', + lang_hy: 'Հայերեն', + lang_az: 'Azərbaycan', + lang_be: 'Беларуская', + lang_bs: 'Bosanski', + lang_bg: 'Български', + lang_ca: 'Català', + lang_hr: 'Hrvatski', + lang_cs: 'Čeština', + lang_da: 'Dansk', + lang_nl: 'Nederlands', + lang_et: 'Eesti', + lang_fi: 'Suomi', + lang_gl: 'Galego', + lang_el: 'Ελληνικά', + lang_he: 'עברית', + lang_hi: 'हिन्दी', + lang_hu: 'Magyar', + lang_is: 'Íslenska', + lang_id: 'Bahasa Indonesia', + lang_kn: 'ಕನ್ನಡ', + lang_kk: 'Қазақша', + lang_lv: 'Latviešu', + lang_lt: 'Lietuvių', + lang_mk: 'Македонски', + lang_ms: 'Bahasa Melayu', + lang_mr: 'मराठी', + lang_mi: 'Te Reo Māori', + lang_ne: 'नेपाली', + lang_no: 'Norsk', + lang_fa: 'فارسی', + lang_pl: 'Polski', + lang_ro: 'Română', + lang_sr: 'Српски', + lang_sk: 'Slovenčina', + lang_sl: 'Slovenščina', + lang_sw: 'Kiswahili', + lang_sv: 'Svenska', + lang_tl: 'Tagalog', + lang_fil: 'Filipino', + lang_ta: 'தமிழ்', + lang_th: 'ไทย', + lang_tr: 'Türkçe', + lang_uk: 'Українська', + lang_ur: 'اردو', + lang_vi: 'Tiếng Việt', + lang_cy: 'Cymraeg', + // BCP-47 format language codes + 'lang_zh-CN': '中文(简体,中国)', + 'lang_zh-TW': '中文(繁體,台灣)', + 'lang_zh-HK': '粵語(香港)', + 'lang_yue-Hant-HK': '粵語(繁體)', + 'lang_en-US': 'English (United States)', + 'lang_en-GB': 'English (United Kingdom)', + 'lang_en-AU': 'English (Australia)', + 'lang_en-CA': 'English (Canada)', + 'lang_en-IN': 'English (India)', + 'lang_en-NZ': 'English (New Zealand)', + 'lang_en-ZA': 'English (South Africa)', + 'lang_ja-JP': '日本語(日本)', + 'lang_ko-KR': '한국어(대한민국)', + 'lang_de-DE': 'Deutsch (Deutschland)', + 'lang_fr-FR': 'Français (France)', + 'lang_es-ES': 'Español (España)', + 'lang_es-MX': 'Español (México)', + 'lang_es-AR': 'Español (Argentina)', + 'lang_es-CO': 'Español (Colombia)', + 'lang_it-IT': 'Italiano (Italia)', + 'lang_pt-BR': 'Português (Brasil)', + 'lang_pt-PT': 'Português (Portugal)', + 'lang_ru-RU': 'Русский (Россия)', + 'lang_nl-NL': 'Nederlands (Nederland)', + 'lang_pl-PL': 'Polski (Polska)', + 'lang_cs-CZ': 'Čeština (Česko)', + 'lang_da-DK': 'Dansk (Danmark)', + 'lang_fi-FI': 'Suomi (Suomi)', + 'lang_sv-SE': 'Svenska (Sverige)', + 'lang_no-NO': 'Norsk (Norge)', + 'lang_tr-TR': 'Türkçe (Türkiye)', + 'lang_el-GR': 'Ελληνικά (Ελλάδα)', + 'lang_hu-HU': 'Magyar (Magyarország)', + 'lang_ro-RO': 'Română (România)', + 'lang_sk-SK': 'Slovenčina (Slovensko)', + 'lang_bg-BG': 'Български (България)', + 'lang_hr-HR': 'Hrvatski (Hrvatska)', + 'lang_ca-ES': 'Català (Espanya)', + 'lang_ar-SA': 'العربية (السعودية)', + 'lang_ar-EG': 'العربية (مصر)', + 'lang_he-IL': 'עברית (ישראל)', + 'lang_hi-IN': 'हिन्दी (भारत)', + 'lang_th-TH': 'ไทย (ประเทศไทย)', + 'lang_vi-VN': 'Tiếng Việt (Việt Nam)', + 'lang_id-ID': 'Bahasa Indonesia (Indonesia)', + 'lang_ms-MY': 'Bahasa Melayu (Malaysia)', + 'lang_fil-PH': 'Filipino (Pilipinas)', + 'lang_af-ZA': 'Afrikaans (Suid-Afrika)', + 'lang_uk-UA': 'Українська (Україна)', + // PDF settings + pdfSettings: 'Обработка PDF', + pdfParsingSettings: 'Настройки обработки PDF', + pdfDescription: + 'Выберите движок для обработки PDF с поддержкой извлечения текста, изображений и таблиц', + pdfProvider: 'PDF-парсер', + pdfFeatures: 'Поддерживаемые функции', + pdfApiKey: 'API-ключ', + pdfBaseUrl: 'Base URL', + mineruDescription: + 'MinerU — коммерческий сервис обработки PDF с поддержкой извлечения таблиц, распознавания формул и анализа макета.', + mineruApiKeyRequired: 'Перед использованием необходимо получить API-ключ на сайте MinerU.', + mineruWarning: 'Внимание', + mineruCostWarning: + 'MinerU — коммерческий сервис, использование может быть платным. Проверьте цены на сайте MinerU.', + enterMinerUApiKey: 'Введите MinerU API-ключ', + mineruLocalDescription: + 'MinerU поддерживает локальное развёртывание с расширенной обработкой PDF (таблицы, формулы, анализ макета). Требуется предварительное развёртывание сервиса MinerU.', + mineruServerAddress: 'Адрес локального сервера MinerU (напр., http://localhost:8080)', + mineruApiKeyOptional: 'Требуется только если на сервере включена аутентификация', + optionalApiKey: 'Необязательный API-ключ', + featureText: 'Извлечение текста', + featureImages: 'Извлечение изображений', + featureTables: 'Извлечение таблиц', + featureFormulas: 'Распознавание формул', + featureLayoutAnalysis: 'Анализ макета', + featureMetadata: 'Метаданные', + // Image Generation settings + enableImageGeneration: 'Включить AI-генерацию изображений', + imageGenerationDisabledHint: + 'При включении изображения будут автоматически генерироваться во время создания курса', + imageSettings: 'Генерация изображений', + imageSection: 'Текст в изображение', + imageProvider: 'Провайдер генерации изображений', + imageModel: 'Модель генерации изображений', + providerSeedream: 'Seedream (ByteDance)', + providerQwenImage: 'Qwen Image (Alibaba)', + providerNanoBanana: 'Nano Banana (Gemini)', + providerGrokImage: 'Grok Image (xAI)', + testImageGeneration: 'Тест генерации изображений', + testImageConnectivity: 'Тест подключения', + imageConnectivitySuccess: 'Подключение к сервису изображений успешно', + imageConnectivityFailed: 'Подключение к сервису изображений не удалось', + imageTestSuccess: 'Тест генерации изображений пройден', + imageTestFailed: 'Тест генерации изображений не пройден', + imageTestPromptPlaceholder: 'Введите описание изображения для теста', + imageTestPromptDefault: 'Милый котёнок сидит на письменном столе', + imageGenerating: 'Генерация изображения...', + imageGenerationFailed: 'Ошибка генерации изображения', + // Video Generation settings + enableVideoGeneration: 'Включить AI-генерацию видео', + videoGenerationDisabledHint: + 'При включении видео будут автоматически генерироваться во время создания курса', + videoSettings: 'Генерация видео', + videoSection: 'Текст в видео', + videoProvider: 'Провайдер генерации видео', + videoModel: 'Модель генерации видео', + providerSeedance: 'Seedance (ByteDance)', + providerKling: 'Kling (Kuaishou)', + providerVeo: 'Veo (Google)', + providerSora: 'Sora (OpenAI)', + providerGrokVideo: 'Grok Video (xAI)', + testVideoGeneration: 'Тест генерации видео', + testVideoConnectivity: 'Тест подключения', + videoConnectivitySuccess: 'Подключение к видеосервису успешно', + videoConnectivityFailed: 'Подключение к видеосервису не удалось', + testingConnection: 'Тестирование...', + videoTestSuccess: 'Тест генерации видео пройден', + videoTestFailed: 'Тест генерации видео не пройден', + videoTestPromptDefault: 'Милый котёнок гуляет по письменному столу', + videoGenerating: 'Генерация видео (ожид. 1-2 мин.)...', + videoGenerationWarning: 'Генерация видео обычно занимает 1-2 минуты, пожалуйста подождите', + mediaRetry: 'Повторить', + mediaContentSensitive: 'Извините, этот контент не прошёл проверку безопасности.', + mediaGenerationDisabled: 'Генерация отключена в настройках', + // Agent settings + singleAgent: 'Один агент', + multiAgent: 'Мульти-агент', + selectAgents: 'Выбрать агентов', + noVisionWarning: + 'Текущая модель не поддерживает зрение. Изображения по-прежнему можно размещать на слайдах, но модель не сможет понимать содержимое изображений для оптимизации', + // Server provider configuration + serverConfigured: 'Сервер', + serverConfiguredNotice: + 'Администратор настроил API-ключ для этого провайдера на сервере. Можете использовать его напрямую или ввести свой ключ.', + optionalOverride: 'Необязательно — оставьте пустым для серверной конфигурации', + // Access code + setupNeeded: 'Требуется настройка', + modelNotConfigured: 'Пожалуйста, выберите модель для начала работы', + // Clear cache + dangerZone: 'Опасная зона', + clearCache: 'Очистить локальный кэш', + clearCacheDescription: + 'Удалить все локально сохранённые данные, включая записи классов, историю чатов, аудиокэш и настройки приложения. Это действие нельзя отменить.', + clearCacheConfirmTitle: 'Вы уверены, что хотите очистить весь кэш?', + clearCacheConfirmDescription: + 'Это навсегда удалит все следующие данные без возможности восстановления:', + clearCacheConfirmItems: + 'Классы и сцены, История чатов, Аудио- и графический кэш, Настройки и предпочтения', + clearCacheConfirmInput: 'Введите «УДАЛИТЬ» для продолжения', + clearCacheConfirmPhrase: 'УДАЛИТЬ', + clearCacheButton: 'Удалить все данные навсегда', + clearCacheSuccess: 'Кэш очищен, страница скоро обновится', + clearCacheFailed: 'Не удалось очистить кэш, попробуйте снова', + // Web Search settings + webSearchSettings: 'Веб-поиск', + webSearchApiKey: 'Tavily API-ключ', + webSearchApiKeyPlaceholder: 'Введите ваш Tavily API-ключ', + webSearchApiKeyPlaceholderServer: 'Серверный ключ настроен, можно ввести свой', + webSearchApiKeyHint: 'Получите API-ключ на tavily.com для веб-поиска', + webSearchBaseUrl: 'Base URL', + webSearchServerConfigured: 'Серверный Tavily API-ключ настроен', + optional: 'Необязательно', + }, + profile: { + title: 'Профиль', + defaultNickname: 'Ученик', + chooseAvatar: 'Выбрать аватар', + uploadAvatar: 'Загрузить', + bioPlaceholder: 'Расскажите о себе — AI-учитель адаптирует уроки под ваш уровень...', + avatarHint: 'Ваш аватар будет отображаться в обсуждениях и чатах', + fileTooLarge: 'Изображение слишком большое — выберите файл до 5 МБ', + invalidFileType: 'Пожалуйста, выберите файл изображения', + editTooltip: 'Нажмите для редактирования профиля', + }, + media: { + imageCapability: 'Генерация изображений', + imageHint: 'Генерация изображений в слайдах', + videoCapability: 'Генерация видео', + videoHint: 'Генерация видео в слайдах', + ttsCapability: 'Синтез речи', + ttsHint: 'AI-учитель говорит вслух', + asrCapability: 'Распознавание речи', + asrHint: 'Голосовой ввод для обсуждения', + provider: 'Провайдер', + model: 'Модель', + voice: 'Голос', + speed: 'Скорость', + language: 'Язык', + }, +} as const; diff --git a/lib/i18n/stage.ts b/lib/i18n/stage.ts index 0a376ca1..02bee160 100644 --- a/lib/i18n/stage.ts +++ b/lib/i18n/stage.ts @@ -296,3 +296,153 @@ export const stageEnUS = { notReady: 'Available after generation completes', }, } as const; + +export const stageRuRU = { + stage: { + currentScene: 'Текущая сцена', + generating: 'Генерация...', + paused: 'Пауза', + generationFailed: 'Ошибка генерации', + confirmSwitchTitle: 'Переключить сцену', + confirmSwitchMessage: + 'В данный момент идёт обсуждение. Переключение сцены завершит текущую тему. Продолжить?', + generatingNextPage: 'Сцена генерируется, пожалуйста подождите...', + fullscreen: 'Полный экран', + exitFullscreen: 'Свернуть', + }, + whiteboard: { + title: 'Интерактивная доска', + open: 'Открыть доску', + clear: 'Очистить доску', + minimize: 'Свернуть доску', + ready: 'Доска готова', + readyHint: 'Элементы появятся здесь, когда AI их добавит', + clearSuccess: 'Доска очищена', + clearError: 'Ошибка очистки доски: ', + resetView: 'Сбросить вид', + restoreError: 'Ошибка восстановления доски: ', + history: 'История', + restore: 'Восстановить', + noHistory: 'Истории пока нет', + restored: 'Доска восстановлена', + elementCount: '{count} элементов', + }, + quiz: { + title: 'Тест', + subtitle: 'Проверьте свои знания', + questionsCount: 'вопросов', + totalPrefix: '', + pointsSuffix: 'б.', + startQuiz: 'Начать тест', + multipleChoiceHint: '(Множественный выбор — выберите все правильные ответы)', + inputPlaceholder: 'Введите ваш ответ...', + charCount: 'символов', + yourAnswer: 'Ваш ответ:', + notAnswered: 'Нет ответа', + aiComment: 'Отзыв AI', + singleChoice: 'Один ответ', + multipleChoice: 'Несколько', + shortAnswer: 'Развёрнутый ответ', + analysis: 'Анализ: ', + excellent: 'Отлично!', + keepGoing: 'Продолжайте!', + needsReview: 'Требует повторения', + correct: 'верно', + incorrect: 'неверно', + answering: 'В процессе', + submitAnswers: 'Отправить ответы', + aiGrading: 'AI проверяет...', + aiGradingWait: 'Пожалуйста подождите, анализируем ваши ответы', + quizReport: 'Результаты теста', + retry: 'Повторить', + }, + roundtable: { + teacher: 'УЧИТЕЛЬ', + you: 'ВЫ', + inputPlaceholder: 'Введите сообщение...', + listening: 'Слушаю...', + processing: 'Обработка...', + noSpeechDetected: 'Речь не обнаружена, попробуйте ещё раз', + discussionEnded: 'Обсуждение завершено', + qaEnded: 'Вопросы и ответы завершены', + thinking: 'Размышляет', + yourTurn: 'Ваша очередь', + stopDiscussion: 'Завершить обсуждение', + autoPlay: 'Автовоспр.', + autoPlayOff: 'Остановить', + speed: 'Скорость', + voiceInput: 'Голосовой ввод', + voiceInputDisabled: 'Голосовой ввод отключён', + textInput: 'Текстовый ввод', + stopRecording: 'Остановить запись', + startRecording: 'Начать запись', + }, + pbl: { + legacyFormat: 'Эта PBL-сцена использует устаревший формат. Пожалуйста, перегенерируйте курс.', + emptyProject: 'PBL-проект ещё не создан. Создайте его через генерацию курса.', + roleSelection: { + title: 'Выберите роль', + description: 'Выберите роль для совместной работы над проектом', + }, + workspace: { + restart: 'Перезапуск', + confirmRestart: 'Сбросить весь прогресс?', + confirm: 'Подтвердить', + cancel: 'Отмена', + }, + issueboard: { + title: 'Доска задач', + noIssues: 'Задач пока нет', + statusDone: 'Готово', + statusActive: 'Активна', + statusPending: 'В ожидании', + }, + chat: { + title: 'Обсуждение проекта', + currentIssue: 'Текущая задача', + mentionHint: 'Используйте @question для вопроса, @judge для проверки', + placeholder: 'Введите сообщение...', + send: 'Отправить', + welcomeMessage: + 'Привет! Я ваш помощник по задаче: "{title}"\n\nЧтобы помочь вам, я подготовил несколько вопросов:\n\n{questions}\n\nОбращайтесь через @question, если нужна помощь!', + issueCompleteMessage: 'Задача "{completed}" выполнена! Переход к следующей: "{next}"', + allCompleteMessage: '🎉 Все задачи выполнены! Отличная работа над проектом!', + }, + guide: { + howItWorks: 'Как это работает', + help: 'Помощь', + title: 'Помощь', + step1: { + title: 'Шаг 1: Выберите роль', + desc: 'После генерации проекта выберите роль из списка (не-системные роли отмечены 🟢)', + }, + step2: { + title: 'Шаг 2: Выполняйте задачи', + desc: 'Каждая задача — это учебное задание:', + s1: { + title: 'Просмотрите задачу', + desc: 'Изучите заголовок, описание и исполнителя задачи', + }, + s2: { + title: 'Получите подсказки', + example: '@question С чего начать?\n@question Как реализовать эту функцию?', + desc: 'Question Agent даёт наводящие вопросы и подсказки (не прямые ответы)', + }, + s3: { + title: 'Сдайте работу', + example: '@judge Готово, проверьте мои заметки', + desc: 'Judge Agent оценивает вашу работу и даёт обратную связь:', + complete: 'Автоматический переход к следующей задаче', + revision: 'Доработайте по замечаниям', + }, + }, + step3: { + title: 'Шаг 3: Завершите проект', + desc: 'Когда все задачи выполнены, система показывает «🎉 Проект завершён!»', + }, + }, + }, + share: { + notReady: 'Доступно после завершения генерации', + }, +} as const; diff --git a/lib/i18n/types.ts b/lib/i18n/types.ts index 6173b0be..fab0ee25 100644 --- a/lib/i18n/types.ts +++ b/lib/i18n/types.ts @@ -1,3 +1,3 @@ -export type Locale = 'zh-CN' | 'en-US'; +export type Locale = 'zh-CN' | 'en-US' | 'ru-RU'; export const defaultLocale: Locale = 'zh-CN'; diff --git a/lib/orchestration/prompt-builder.ts b/lib/orchestration/prompt-builder.ts index b73f310f..2aaa82cc 100644 --- a/lib/orchestration/prompt-builder.ts +++ b/lib/orchestration/prompt-builder.ts @@ -166,7 +166,7 @@ Personalize your teaching based on their background when relevant. Address them // Build language constraint from stage language const courseLanguage = storeState.stage?.language; const languageConstraint = courseLanguage - ? `\n# Language (CRITICAL)\nYou MUST speak in ${courseLanguage === 'zh-CN' ? 'Chinese (Simplified)' : courseLanguage === 'en-US' ? 'English' : courseLanguage}. ALL text content in your response MUST be in this language.\n` + ? `\n# Language (CRITICAL)\nYou MUST speak in ${courseLanguage === 'zh-CN' ? 'Chinese (Simplified)' : courseLanguage === 'en-US' ? 'English' : courseLanguage === 'ru-RU' ? 'Russian' : courseLanguage}. ALL text content in your response MUST be in this language.\n` : ''; return `# Role diff --git a/lib/pbl/pbl-system-prompt.ts b/lib/pbl/pbl-system-prompt.ts index 72cdc10b..f9f5401e 100644 --- a/lib/pbl/pbl-system-prompt.ts +++ b/lib/pbl/pbl-system-prompt.ts @@ -20,6 +20,10 @@ export function buildPBLSystemPrompt(config: PBLSystemPromptConfig): string { return buildPBLSystemPromptZH(config); } + if (language === 'ru-RU') { + return buildPBLSystemPromptRU(config); + } + return `You are a Teaching Assistant (TA) on a Project-Based Learning platform. You are fully responsible for designing group projects for students based on the course information provided by the teacher. ## Your Responsibility @@ -148,3 +152,68 @@ function buildPBLSystemPromptZH(config: PBLSystemPromptConfig): string { 你的初始模式是 **project_info**。`; } + +function buildPBLSystemPromptRU(config: PBLSystemPromptConfig): string { + const { projectTopic, projectDescription, targetSkills, issueCount = 3 } = config; + + return `Вы — Teaching Assistant (TA) на платформе проектного обучения (PBL). Вы полностью отвечаете за разработку групповых проектов для студентов на основе информации от преподавателя. + +## Ваша ответственность + +Разработайте полный проект: +1. Создайте ясное, привлекательное название проекта +2. Напишите краткое описание проекта (2-4 предложения), охватывающее: + - Суть проекта + - Ключевые цели обучения + - Что студенты создадут + +Информация от преподавателя: +- **Тема проекта**: ${projectTopic} +- **Описание проекта**: ${projectDescription} +- **Целевые навыки**: ${targetSkills.join(', ')} +- **Рекомендуемое количество задач**: ${issueCount} + +На основе этой информации самостоятельно разработайте проект. + +## Система режимов + +Вы можете переключаться между режимами, каждый предоставляет разные инструменты: +- **project_info**: Настройка базовой информации о проекте (название, описание) +- **agent**: Определение ролей проекта +- **issueboard**: Настройка рабочего процесса и задач +- **idle**: Специальный режим, означающий завершение настройки + +Вы начинаете в режиме **project_info**. Используйте инструмент \`set_mode\` для переключения. + +## Рабочий процесс + +1. В режиме **project_info**: Настройте название и описание проекта +2. Переключитесь на **agent**: Определите 2-4 роли для студентов (НЕ создавайте управленческие роли) +3. Переключитесь на **issueboard**: Создайте ${issueCount} последовательных задач +4. После завершения переключитесь на **idle** + +## Руководство по ролям + +- Создайте 2-4 **рабочие** роли для студентов +- Каждая роль должна иметь чёткую ответственность и уникальный системный промпт +- Роли должны дополнять друг друга (напр., «Аналитик данных», «Фронтенд-разработчик») +- НЕ создавайте системных агентов (Question/Judge Agent создаются автоматически) + +## Руководство по задачам + +- Создайте ровно ${issueCount} задач, формирующих логическую последовательность +- Каждая задача должна быть выполнима одним человеком +- Задачи должны быть взаимосвязаны (предыдущие создают основу для следующих) +- Каждая задача требует: заголовок, описание, ответственный (имя роли) и участники + +## Автоматическое создание агентов задач + +При создании задач: +- Каждая задача автоматически получает Question Agent и Judge Agent +- Вам НЕ нужно создавать их вручную +- Сосредоточьтесь на содержательных задачах + +**ВАЖНО**: После настройки информации о проекте, ролей и задач переключитесь на режим **idle**. + +Ваш начальный режим — **project_info**.`; +} diff --git a/lib/server/classroom-generation.ts b/lib/server/classroom-generation.ts index eda67b4c..c02ab7fd 100644 --- a/lib/server/classroom-generation.ts +++ b/lib/server/classroom-generation.ts @@ -96,8 +96,10 @@ function createInMemoryStore(stage: Stage): StageStore { }; } -function normalizeLanguage(language?: string): 'zh-CN' | 'en-US' { - return language === 'en-US' ? 'en-US' : 'zh-CN'; +function normalizeLanguage(language?: string): 'zh-CN' | 'en-US' | 'ru-RU' { + if (language === 'en-US') return 'en-US'; + if (language === 'ru-RU') return 'ru-RU'; + return 'zh-CN'; } function stripCodeFences(text: string): string { diff --git a/lib/types/generation.ts b/lib/types/generation.ts index c1e6eb7a..f12f067b 100644 --- a/lib/types/generation.ts +++ b/lib/types/generation.ts @@ -64,7 +64,7 @@ export interface UploadedDocument { */ export interface UserRequirements { requirement: string; // Single free-form text for all user input - language: 'zh-CN' | 'en-US'; // Course language - critical for generation + language: 'zh-CN' | 'en-US' | 'ru-RU'; // Course language - critical for generation userNickname?: string; // Student nickname for personalization userBio?: string; // Student background for personalization webSearch?: boolean; // Enable web search for richer context @@ -100,7 +100,7 @@ export interface SceneOutline { teachingObjective?: string; estimatedDuration?: number; // seconds order: number; - language?: 'zh-CN' | 'en-US'; // Generation language (inherited from requirements) + language?: 'zh-CN' | 'en-US' | 'ru-RU'; // Generation language (inherited from requirements) // Suggested image IDs (from PDF-extracted images) suggestedImageIds?: string[]; // e.g., ["img_1", "img_3"] // AI-generated media requests (when PDF images are insufficient) @@ -124,7 +124,7 @@ export interface SceneOutline { projectDescription: string; targetSkills: string[]; issueCount?: number; - language: 'zh-CN' | 'en-US'; + language: 'zh-CN' | 'en-US' | 'ru-RU'; }; } diff --git a/packages/pptxgenjs/rollup.config-1774379659781.cjs b/packages/pptxgenjs/rollup.config-1774379659781.cjs new file mode 100644 index 00000000..0fbbd412 --- /dev/null +++ b/packages/pptxgenjs/rollup.config-1774379659781.cjs @@ -0,0 +1,36 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var pkg = require('./package.json'); +var resolve = require('@rollup/plugin-node-resolve'); +var commonjs = require('@rollup/plugin-commonjs'); +var typescript = require('rollup-plugin-typescript2'); + +const nodeBuiltinsRE = /^node:.*/; /* Regex that matches all Node built-in specifiers */ + +var rollup_config = { + input: "src/pptxgen.ts", + output: [ + { + file: "./dist/pptxgen.js", + format: "iife", + name: "PptxGenJS", + globals: { jszip: "JSZip" }, + }, + { file: "./dist/pptxgen.cjs.js", format: "cjs", exports: "default" }, + { file: "./dist/pptxgen.es.js", format: "es" }, + ], + external: [ + nodeBuiltinsRE, + ...Object.keys(pkg.dependencies || {}), + ...Object.keys(pkg.peerDependencies || {}), + ], + plugins: [ + resolve({ preferBuiltins: true }), + commonjs(), + typescript({ typescript: require("typescript") }), + ] +}; + +exports.default = rollup_config;