Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/icons/Export.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const Export: React.FC<React.SVGProps<SVGSVGElement>> = () => {
return (
<svg
fill="none"
height="100%"
viewBox="0 0 15 15"
width="100%"
xmlns="http://www.w3.org/2000/svg"
>
<path
clipRule="evenodd"
d="M3.5 5.00006C3.22386 5.00006 3 5.22392 3 5.50006L3 11.5001C3 11.7762 3.22386 12.0001 3.5 12.0001L11.5 12.0001C11.7761 12.0001 12 11.7762 12 11.5001L12 5.50006C12 5.22392 11.7761 5.00006 11.5 5.00006L10.25 5.00006C9.97386 5.00006 9.75 4.7762 9.75 4.50006C9.75 4.22392 9.97386 4.00006 10.25 4.00006L11.5 4.00006C12.3284 4.00006 13 4.67163 13 5.50006L13 11.5001C13 12.3285 12.3284 13.0001 11.5 13.0001L3.5 13.0001C2.67157 13.0001 2 12.3285 2 11.5001L2 5.50006C2 4.67163 2.67157 4.00006 3.5 4.00006L4.75 4.00006C5.02614 4.00006 5.25 4.22392 5.25 4.50006C5.25 4.7762 5.02614 5.00006 4.75 5.00006L3.5 5.00006ZM7 1.6364L5.5682 3.0682C5.39246 3.24393 5.10754 3.24393 4.9318 3.0682C4.75607 2.89246 4.75607 2.60754 4.9318 2.4318L7.1818 0.181802C7.26619 0.09741 7.38065 0.049999 7.5 0.049999C7.61935 0.049999 7.73381 0.09741 7.8182 0.181802L10.0682 2.4318C10.2439 2.60754 10.2439 2.89246 10.0682 3.0682C9.89246 3.24393 9.60754 3.24393 9.4318 3.0682L8 1.6364L8 8.5C8 8.77614 7.77614 9 7.5 9C7.22386 9 7 8.77614 7 8.5L7 1.6364Z"
fill="currentColor"
fillRule="evenodd"
></path>
</svg>
);
};
1 change: 1 addition & 0 deletions src/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export { ArrowUp } from "./ArrowUp";
export { BulletList } from "./BulletList";
export { Clear } from "./Clear";
export { Close } from "./Close";
export { Export } from "./Export";
export { Gear } from "./Gear";
export { TextFile } from "./TextFile";
70 changes: 68 additions & 2 deletions src/plugin/Panel/ChatInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button, PromptInput } from "@components";
import { usePlugin } from "@context";
import { Add, ArrowUp, Clear } from "@icons";
import { Add, ArrowUp, Clear, Export } from "@icons";
import { UserMessage } from "@types";
import type { FC } from "react";
import { useState } from "react";
Expand Down Expand Up @@ -81,6 +81,61 @@ export const ChatInput: FC = () => {
dispatch({ type: "setMediaDialogState", state: "open" });
}

function exportConversation() {
const title = state.manifest.label?.en?.[0] ?? state.manifest.id;
const exportDate = new Date().toISOString();

let markdown = `# ${title}\n\n`;
markdown += `- **Name**: ${state.manifest.label?.en?.[0] ?? "N/A"}\n`;
markdown += `- **Manifest URI**: ${state.manifest.id}\n`;
markdown += `- **Exported at**: ${exportDate}\n\n`;

for (const message of state.messages) {
switch (message.role) {
case "user": {
markdown += `## User Message\n\n`;
for (const content of message.content) {
switch (content.type) {
case "text":
markdown += `${content.content}\n\n`;
break;
case "annotation":
markdown += `### Annotation\n\n${content.content.content}\n\n`;
break;
case "media":
markdown += `### Media\n\n[${content.content?.caption ?? "IIIF Image"}](${content.content.src})\n\n`;
break;
}
}
break;
}
case "assistant": {
markdown += `## Assistant Message\n\n`;
switch (message.type) {
case "tool-call":
markdown += `### Tool Calls\n\n`;
markdown += `**${message.content.tool_name}**: ${message.content.content}\n\n`;
break;
case "response":
markdown += `### Response\n\n${message.content.content}\n\n`;
break;
}
break;
}
default:
break;
}
}

const blob = new Blob([markdown], { type: "text/markdown" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `${title.replace(/[^a-z0-9]/gi, "_")}.md`;
a.click();
URL.revokeObjectURL(url);
}

const PromptInputButtons = state?.provider?.PromptInputButtons?.bind(state.provider) ?? null; // bind `this`;
return (
<form
Expand Down Expand Up @@ -117,7 +172,7 @@ export const ChatInput: FC = () => {
})}
</div>
<div className="controls">
{PromptInputButtons && <PromptInputButtons />}
{PromptInputButtons && <PromptInputButtons disabled={state.conversationState === "assistant_responding"} />}
<Button
aria-label="Clear conversation"
shape="circle"
Expand All @@ -138,6 +193,17 @@ export const ChatInput: FC = () => {
>
<Add />
</Button>
<Button
aria-label="Export conversation"
disabled={state.messages.filter((m) => m.role !== "system").length === 0}
shape="circle"
size="small"
state={formState !== "success" ? formState : undefined}
title="Export conversation"
onClick={exportConversation}
>
<Export />
</Button>
<Button
aria-label="Submit question"
disabled={!textareaValue.trim()}
Expand Down
2 changes: 1 addition & 1 deletion src/plugin/base_provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export abstract class BaseProvider {
/**
* A component that providers can implement to add buttons to the Prompt Input area, extending functionality.
*/
PromptInputButtons(): JSX.Element & { props: { children: (typeof Button)[] } } {
PromptInputButtons(_props: { disabled?: boolean }): JSX.Element & { props: { children: (typeof Button)[] } } {
return <></>;
}

Expand Down
5 changes: 4 additions & 1 deletion src/providers/userTokenProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,9 @@ export class UserTokenProvider extends BaseProvider {
}
}

PromptInputButtons() {
PromptInputButtons(props: {
disabled?: boolean;
}): JSX.Element & { props: { children: (typeof Button)[] } } {
/* eslint-disable react-hooks/rules-of-hooks */
const dialogRef = useRef<HTMLDialogElement>(null);
const [isOpen, setIsOpen] = useState(false);
Expand Down Expand Up @@ -422,6 +424,7 @@ export class UserTokenProvider extends BaseProvider {
</Dialog>
<Button
aria-label="Tasks"
disabled={props.disabled}
shape="circle"
size="small"
title="Tasks"
Expand Down
Loading