Skip to content

Commit

Permalink
Some UI
Browse files Browse the repository at this point in the history
  • Loading branch information
awhiteside1 committed Sep 24, 2024
1 parent cff2d86 commit f5418a4
Show file tree
Hide file tree
Showing 17 changed files with 2,525 additions and 176 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,5 @@ dist

**/dist/
dist/
.idea/
.idea/
*storybook.log
14 changes: 14 additions & 0 deletions packages/chat-ui/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
"@storybook/addon-onboarding",
"@storybook/addon-links",
"@storybook/addon-essentials",
"@chromatic-com/storybook",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/react-vite",
options: {},
},
};
24 changes: 24 additions & 0 deletions packages/chat-ui/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Preview } from "@storybook/react";
import { Reshaped } from "reshaped";
import "reshaped/themes/reshaped/theme.css"
const preview: Preview = {
decorators: [
(story) =>
Reshaped({
children: story(),
theme: "reshaped",
defaultTheme:"reshaped",
defaultColorMode: "light",
}),
],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};

export default preview;
118 changes: 71 additions & 47 deletions packages/chat-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,49 +1,73 @@
{
"name": "@payload-llm-plugins/chat-ui",
"type": "module",
"types": "./dist/index.d.ts",
"module": "./dist/index.mjs",
"sources": "src/index.ts",
"files": ["dist/", "*.css"],
"exports": {
"./ChatView": {
"types": "./src/ui/page/ChatView.ts",
"import": "./dist/ui/page/ChatView.js",
"default": "./dist/ui/page/ChatView.cjs"
},
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"default": "./dist/index.cjs"
}
},
"scripts": {
"test": "vitest --run",
"bench": "vitest --run bench",
"build:ui": "vite build",
"build:plugin": "unbuild --sourcemaps",
"build": "pnpm build:plugin && pnpm build:ui",
"generate": "payload generate:types"
},
"dependencies": {
"defu": "^6.1.4",
"radash": "^12.1.0"
},
"peerDependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"tsup": "^8.3.0",
"@types/react": "18.3.8",
"@types/react-dom": "^18.3.0",
"vite-plugin-lib-inject-css": "^2.1.1",
"type-fest": "^4.26.1",
"@vitejs/plugin-react": "^4.3.1",
"vite": "^5.4.7",
"vitest": "^2.1.1",
"reshaped": "^3.2.0-canary.5",
"postcss": "^8.4.47",
"vite-plugin-externalize-deps": "^0.8.0"
}
"name": "@payload-llm-plugins/chat-ui",
"type": "module",
"types": "./dist/index.d.ts",
"module": "./dist/index.mjs",
"sources": "src/index.ts",
"files": [
"dist/",
"*.css"
],
"exports": {
"./ChatView": {
"types": "./src/ui/page/ChatView.ts",
"import": "./dist/ui/page/ChatView.js",
"default": "./dist/ui/page/ChatView.cjs"
},
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"default": "./dist/index.cjs"
}
},
"scripts": {
"test": "vitest --run",
"bench": "vitest --run bench",
"build:ui": "vite build",
"build:plugin": "unbuild --sourcemaps",
"build": "pnpm build:plugin && pnpm build:ui",
"generate": "payload generate:types",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@heroicons/react": "^2.1.5",
"chancejs": "^0.0.8",
"defu": "^6.1.4",
"feather-icons": "^4.29.2",
"radash": "^12.1.0",
"reshaped": "^3.2.0-canary.6",
"ts-pattern": "^5.3.1"
},
"peerDependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@chromatic-com/storybook": "^1.9.0",
"@storybook/addon-essentials": "^8.3.2",
"@storybook/addon-interactions": "^8.3.2",
"@storybook/addon-links": "^8.3.2",
"@storybook/addon-onboarding": "^8.3.2",
"@storybook/blocks": "^8.3.2",
"@storybook/react": "^8.3.2",
"@storybook/react-vite": "^8.3.2",
"@storybook/test": "^8.3.2",
"@types/chance": "^1.1.6",
"@types/feather-icons": "^4.29.4",
"@types/react": "18.3.8",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"chance": "^1.1.12",
"postcss": "^8.4.47",
"prop-types": "^15.8.1",
"reshaped": "^3.2.0-canary.5",
"storybook": "^8.3.2",
"tsup": "^8.3.0",
"type-fest": "^4.26.1",
"vite": "^5.4.7",
"vite-plugin-externalize-deps": "^0.8.0",
"vite-plugin-lib-inject-css": "^2.1.1",
"vitest": "^2.1.1"
}
}
30 changes: 30 additions & 0 deletions packages/chat-ui/src/narrative/messages/plain/Message.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Message } from "./Message";
import { Meta, StoryObj } from "@storybook/react";
import { Avatar } from "reshaped";

export default {
parameters:{
layout: "centered"
},
component: Message,
} as Meta<typeof Message>;

export const UserExample: StoryObj<typeof Message> = {
args: {
children: <p>A super important message<br/>It's a small world after all </p>,
speaker: {
role: "user",
avatar: <Avatar size={10} color="primary" initials="AW" />,
},
},
};

