mirror of
https://github.com/stackblitz/bolt.new
synced 2025-03-12 14:58:30 +00:00
Export chat from sidebar
This commit is contained in:
parent
95776af641
commit
a6060b81a5
@ -1,6 +1,7 @@
|
|||||||
import * as Dialog from '@radix-ui/react-dialog';
|
import * as Dialog from '@radix-ui/react-dialog';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { type ChatHistoryItem } from '~/lib/persistence';
|
import { type ChatHistoryItem } from '~/lib/persistence';
|
||||||
|
import { exportChat } from '~/utils/chatExport';
|
||||||
|
|
||||||
interface HistoryItemProps {
|
interface HistoryItemProps {
|
||||||
item: ChatHistoryItem;
|
item: ChatHistoryItem;
|
||||||
@ -43,9 +44,17 @@ export function HistoryItem({ item, onDelete, onDuplicate }: HistoryItemProps) {
|
|||||||
>
|
>
|
||||||
<a href={`/chat/${item.urlId}`} className="flex w-full relative truncate block">
|
<a href={`/chat/${item.urlId}`} className="flex w-full relative truncate block">
|
||||||
{item.description}
|
{item.description}
|
||||||
<div className="absolute right-0 z-1 top-0 bottom-0 bg-gradient-to-l from-bolt-elements-background-depth-2 group-hover:from-bolt-elements-background-depth-3 to-transparent w-10 flex justify-end group-hover:w-15 group-hover:from-45%">
|
<div className="absolute right-0 z-1 top-0 bottom-0 bg-gradient-to-l from-bolt-elements-background-depth-2 group-hover:from-bolt-elements-background-depth-3 box-content pl-3 to-transparent w-10 flex justify-end group-hover:w-15 group-hover:from-99%">
|
||||||
{hovering && (
|
{hovering && (
|
||||||
<div className="flex items-center p-1 text-bolt-elements-textSecondary">
|
<div className="flex items-center p-1 text-bolt-elements-textSecondary">
|
||||||
|
<button
|
||||||
|
className="i-ph:download-simple scale-110 mr-2"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
exportChat(item.messages, item.description);
|
||||||
|
}}
|
||||||
|
title="Export chat"
|
||||||
|
/>
|
||||||
{onDuplicate && (
|
{onDuplicate && (
|
||||||
<button
|
<button
|
||||||
className="i-ph:copy scale-110 mr-2"
|
className="i-ph:copy scale-110 mr-2"
|
||||||
|
46
app/utils/chatExport.ts
Normal file
46
app/utils/chatExport.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import type { Message } from 'ai';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
export interface ChatExportData {
|
||||||
|
messages: Message[];
|
||||||
|
description?: string;
|
||||||
|
exportDate: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const exportChat = (messages: Message[], description?: string) => {
|
||||||
|
const chatData: ChatExportData = {
|
||||||
|
messages,
|
||||||
|
description,
|
||||||
|
exportDate: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const blob = new Blob([JSON.stringify(chatData, null, 2)], { type: 'application/json' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = `chat-${new Date().toISOString()}.json`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importChat = async (file: File): Promise<ChatExportData> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
try {
|
||||||
|
const content = e.target?.result as string;
|
||||||
|
const data = JSON.parse(content);
|
||||||
|
if (!Array.isArray(data.messages)) {
|
||||||
|
throw new Error('Invalid chat file format');
|
||||||
|
}
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(new Error('Failed to parse chat file'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.onerror = () => reject(new Error('Failed to read chat file'));
|
||||||
|
reader.readAsText(file);
|
||||||
|
});
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user