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
30 changes: 22 additions & 8 deletions dev/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="vite/client" />
import Viewer from "@samvera/clover-iiif/viewer";
import { StrictMode } from "react";
import { StrictMode, useState } from "react";
import { createRoot } from "react-dom/client";
import { PluginControl, PluginPanel } from "../src/plugin";
import { UserTokenProvider } from "../src/providers";
Expand All @@ -11,13 +11,21 @@ const wiki_tool = new WikipediaQueryRun({
maxDocContentLength: 4000,
});

createRoot(document.getElementById("root")!).render(
<StrictMode>
function App() {
const [iiifContent, setIiifContent] = useState<string>(
import.meta.env.VITE_IIIF_URL ||
"https://api.dc.library.northwestern.edu/api/v2/works/8a833741-74a8-40dc-bd1d-c416a3b1bb38?as=iiif",
);

const tokenProvider = new UserTokenProvider({
tools: [wiki_tool],
viewer_iiif_content_callback: (iiif_resource) => {
setIiifContent(iiif_resource);
},
});
return (
<Viewer
iiifContent={
import.meta.env.VITE_IIIF_URL ||
"https://api.dc.library.northwestern.edu/api/v2/works/8a833741-74a8-40dc-bd1d-c416a3b1bb38?as=iiif"
}
iiifContent={iiifContent}
plugins={[
{
id: "clover-ai",
Expand All @@ -32,11 +40,17 @@ createRoot(document.getElementById("root")!).render(
en: ["AI Chat"],
},
componentProps: {
provider: new UserTokenProvider({ tools: [wiki_tool] }),
provider: tokenProvider,
},
},
},
]}
/>
);
}

createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>,
);
2 changes: 2 additions & 0 deletions src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const Button: FC<ButtonProps> = ({
state = "idle",
children,
disabled,
type = "button",
...props
}) => {
return (
Expand All @@ -30,6 +31,7 @@ export const Button: FC<ButtonProps> = ({
data-state={state}
data-variant={variant}
disabled={state === "loading" || disabled}
type={type}
>
{children}
</button>
Expand Down
2 changes: 1 addition & 1 deletion src/components/PromptInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const PromptInput: FC<PromptInputProps> = ({
{error && <div className={styles.errorMessage}>{error}</div>}
<div className={styles.promptInput} data-error={!!error}>
<textarea
aria-label="Write your prompt to chat with a model"
aria-label={rest["aria-label"] ?? "Write your prompt to chat with a model"}
placeholder={placeholder}
{...rest}
></textarea>
Expand Down
12 changes: 12 additions & 0 deletions src/icons/BulletList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const BulletList: React.FC<React.SVGProps<SVGSVGElement>> = () => {
return (
<svg fill="none" height="15" viewBox="0 0 15 15" width="15" xmlns="http://www.w3.org/2000/svg">
<path
clipRule="evenodd"
d="M1.5 5.25C1.91421 5.25 2.25 4.91421 2.25 4.5C2.25 4.08579 1.91421 3.75 1.5 3.75C1.08579 3.75 0.75 4.08579 0.75 4.5C0.75 4.91421 1.08579 5.25 1.5 5.25ZM4 4.5C4 4.22386 4.22386 4 4.5 4H13.5C13.7761 4 14 4.22386 14 4.5C14 4.77614 13.7761 5 13.5 5H4.5C4.22386 5 4 4.77614 4 4.5ZM4.5 7C4.22386 7 4 7.22386 4 7.5C4 7.77614 4.22386 8 4.5 8H13.5C13.7761 8 14 7.77614 14 7.5C14 7.22386 13.7761 7 13.5 7H4.5ZM4.5 10C4.22386 10 4 10.2239 4 10.5C4 10.7761 4.22386 11 4.5 11H13.5C13.7761 11 14 10.7761 14 10.5C14 10.2239 13.7761 10 13.5 10H4.5ZM2.25 7.5C2.25 7.91421 1.91421 8.25 1.5 8.25C1.08579 8.25 0.75 7.91421 0.75 7.5C0.75 7.08579 1.08579 6.75 1.5 6.75C1.91421 6.75 2.25 7.08579 2.25 7.5ZM1.5 11.25C1.91421 11.25 2.25 10.9142 2.25 10.5C2.25 10.0858 1.91421 9.75 1.5 9.75C1.08579 9.75 0.75 10.0858 0.75 10.5C0.75 10.9142 1.08579 11.25 1.5 11.25Z"
fill="currentColor"
fillRule="evenodd"
></path>
</svg>
);
};
13 changes: 6 additions & 7 deletions src/icons/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Add } from "./Add";
import { ArrowUp } from "./ArrowUp";
import { Clear } from "./Clear";
import { Close } from "./Close";
import { Gear } from "./Gear";

export { Add, ArrowUp, Clear, Close, Gear };
export { Add } from "./Add";
export { ArrowUp } from "./ArrowUp";
export { BulletList } from "./BulletList";
export { Clear } from "./Clear";
export { Close } from "./Close";
export { Gear } from "./Gear";
1 change: 0 additions & 1 deletion src/plugin/Panel/ChatInput/SelectedMedia/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export const SelectedMedia: React.FC<SelectedMediaProps> = ({ media }) => {
shape="circle"
size="small"
title="Remove media"
type="button"
variant="secondary"
onClick={() => handleClick(media.id)}
>
Expand Down
5 changes: 3 additions & 2 deletions src/plugin/Panel/ChatInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const ChatInput: FC = () => {
dispatch({ type: "setMediaDialogState", state: "open" });
}

const PromptInputButtons = state?.provider?.PromptInputButtons?.bind(state.provider) ?? null; // bind `this`;
return (
<form
onSubmit={async (e) => {
Expand All @@ -108,13 +109,13 @@ export const ChatInput: FC = () => {
)}
</div>
<div className="controls">
{PromptInputButtons && <PromptInputButtons />}
<Button
aria-label="Clear conversation"
shape="circle"
size="small"
state={formState !== "success" ? formState : undefined}
title="Clear conversation"
type="button"
onClick={clearConversation}
>
<Clear />
Expand All @@ -125,7 +126,6 @@ export const ChatInput: FC = () => {
size="small"
state={formState !== "success" ? formState : undefined}
title="Add media"
type="button"
onClick={openDialog}
>
<Add />
Expand All @@ -136,6 +136,7 @@ export const ChatInput: FC = () => {
size="small"
state={formState !== "success" ? formState : undefined}
title="Submit question"
type="submit"
>
<ArrowUp />
</Button>
Expand Down
2 changes: 1 addition & 1 deletion src/plugin/Panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function PluginPanelComponent(props: CloverPlugin & PluginProps) {

useEffect(() => {
if (provider) {
provider.update_dispatch(dispatch);
provider.update_plugin_dispatch(dispatch);
provider.update_plugin_state(state);
provider.set_system_prompt();
dispatch({ type: "updateProvider", provider });
Expand Down
42 changes: 36 additions & 6 deletions src/plugin/base_provider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Button } from "@components";
import type { PluginContextActions, PluginContextStore } from "@context";
import { ManifestNormalized } from "@iiif/presentation-3-normalized";
import type { ManifestNormalized } from "@iiif/presentation-3-normalized";
import type { ConversationState, Message } from "@types";
import { getLabelByUserLanguage } from "@utils";
import dedent from "dedent";
Expand All @@ -9,7 +10,6 @@ type ProviderStatus = "initializing" | "ready" | "error";

/**
* A Provider class that handles interfacing with the Plugin.
*
*/
export abstract class BaseProvider {
#plugin_dispatch: Dispatch<PluginContextActions> | undefined;
Expand All @@ -20,6 +20,9 @@ export abstract class BaseProvider {
this.#status = "ready";
}

/**
* Get the dispatch function to allow the Provider to update Plugin state
*/
private get plugin_dispatch(): Dispatch<PluginContextActions> {
if (!this.#plugin_dispatch) {
throw new Error("Provider dispatch not initialized.");
Expand All @@ -28,19 +31,25 @@ export abstract class BaseProvider {
}

/**
* Sets the dispatch function to allow the provider to update Plugin state
* Sets the dispatch function to allow the Provider to update Plugin state
*/
private set plugin_dispatch(dispatch: Dispatch<PluginContextActions>) {
this.#plugin_dispatch = dispatch;
}

/**
* Get the current Plugin state
*/
protected get plugin_state(): PluginContextStore {
if (!this.#plugin_state) {
throw new Error("Provider plugin_state not initialized.");
}
return this.#plugin_state;
}

/**
* Sets the current Plugin state
*/
protected set plugin_state(state: PluginContextStore) {
this.#plugin_state = state;
}
Expand Down Expand Up @@ -96,7 +105,7 @@ export abstract class BaseProvider {
}

/**
* Update the Plugin state with the current provider.
* Update the Plugin state with the current Provider.
*/
protected update_plugin_provider() {
this.plugin_dispatch({
Expand All @@ -110,14 +119,27 @@ export abstract class BaseProvider {
*/
abstract generate_response(messages: Message[], conversationHistory: Message[]): Promise<void>;

/**
* Get the current status of the Provider
*/
get status(): ProviderStatus {
return this.#status;
}

/**
* Set the current status of the Provider
*/
set status(value: ProviderStatus) {
this.#status = value;
}

/**
* A component that providers can implement to add buttons to the Prompt Input area, extending functionality.
*/
PromptInputButtons(): JSX.Element & { props: { children: (typeof Button)[] } } {
return <></>;
}

/**
* Set the system prompt in the Plugin state based on the current manifest.
*/
Expand All @@ -131,16 +153,24 @@ export abstract class BaseProvider {
}

/**
* A component that providers can implement to set up their UI.
* A component that a Provider can implement before users can chat.
*
* @remarks This is useful for setting up authentication or other pre-requisites.
*/
SetupComponent(): JSX.Element {
return <></>;
}

update_dispatch(dispatch: Dispatch<PluginContextActions>) {
/**
* Update the reference to the Plugin's dispatch function
*/
update_plugin_dispatch(dispatch: Dispatch<PluginContextActions>) {
this.plugin_dispatch = dispatch;
}

/**
* Update the reference to the Plugin's state
*/
update_plugin_state(context: PluginContextStore) {
this.plugin_state = context;
}
Expand Down
12 changes: 6 additions & 6 deletions src/plugin/context/plugin-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ interface SetVaultAction {
type: "setVault";
}

interface SystemPromptAction {
interface SetSystemPromptAction {
systemPrompt: string;
type: "setSystemPrompt";
}
Expand All @@ -84,16 +84,16 @@ interface UpdateLastMessageAction {
export type PluginContextActions =
| AddMessageAction
| ClearConversation
| UpdateProviderAction
| UpdateLastMessageAction
| SetActiveCanvasAction
| SetConversationState
| SetManifestAction
| SetActiveCanvasAction
| SetMediaDialogStateAction
| SetSelectedMediaAction
| SetOSDViewerAction
| SetSelectedMediaAction
| SetSystemPromptAction
| SetVaultAction
| SystemPromptAction;
| UpdateProviderAction
| UpdateLastMessageAction;

/** Default values not inherited from the Clover Viewer */
type InitPluginContextStore = Omit<PluginContextStore, "vault" | "activeCanvas" | "manifest">;
Expand Down
15 changes: 3 additions & 12 deletions src/providers/mediaPipeProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -354,15 +354,8 @@ export class MediaPipeProvider extends BaseProvider {
</a>
.
</p>
<Button type="button" onClick={handleConsent}>
Accept and Continue
</Button>
<Button
style={{ marginLeft: "0.75rem" }}
type="button"
variant="danger"
onClick={handleForceReload}
>
<Button onClick={handleConsent}>Accept and Continue</Button>
<Button style={{ marginLeft: "0.75rem" }} variant="danger" onClick={handleForceReload}>
Force Reload (Clear Cache)
</Button>
</div>
Expand All @@ -378,9 +371,7 @@ export class MediaPipeProvider extends BaseProvider {
<p className={styles.errorNote}>
Check the browser console for detailed error information.
</p>
<Button type="button" onClick={handleRetry}>
Back
</Button>
<Button onClick={handleRetry}>Back</Button>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function ModelSelection({ handleBack, handleClick, models }: Props) {
</Button>
))}
<div>
<Button type="button" variant="ghost" onClick={handleBack}>
<Button variant="ghost" onClick={handleBack}>
Back
</Button>
</div>
Expand Down
Loading