export const AssistantExample: StoryObj<typeof Message> = {
args: {
children: <p>A super important message<br/>It's a small world after all </p>,
speaker: {
role: "assistant",
avatar: <Avatar size={10} color="neutral" initials="GPT" />,
},
},
};
50 changes: 50 additions & 0 deletions packages/chat-ui/src/narrative/messages/plain/Message.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as React from "react";

import { PropsWithChildren, ReactNode } from "react";
import { View, ViewProps } from "reshaped";

type Role = "assistant" | "user" | "member";

interface Props {
speaker: {
role: Role;
avatar: ReactNode;
};
}

const Options = {
user: {
color: "primary-faded",
side: "right",
},
member: {
color: "primary-faded",
side: "left",
},
assistant: {
color: "neutral-faded",
side: "left",
},
} satisfies Record<
Role,
{ color: ViewProps["backgroundColor"]; side: "left" | "right" }
>;

export const Message = ({ children, speaker }: PropsWithChildren<Props>) => {
const { color, side } = Options[speaker.role];
const direction = side === "left" ? "row" : "row-reverse";

return (
<View width="100%" align="end" gap={3} wrap={false} maxWidth="100%" direction={direction}>
<View maxWidth={10}>{speaker.avatar}</View>
<View
maxWidth="60%"
padding={3}
backgroundColor={color}
borderRadius="medium"
>
{children}
</View>
</View>
);
};
63 changes: 63 additions & 0 deletions packages/chat-ui/src/narrative/messages/plain/fixtures.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Role, Speaker } from "./type";
import { Avatar } from "reshaped";
import { match } from "ts-pattern";
import { draw, list } from "radash";
import { Message } from "./Message";
import Chance from "chance";
import feather from "feather-icons";

const givenASpeaker = (role: Role = "user"): Speaker => {
const initials = match(role)
.with("user", () => "AW")
.with("assistant", () => "GPT")
.with("member", () => "SS")
.otherwise(() => "?");

return {
avatar: (
<Avatar
size={10}
color={draw(["primary", "neutral", "positive"])}
initials={initials}
/>
),
role: role,
};
};

export const givenSomeMessages = (count = 5) => {
const chance = new Chance();
const assistant = givenASpeaker("assistant");
const user = givenASpeaker("user");

return (
<>
{list(0, count, (i) => (
<Message key={i} speaker={chance.pickone([assistant, user])}>
<p>{chance.sentence()}</p>
</Message>
))}
</>
);
};

export const givenAnIceBreaker = () => {
const chance = new Chance();
return {
title: chance.sentence({ words: 4 }),
subtitle: chance.sentence({ words: 8 }),
icon: (
<FeatherIcon icon={draw(["speaker", "coffee", "anchor", "server"])} />
),
};
};

export const FeatherIcon = ({ icon }: { icon: keyof typeof feather.icons }) => {
const data = feather.icons[icon];
return (
<svg
{...data.attrs}
dangerouslySetInnerHTML={{ __html: data.contents }}
></svg>
);
};
11 changes: 11 additions & 0 deletions packages/chat-ui/src/narrative/messages/plain/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ReactNode } from "react";

export type Role = "assistant" | "user" | "member";
export type Speaker = {
role: Role;
avatar: ReactNode;
}

export interface Props {
speaker: Speaker
}
28 changes: 28 additions & 0 deletions packages/chat-ui/src/narrative/panes/chat/Chat.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ChatPane, WithMessages } from "./ChatPane";
import { Meta, StoryObj } from "@storybook/react";
import {
givenAnIceBreaker,
givenSomeMessages,
} from "../../messages/plain/fixtures";
import { Icebreaker, IceBreakers } from "./elements/Icebreakers";

export default {
component: ChatPane,
parameters: { layout: "full" },
} as Meta<typeof ChatPane>;

export const ChatExample: StoryObj<typeof ChatPane> = {
args: { children: <WithMessages messages={givenSomeMessages()} /> },
};

export const NewChat: StoryObj<typeof ChatPane> = {
args: {
children: (
<IceBreakers>
<Icebreaker {...givenAnIceBreaker()} />
<Icebreaker {...givenAnIceBreaker()} />
<Icebreaker {...givenAnIceBreaker()} />
</IceBreakers>
),
},
};
30 changes: 30 additions & 0 deletions packages/chat-ui/src/narrative/panes/chat/ChatPane.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { MessageList } from "./elements/MessageList";
import { Divider, View } from "reshaped";
import { MessageInput } from "./elements/MessageInput";
import { PropsWithChildren } from "react";

interface Props {
messages: JSX.Element;
}

export const WithMessages = ({ messages }: Props) => {
return <MessageList>{messages}</MessageList>;
};


export const ChatPane = ({ children }: PropsWithChildren) => {
return (
<View
inset={0}
position="fixed"
direction="column"
width="100%"
justify="end"
minHeight="100%"
>
<View padding={3}>{children}</View>
<Divider />
<MessageInput />
</View>
);
};
Loading

0 comments on commit f5418a4

Please sign in to comment.