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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Install dependencies
run: npm install
- name: Run formatting
run: npm run format
run: npm run format:check

lint:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check .",
"test": "vitest --watch=false",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
shape?: "pill" | "circle";
size?: "small" | "medium" | "large";
state?: "idle" | "loading" | "error";
variant?: "primary" | "secondary" | "ghost" | "danger";
variant?: "accent" | "primary" | "secondary" | "ghost" | "danger";
}

/**
Expand Down
28 changes: 23 additions & 5 deletions src/components/Button/style.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@
}

/* Style variants */
.button[data-variant="accent"] {
--button-background-color: var(--clover-ai-colors-accent);
--button-border-color: var(--clover-ai-colors-accent);

&:focus,
&:hover {
--button-border-color: var(--clover-ai-colors-accentAlt);
}
}

.button[data-variant="secondary"] {
--button-color: var(--clover-ai-colors-primary);
--button-background-color: var(--clover-ai-colors-secondaryMuted);
Expand All @@ -60,17 +70,24 @@
}

.button[data-variant="ghost"] {
--button-border-color: transparent;
--button-background-color: transparent;
--button-color: inherit;

&:focus,
&:hover {
--button-background-color: color-mix(
&:focus:not(:disabled),
&:hover:not(:disabled) {
--ghost-hover-background-color: color-mix(
in srgb,
var(--clover-ai-colors-accentMuted) 10%,
transparent
);
--button-border-color: var(--clover-ai-colors-accentAlt);
--button-background-color: var(--ghost-hover-background-color);
}

&[data-variant="ghost"]:disabled {
--button-border-color: transparent;
--button-background-color: transparent;
--button-color: var(--clover-ai-colors-secondaryMuted);
}
}

Expand Down Expand Up @@ -110,11 +127,12 @@
.button:disabled,
.button[data-state="loading"] {
cursor: not-allowed;
--button-background-color: var(--clover-ai-colors-secondaryAlt);
--button-background-color: var(--clover-ai-colors-secondaryMuted);
--button-border-color: var(--clover-ai-colors-secondaryMuted);

&:focus,
&:hover {
--button-background-color: var(--clover-ai-colors-secondaryMuted);
--button-border-color: var(--clover-ai-colors-secondaryMuted);
}
}
Expand Down
9 changes: 2 additions & 7 deletions src/components/Messages/AnnotationContent/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TextFile } from "@icons";
import { AnnotationContent as IAnnotationContent, Role } from "@types";
import type { FC } from "react";
import { useState } from "react";
import { TextFile } from "@icons";
import style from "./style.module.css";

export interface AnnotationContentProps {
Expand All @@ -26,12 +26,7 @@ export const AnnotationContent: FC<AnnotationContentProps> = ({ content, role })
<TextFile />
<span className={style.label}>{expanded ? "Annotation" : "Show more"}</span>
</div>
{expanded && (
<div
className={style.body}
dangerouslySetInnerHTML={{ __html: rawContent }}
/>
)}
{expanded && <div className={style.body} dangerouslySetInnerHTML={{ __html: rawContent }} />}
</div>
);
};
2 changes: 1 addition & 1 deletion src/icons/Export.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ export const Export: React.FC<React.SVGProps<SVGSVGElement>> = () => {
></path>
</svg>
);
};
};
8 changes: 7 additions & 1 deletion src/plugin/Panel/ChatInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,16 @@ export const ChatInput: FC = () => {
})}
</div>
<div className="controls">
{PromptInputButtons && <PromptInputButtons disabled={state.conversationState === "assistant_responding"} />}
{PromptInputButtons && (
<PromptInputButtons disabled={state.conversationState === "assistant_responding"} />
)}
<Button
aria-label="Clear conversation"
shape="circle"
size="small"
state={formState !== "success" ? formState : undefined}
title="Clear conversation"
variant="ghost"
onClick={clearConversation}
>
<Clear />
Expand All @@ -189,6 +192,7 @@ export const ChatInput: FC = () => {
size="small"
state={formState !== "success" ? formState : undefined}
title="Add content"
variant="ghost"
onClick={openDialog}
>
<Add />
Expand All @@ -200,6 +204,7 @@ export const ChatInput: FC = () => {
size="small"
state={formState !== "success" ? formState : undefined}
title="Export conversation"
variant="ghost"
onClick={exportConversation}
>
<Export />
Expand All @@ -212,6 +217,7 @@ export const ChatInput: FC = () => {
state={formState !== "success" ? formState : undefined}
title="Submit question"
type="submit"
variant="accent"
>
<ArrowUp />
</Button>
Expand Down
46 changes: 34 additions & 12 deletions src/plugin/Panel/MediaDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,33 @@ import { Button, Dialog, Heading, ImageSelect } from "@components";
import { usePlugin } from "@context";
import { serializeConfigPresentation3, Traverse } from "@iiif/parser";
import type {
Annotation as IIIFAnnotation,
Canvas,
ContentResource,
EmbeddedResource,
Annotation as IIIFAnnotation,
SpecificResource,
} from "@iiif/presentation-3";
import { Annotation, Media, SelectedContent } from "@types";
import { getLabelByUserLanguage, updateIIIFImageRequestURI } from "@utils";
import { FC, useEffect, useRef, useState } from "react";
import style from "./style.module.css";

function isEmbeddedResource(
body: unknown,
): body is EmbeddedResource {
return typeof body === "object" && body !== null && "type" in body && (body as EmbeddedResource).type === "TextualBody";
function isEmbeddedResource(body: unknown): body is EmbeddedResource {
return (
typeof body === "object" &&
body !== null &&
"type" in body &&
(body as EmbeddedResource).type === "TextualBody"
);
}

function isSpecificResource(body: unknown): body is SpecificResource {
return typeof body === "object" && body !== null && "type" in body && (body as SpecificResource).type === "SpecificResource";
return (
typeof body === "object" &&
body !== null &&
"type" in body &&
(body as SpecificResource).type === "SpecificResource"
);
}

function getTextualBodyValue(body: unknown): string | undefined {
Expand All @@ -46,7 +54,11 @@ function getTextualBodyValue(body: unknown): string | undefined {
return getTextualBodyValue(body.source);
}

if (typeof body === "object" && "value" in body && typeof (body as { value: unknown }).value === "string") {
if (
typeof body === "object" &&
"value" in body &&
typeof (body as { value: unknown }).value === "string"
) {
return (body as { value: string }).value;
}

Expand All @@ -69,7 +81,9 @@ function isAnnotationInSelectedContent(
annotation: Annotation,
selectedContent: SelectedContent[],
): boolean {
return selectedContent.some((item) => item.type === "annotation" && item.content.id === annotation.id);
return selectedContent.some(
(item) => item.type === "annotation" && item.content.id === annotation.id,
);
}

function handleSelectedMedia(
Expand Down Expand Up @@ -172,7 +186,9 @@ const CurrentView = () => {
imgObjectFit="contain"
src={fragmentMedia.src}
initialState={
isMediaInSelectedContent(fragmentMedia, state.selectedContent) ? "selected" : "unselected"
isMediaInSelectedContent(fragmentMedia, state.selectedContent)
? "selected"
: "unselected"
}
onSelectionChange={(selected) => handleAddMedia(selected, fragmentMedia)}
/>
Expand Down Expand Up @@ -237,9 +253,11 @@ const Paintings: FC<{ canvas: Canvas }> = ({ canvas }) => {
<ImageSelect
figcaption={m.caption}
imgObjectFit="cover"
initialState={isMediaInSelectedContent(m, state.selectedContent) ? "selected" : "unselected"}
key={`painting-${m.id}`}
src={m.src}
initialState={
isMediaInSelectedContent(m, state.selectedContent) ? "selected" : "unselected"
}
onSelectionChange={(selected) => handleAddMedia(selected, m)}
/>
))}
Expand Down Expand Up @@ -295,9 +313,11 @@ const Placeholder: FC<{ placeholder: NonNullable<Canvas["placeholderCanvas"]> }>
<ImageSelect
figcaption={m.caption}
imgObjectFit="cover"
initialState={isMediaInSelectedContent(m, state.selectedContent) ? "selected" : "unselected"}
key={`placeholder-${m.id}`}
src={m.src}
initialState={
isMediaInSelectedContent(m, state.selectedContent) ? "selected" : "unselected"
}
onSelectionChange={(selected) => handleAddMedia(selected, m)}
/>
))}
Expand Down Expand Up @@ -383,7 +403,9 @@ const Annotations: FC<{ canvas: Canvas }> = ({ canvas }) => {
function handleAddAnnotation(selected: boolean, annotation: Annotation) {
const resources: SelectedContent[] = selected
? [...state.selectedContent, { type: "annotation", content: annotation }]
: state.selectedContent.filter((item) => !(item.type === "annotation" && item.content.id === annotation.id));
: state.selectedContent.filter(
(item) => !(item.type === "annotation" && item.content.id === annotation.id),
);

dispatch({ type: "setSelectedContent", selectedContent: resources });
}
Expand Down
4 changes: 3 additions & 1 deletion src/plugin/base_provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ export abstract class BaseProvider {
/**
* A component that providers can implement to add buttons to the Prompt Input area, extending functionality.
*/
PromptInputButtons(_props: { disabled?: boolean }): 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/plugin/context/plugin-context.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,10 @@ describe("pluginReducer", () => {

it("should handle setSelectedContent", () => {
const selectedContent: SelectedContent[] = [
{ type: "media", content: { type: "image" as const, id: "media-1", src: "http://example.com/img.png" } },
{
type: "media",
content: { type: "image" as const, id: "media-1", src: "http://example.com/img.png" },
},
{ type: "annotation", content: { id: "annotation-1", content: "<p>Test annotation</p>" } },
];
const action: PluginContextActions = { type: "setSelectedContent", selectedContent };
Expand Down
1 change: 1 addition & 0 deletions src/providers/userTokenProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ export class UserTokenProvider extends BaseProvider {
size="small"
title="Tasks"
type="button"
variant="ghost"
onClick={openDialog}
>
<BulletList />
Expand Down
8 changes: 8 additions & 0 deletions stories/components/Button.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ export const Primary: Story = {
},
};

export const Accent: Story = {
name: "Variant / Accent",
args: {
variant: "accent",
children: "Click me",
},
};

export const Secondary: Story = {
name: "Variant / Secondary",
args: {
Expand Down
Loading