Skip to content

Commit

Permalink
feat: support export to tavern and ooba format
Browse files Browse the repository at this point in the history
  • Loading branch information
pionxzh committed Feb 24, 2024
1 parent 4ab9b19 commit a28f9fd
Show file tree
Hide file tree
Showing 17 changed files with 146 additions and 139 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Expand Up @@ -35,6 +35,7 @@
"headlessui",
"mdast",
"micromark",
"ooba",
"preact",
"tabler",
"unist"
Expand Down
2 changes: 1 addition & 1 deletion src/constants.ts
Expand Up @@ -8,7 +8,7 @@ export const apiUrl = API_MAPPING[baseUrl]

export const KEY_LANGUAGE = 'exporter:language'
export const KEY_FILENAME_FORMAT = 'exporter:filename_format'
export const KEY_OFFICIAL_JSON_FORMAT = 'exporter:official_json_format'
// export const KEY_OFFICIAL_JSON_FORMAT = 'exporter:official_json_format'
export const KEY_TIMESTAMP_ENABLED = 'exporter:enable_timestamp'
export const KEY_TIMESTAMP_24H = 'exporter:timestamp_24h'
export const KEY_TIMESTAMP_MARKDOWN = 'exporter:timestamp_markdown'
Expand Down
39 changes: 37 additions & 2 deletions src/exporter/json.ts
Expand Up @@ -2,10 +2,11 @@ import JSZip from 'jszip'
import { fetchConversation, getCurrentChatId, processConversation } from '../api'
import i18n from '../i18n'
import { checkIfConversationStarted } from '../page'
import { convertToOoba, convertToTavern } from '../utils/conversion'
import { downloadFile, getFileNameWithFormat } from '../utils/download'
import type { ApiConversationWithId } from '../api'

export async function exportToJson(fileNameFormat: string, options: { officialFormat: boolean }) {
export async function exportToJson(fileNameFormat: string) {
if (!checkIfConversationStarted()) {
alert(i18n.t('Please start a conversation first'))
return false
Expand All @@ -19,7 +20,41 @@ export async function exportToJson(fileNameFormat: string, options: { officialFo
/**
* The official format is just an array of the API response.
*/
const content = conversationToJson(options.officialFormat ? [rawConversation] : rawConversation)
const content = conversationToJson([rawConversation])
downloadFile(fileName, 'application/json', content)

return true
}

export async function exportToTavern(fileNameFormat: string) {
if (!checkIfConversationStarted()) {
alert(i18n.t('Please start a conversation first'))
return false
}

const chatId = await getCurrentChatId()
const rawConversation = await fetchConversation(chatId, false)
const conversation = processConversation(rawConversation)

const fileName = getFileNameWithFormat(`${fileNameFormat}.tavern`, 'jsonl', { title: conversation.title, chatId })
const content = convertToTavern(conversation)
downloadFile(fileName, 'application/json-lines', content)

return true
}

export async function exportToOoba(fileNameFormat: string) {
if (!checkIfConversationStarted()) {
alert(i18n.t('Please start a conversation first'))
return false
}

const chatId = await getCurrentChatId()
const rawConversation = await fetchConversation(chatId, false)
const conversation = processConversation(rawConversation)

const fileName = getFileNameWithFormat(`${fileNameFormat}.ooba`, 'json', { title: conversation.title, chatId })
const content = convertToOoba(conversation)
downloadFile(fileName, 'application/json', content)

return true
Expand Down
2 changes: 1 addition & 1 deletion src/locales/en.json
Expand Up @@ -31,9 +31,9 @@
"Enable on Markdown": "Enable on Markdown files",
"Use 24-hour format": "Use 24-hour format (eg. 23:59)",
"Export Format": "Export Format",
"Export JSON Format Description": "Export JSON in OpenAI Official Format",
"Export Metadata": "Export Metadata",
"Export Metadata Description": "Add metadata to exported Markdown and HTML files.",
"OpenAI Official Format": "OpenAI Official Format",
"Conversation Archive Alert": "Are you sure you want to archive all selected conversations?",
"Conversation Archived Message": "All selected conversations have been archived. Please refresh the page to see the changes.",
"Conversation Delete Alert": "Are you sure you want to delete all selected conversations?",
Expand Down
2 changes: 1 addition & 1 deletion src/locales/es.json
Expand Up @@ -31,9 +31,9 @@
"Enable on Markdown": "Habilitar en archivos Markdown",
"Use 24-hour format": "Usar formato de 24 horas (ej. 23:59)",
"Export Format": "Formato de Exportación",
"Export JSON Format Description": "Exportar JSON en el Formato Oficial de OpenAI",
"Export Metadata": "Exportar Metadatos",
"Export Metadata Description": "Añadir Metadatos a los archivos Markdown y HTML exportados.",
"OpenAI Official Format": "Formato Oficial de OpenAI",
"Conversation Archive Alert": "¿Estás seguro que quieres archivar todas las conversaciones seleccionadas?",
"Conversation Archived Message": "Todos las conversaciones seleccionadas se han archivado. Por favor refresca la página para ver los cambios.",
"Conversation Delete Alert": "¿Estás seguro que quieres borrar todas las conversaciones seleccionadas?",
Expand Down
2 changes: 1 addition & 1 deletion src/locales/id.json
Expand Up @@ -31,9 +31,9 @@
"Enable on Markdown": "Aktifkan pada file Markdown",
"Use 24-hour format": "Gunakan format 24 jam (contohnya: 23:59)",
"Export Format": "Format Ekspor",
"Export JSON Format Description": "Ekspor JSON dalam Format Resmi OpenAI",
"Export Metadata": "Ekspor Metada",
"Export Metadata Description": "Tambahkan metadata ke file Markdown dan HTML yang diekspor.",
"OpenAI Official Format": "Format Resmi OpenAI",
"Conversation Archive Alert": "Apakah Anda yakin ingin mengarsipkan semua percakapan yang dipilih?",
"Conversation Archived Message": "Semua percakapan yang dipilih telah diarsipkan. Harap segarkan halaman untuk melihat perubahan.",
"Conversation Delete Alert": "Apakah Anda yakin ingin menghapus semua percakapan yang dipilih?",
Expand Down
2 changes: 1 addition & 1 deletion src/locales/jp.json
Expand Up @@ -31,9 +31,9 @@
"Enable on Markdown": "Markdown ファイルで有効にする",
"Use 24-hour format": "24時間形式を使用する (例: 23:59)",
"Export Format": "エクスポートフォーマット",
"Export JSON Format Description": "OpenAI公式フォーマットでのJSONのエクスポート",
"Export Metadata": "メタデータをエクスポート",
"Export Metadata Description": "エクスポートされたMarkdownおよびHTMLファイルにメタデータを追加します。",
"OpenAI Official Format": "OpenAI公式フォーマット",
"Conversation Archive Alert": "選択したすべての会話をアーカイブしてもよろしいですか?",
"Conversation Archived Message": "選択したすべての会話がアーカイブされました。変更を表示するには、ページを更新してください。",
"Conversation Delete Alert": "選択したすべての会話を削除してもよろしいですか?",
Expand Down
2 changes: 1 addition & 1 deletion src/locales/tr.json
Expand Up @@ -31,9 +31,9 @@
"Enable on Markdown": "Markdown dosyalarında etkinleştir",
"Use 24-hour format": "24 saat biçimini kullan (örn. 23:59)",
"Export Format": "Dışa Aktarma Formatı",
"Export JSON Format Description": "OpenAI Resmi Formatında JSON Dışa Aktarma",
"Export Metadata": "Üst veriyi dışa aktar",
"Export Metadata Description": "Dışa aktarılan Markdown ve HTML dosyalarına üst veri ekle",
"OpenAI Official Format": "OpenAI Resmi Format",
"Conversation Archive Alert": "Seçilen tüm konuşmaları arşivlemek istediğinizden emin misiniz?",
"Conversation Archived Message": "Seçilen tüm konuşmalar arşivlendi. Değişiklikleri görmek için sayfayı yenileyin.",
"Conversation Delete Alert": "Seçilen tüm konuşmaları silmek istediğinizden emin misiniz?",
Expand Down
2 changes: 1 addition & 1 deletion src/locales/zh-Hans.json
Expand Up @@ -31,9 +31,9 @@
"Enable on Markdown": "在 Markdown 文件上启用",
"Use 24-hour format": "使用24小时制 (例如 23:59)",
"Export Format": "导出格式",
"Export JSON Format Description": "以 OpenAI 官方格式导出JSON",
"Export Metadata": "导出元数据",
"Export Metadata Description": "会添加至 Markdown 以及 HTML 导出。",
"OpenAI Official Format": "OpenAI 官方格式",
"Conversation Archive Alert": "确定要归档所有选取的对话?",
"Conversation Archived Message": "所有所选的对话已归档。请刷新页面。",
"Conversation Delete Alert": "确定要删除所有选取的对话?",
Expand Down
2 changes: 1 addition & 1 deletion src/locales/zh-Hant.json
Expand Up @@ -31,9 +31,9 @@
"Enable on Markdown": "在 Markdown 檔案上啟用",
"Use 24-hour format": "使用24小時制 (例如 23:59)",
"Export Format": "匯出格式",
"Export JSON Format Description": "以 OpenAI 官方格式匯出 JSON",
"Export Metadata": "匯出元資料",
"Export Metadata Description": "會添加至 Markdown 以及 HTML 匯出。",
"OpenAI Official Format": "OpenAI 官方格式",
"Conversation Archive Alert": "確定要封存所有選取的對話?",
"Conversation Archived Message": "所有選取的對話已封存。請重新整理頁面。",
"Conversation Delete Alert": "確定要刪除所有選取的對話?",
Expand Down
1 change: 1 addition & 0 deletions src/ui/Dialog.css
Expand Up @@ -21,6 +21,7 @@
overflow-y: auto;
padding: 16px 24px;
z-index: 1001;
outline: none;
animation: contentShow 150ms cubic-bezier(0.16, 1, 0.3, 1);
}

Expand Down
7 changes: 4 additions & 3 deletions src/ui/ExportDialog.tsx
Expand Up @@ -75,14 +75,15 @@ interface DialogContentProps {

const DialogContent: FC<DialogContentProps> = ({ format }) => {
const { t } = useTranslation()
const { enableMeta, exportMetaList, exportOfficialJsonFormat } = useSettingContext()
const { enableMeta, exportMetaList } = useSettingContext()
const metaList = useMemo(() => enableMeta ? exportMetaList : [], [enableMeta, exportMetaList])

const exportAllOptions = useMemo(() => [
{ label: 'Markdown', callback: exportAllToMarkdown },
{ label: 'JSON', callback: exportOfficialJsonFormat ? exportAllToOfficialJson : exportAllToJson },
{ label: 'HTML', callback: exportAllToHtml },
], [exportOfficialJsonFormat])
{ label: 'JSON', callback: exportAllToOfficialJson },
{ label: 'JSON (ZIP)', callback: exportAllToJson },
], [])

const fileInputRef = useRef<HTMLInputElement>(null)
const [exportSource, setExportSource] = useState<ExportSource>('API')
Expand Down
58 changes: 48 additions & 10 deletions src/ui/Menu.tsx
@@ -1,9 +1,10 @@
import * as Dialog from '@radix-ui/react-dialog'
import * as HoverCard from '@radix-ui/react-hover-card'
import { useCallback, useEffect, useMemo, useState } from 'preact/hooks'
import { useTranslation } from 'react-i18next'
import { exportToHtml } from '../exporter/html'
import { exportToPng } from '../exporter/image'
import { exportToJson } from '../exporter/json'
import { exportToJson, exportToOoba, exportToTavern } from '../exporter/json'
import { exportToMarkdown } from '../exporter/markdown'
import { exportToText } from '../exporter/text'
import { useWindowResize } from '../hooks/useWindowResize'
Expand All @@ -23,12 +24,12 @@ function MenuInner({ container }: { container: HTMLDivElement }) {
const disabled = getHistoryDisabled()

const [open, setOpen] = useState(false)
const [jsonOpen, setJsonOpen] = useState(false)
const [exportOpen, setExportOpen] = useState(false)
const [settingOpen, setSettingOpen] = useState(false)

const {
format,
exportOfficialJsonFormat,
enableTimestamp,
timeStamp24H,
enableMeta,
Expand All @@ -50,7 +51,13 @@ function MenuInner({ container }: { container: HTMLDivElement }) {
const onClickPng = useCallback(() => exportToPng(format), [format])
const onClickMarkdown = useCallback(() => exportToMarkdown(format, metaList), [format, metaList])
const onClickHtml = useCallback(() => exportToHtml(format, metaList), [format, metaList])
const onClickJSON = useCallback(() => exportToJson(format, { officialFormat: exportOfficialJsonFormat }), [format, exportOfficialJsonFormat])
const onClickJSON = useCallback(() => {
setJsonOpen(true)
return true
}, [])
const onClickOfficialJSON = useCallback(() => exportToJson(format), [format])
const onClickTavern = useCallback(() => exportToTavern(format), [format])
const onClickOoba = useCallback(() => exportToOoba(format), [format])

const width = useWindowResize(() => window.innerWidth)
const isMobile = width < 768
Expand Down Expand Up @@ -95,7 +102,7 @@ function MenuInner({ container }: { container: HTMLDivElement }) {
</HoverCard.Trigger>
<Portal
container={isMobile ? container : document.body}
forceMount={open || settingOpen || exportOpen}
forceMount={open || jsonOpen || settingOpen || exportOpen}
>
<HoverCard.Content
className={`
Expand Down Expand Up @@ -151,12 +158,43 @@ function MenuInner({ container }: { container: HTMLDivElement }) {
className="row-half"
onClick={onClickHtml}
/>
<MenuItem
text={t('JSON')}
icon={IconJSON}
className="row-half"
onClick={onClickJSON}
/>
<Dialog.Root
open={jsonOpen}
onOpenChange={setJsonOpen}
>
<Dialog.Trigger asChild>
<MenuItem
text={t('JSON')}
icon={IconJSON}
className="row-half"
onClick={onClickJSON}
/>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="DialogOverlay" />
<Dialog.Content className="DialogContent" style={{ width: '320px' }}>
<Dialog.Title className="DialogTitle">{t('JSON')}</Dialog.Title>
<MenuItem
text={t('OpenAI Official Format')}
icon={IconCopy}
className="row-full"
onClick={onClickOfficialJSON}
/>
<MenuItem
text="JSONL (TavernAI, SillyTavern)"
icon={IconCopy}
className="row-full"
onClick={onClickTavern}
/>
<MenuItem
text="Ooba (text-generation-webui)"
icon={IconCopy}
className="row-full"
onClick={onClickOoba}
/>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
<ExportDialog
format={format}
open={exportOpen}
Expand Down
9 changes: 0 additions & 9 deletions src/ui/SettingContext.tsx
Expand Up @@ -4,7 +4,6 @@ import {
KEY_FILENAME_FORMAT,
KEY_META_ENABLED,
KEY_META_LIST,
KEY_OFFICIAL_JSON_FORMAT,
KEY_TIMESTAMP_24H,
KEY_TIMESTAMP_ENABLED,
KEY_TIMESTAMP_HTML,
Expand All @@ -29,9 +28,6 @@ const SettingContext = createContext({
format: defaultFormat,
setFormat: (_: string) => {},

exportOfficialJsonFormat: false,
setExportOfficialJsonFormat: (_: boolean) => {},

enableTimestamp: false,
setEnableTimestamp: (_: boolean) => {},
timeStamp24H: false,
Expand All @@ -52,8 +48,6 @@ const SettingContext = createContext({
export const SettingProvider: FC = ({ children }) => {
const [format, setFormat] = useGMStorage(KEY_FILENAME_FORMAT, defaultFormat)

const [exportOfficialJsonFormat, setExportOfficialJsonFormat] = useGMStorage(KEY_OFFICIAL_JSON_FORMAT, false)

const [enableTimestamp, setEnableTimestamp] = useGMStorage(KEY_TIMESTAMP_ENABLED, false)
const [timeStamp24H, setTimeStamp24H] = useGMStorage(KEY_TIMESTAMP_24H, false)
const [enableTimestampHTML, setEnableTimestampHTML] = useGMStorage(KEY_TIMESTAMP_HTML, false)
Expand All @@ -75,9 +69,6 @@ export const SettingProvider: FC = ({ children }) => {
format,
setFormat,

exportOfficialJsonFormat,
setExportOfficialJsonFormat,

enableTimestamp,
setEnableTimestamp,
timeStamp24H,
Expand Down
17 changes: 0 additions & 17 deletions src/ui/SettingDialog.tsx
Expand Up @@ -33,7 +33,6 @@ export const SettingDialog: FC<SettingDialogProps> = ({
timeStamp24H, setTimeStamp24H,
enableTimestampHTML, setEnableTimestampHTML,
enableTimestampMarkdown, setEnableTimestampMarkdown,
exportOfficialJsonFormat, setExportOfficialJsonFormat,
enableMeta, setEnableMeta,
exportMetaList, setExportMetaList,
/* eslint-enable pionxzh/consistent-list-newline */
Expand Down Expand Up @@ -111,22 +110,6 @@ export const SettingDialog: FC<SettingDialogProps> = ({
</dd>
</div>
</div>
<div className="relative flex bg-white dark:bg-white/5 rounded p-4">
<div>
<dt className="text-md font-medium text-gray-800 dark:text-white">
{t('Export Format')}
</dt>
<dd className="text-sm text-gray-700 dark:text-gray-300">
<div className="mt-2">
<Toggle
label={t('Export JSON Format Description')}
checked={exportOfficialJsonFormat}
onCheckedUpdate={setExportOfficialJsonFormat}
/>
</div>
</dd>
</div>
</div>
<div className="relative flex bg-white dark:bg-white/5 rounded p-4">
<div>
<dt className="text-md font-medium text-gray-800 dark:text-white">
Expand Down

0 comments on commit a28f9fd

Please sign in to comment.