Skip to content

Commit

Permalink
Merge pull request #671 from vernondegoede/vernon/improve-chat-custom…
Browse files Browse the repository at this point in the history
…izations

Introduce more chat customization options
  • Loading branch information
gharbat authored Mar 3, 2024
2 parents 0e04a0f + 8da34c3 commit b417c29
Show file tree
Hide file tree
Showing 48 changed files with 402 additions and 469 deletions.
45 changes: 26 additions & 19 deletions copilot-widget/README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,57 @@
# Copilot widget
# OpenCopilot widget

This is the widget for your copilot, it's what your users will interact with.
This is the widget for OpenCopilot: it's what your users will interact with.

It's a simple react application built to be used in any webpage as a widget, to download the latest build of the widget, go to the actions tab and download the latest build artifact.
It's a simple React application that can be used in any webpage as a widget. To download the latest build of the widget, go to the actions tab in GitHub and download the latest build artifact.

## How to install

1. download the latest build artifact from the actions tab.
1. Download the latest build artifact from the actions tab.

2. extract the zip file.
2. Extract the zip file.

3. copy the `assets/*.js` file to your project.
3. Copy the `assets/*.js` file to your project.

4. reference the js file in your html file as follows:
4. Reference the js file in your HTML file as follows:

```html
<script src="[some_js_file].js"></script>
```

5. init the widget.
5. Initialize the widget.

```html
<script>
// you should run it after window loads
window.onload = () => {
initAiCoPilot({
initialMessage: "Hi Sir", // initial message obiviously.
token: "not_super_secret_token", // your copilot token.
rootId: "copilot-widget", //@optional: the root element id in which the widget will mount on
triggerSelector: "#triggerSelector", // the selector of the element that will trigger the widget on click.
apiUrl: "https://cloud.openchat.so/api", // the url of the copilot api.
initialMessage: "Hey, how can I help you today?", // initial bot message
token: "not_super_secret_token", // your OpenCopilot token
rootId: "copilot-widget", // @optional: the root element id in which the widget will mount on
triggerSelector: "#triggerSelector", // the selector of the element that will trigger the widget on click
apiUrl: "https://cloud.openchat.so/api", // the url of the OpenCopilot api.
headers: {
// headers that you want to send with every message request.
Authorization: "Bearer your_auth_token_goes_here",
},
user: { name: "Some User" }, // @optional: user object
}, // @optional: headers that you want to send with every message request
bot: {
name: "AI Assistant",
avatarUrl: "https://example.com/company-logo.png"
}, // @optional: bot object – this info will be used in the chat
user: {
name: "John Doe",
avatarUrl: "https://example.com/user-avatar.png"
}, // @optional: user object – this info will be used in the chat
containerProps: {}, // @optional: `HTMLProps`
warnBeforeClose: true, // @optional: Set to false if you don't want to warn the user before closing the chat
});
};
</script>
```

### How to use
## How to use

1. click on the trigger element to open the widget.
1. Click on the trigger element to open the widget.

2. type your message and press enter to send it.
2. Type your message and press enter to send it.

<img width="394" alt="OpenCopilot widget" src="https://github.com/openchatai/OpenCopilot/assets/32633162/77b30faa-c59e-4a3a-821a-d14a61a49a65">
96 changes: 38 additions & 58 deletions copilot-widget/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,6 @@
href="https://fonts.googleapis.com/css2?family=Noto+Sans+Arabic:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
</head>

<body>
<main>
<h2>
This is an Example on how the widget is fluid and can be placed
anywhere;
</h2>
</main>
<style>
/* reset */
* {
Expand All @@ -34,9 +25,7 @@ <h2>
background-color: #f5f5f5;
font-family: "Noto Sans Arabic", sans-serif;
}
</style>

<style>
#opencopilot-root {
margin-top: 50px;
background-color: red;
Expand All @@ -49,7 +38,7 @@ <h2>
}

#opencopilot-root::after {
content: "Cotainer";
content: "Container";
position: absolute;
top: 0;
translate: -50% -50%;
Expand All @@ -60,60 +49,59 @@ <h2>
color: black;
}
</style>
</head>

<body>
<main>
<h2>
Playground: here you can see how the widget behaves in different
contexts
</h2>
</main>

<script type="module" src="/src/main.tsx"></script>
<div class="some-class">some text here, this is a sample text</div>
<style>
.some-class {
background-color: #fff;
padding: 5px;
border-radius: 5px;
color: black;
position: fixed;
bottom: 10px;
right: 20px;
}
</style>
<div id="opencopilot-root"></div>

<script>
const token = "NtITS4Z07ZrdctTN";
const apiUrl = "http://localhost:8888/backend";
const socketUrl = "http://localhost:8888";
</script>
<script>
const sharedConfig = {
initialMessage: "Welcome back! How can I help you today?", // optional
token: token, // required
apiUrl: apiUrl, // required
socketUrl: socketUrl, // required
defaultOpen: true, // optional
language: "nl", // optional
warnBeforeClose: true,
headers: {
// optional
// Authorization: "Bearer BQAlIe479yo16aXf",
},
bot: {
name: "AI Bot",
},
user: {
// optional
name: "John Doe",
avatarUrl: "https://i.pravatar.cc/[email protected]"
},
};

// this one loads in the center of the screen
document.addEventListener("DOMContentLoaded", () => {
initAiCoPilot({
initialMessage: "Hi Sir", // optional
token: token, // required
...sharedConfig,
triggerSelector: "#triggerSelector", // optional
rootId: "opencopilot-root", // optional otherwise it will create a div with id opencopilot-root
apiUrl: apiUrl, // required
socketUrl: socketUrl, // required
defaultOpen: true, // optional
language: "ar", // optional
containerProps: {}, // optional
headers: {
// optional
// Authorization: "Bearer BQAlIe479yo16aXf",
},
user: {
// optional
name: "John Doe",
},
});
});
</script>

<script>
// this one is rendered on the bottom right corner
// this one is rendered in the bottom right corner
document.addEventListener("DOMContentLoaded", () => {
initAiCoPilot({
initialMessage: "Hi Sir", // optional
token: token, // required
// triggerSelector: "#triggerSelector", // optional
...sharedConfig,
rootId: "opencopilot-root-2", // optional otherwise it will create a div with id opencopilot-root
apiUrl: apiUrl, // required
socketUrl: socketUrl,
defaultOpen: true, // optional
containerProps: {
// optional
style: {
Expand All @@ -126,14 +114,6 @@ <h2>
overflow: "hidden",
},
}, // optional
headers: {
// optional
Authorization: "Bearer BQAlIe479yo16aXf",
},
user: {
// optional
name: "John Doe",
},
});
});
</script>
Expand Down
22 changes: 12 additions & 10 deletions copilot-widget/lib/CopilotWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ import { useEffect, useRef } from "react";
import { useWidgetState } from "./contexts/WidgetState";
import cn from "./utils/cn";
import ChatScreen from "./screens/ChatScreen";
import { IS_SERVER } from "./utils/is_server";
import { isServer } from "./utils/isServer.ts";
import { MessageCircle } from "lucide-react";

function useTrigger(selector?: string, toggle?: () => void) {
const trigger = useRef<HTMLElement | null>(
!selector ? null : IS_SERVER ? null : document.querySelector(selector)
!selector ? null : isServer ? null : document.querySelector(selector)
).current;

useEffect(() => {
if (!selector) {
return;
}
if (trigger && !IS_SERVER) {

if (trigger && !isServer) {
trigger.addEventListener("click", () => toggle?.());
return () => trigger.removeEventListener("click", () => toggle?.());
} else {
Expand All @@ -25,8 +26,8 @@ function useTrigger(selector?: string, toggle?: () => void) {
}, [selector, toggle, trigger]);
}

const TRIGGER_BOTTOM = "20px";
const TRIGGER_RIGHT = "20px";
const OFFSET_BOTTOM = "20px";
const OFFSET_RIGHT = "20px";

export function CopilotWidget({
triggerSelector,
Expand All @@ -38,6 +39,7 @@ export function CopilotWidget({
const [open, toggle] = useWidgetState();
useTrigger(triggerSelector, toggle);
const SHOULD_RENDER_IN_THE_RIGHT_CORNER = !triggerSelector && __isEmbedded;

return (
<>
<div
Expand All @@ -52,13 +54,13 @@ export function CopilotWidget({
)}
style={{
right: SHOULD_RENDER_IN_THE_RIGHT_CORNER
? `calc(${TRIGGER_RIGHT})`
? `calc(${OFFSET_RIGHT})`
: undefined,
bottom: SHOULD_RENDER_IN_THE_RIGHT_CORNER
? `calc(${TRIGGER_BOTTOM} + 60px)`
? `calc(${OFFSET_BOTTOM} + 60px)`
: undefined,
height: SHOULD_RENDER_IN_THE_RIGHT_CORNER
? `calc(95% - ${TRIGGER_BOTTOM} - 100px)`
? `calc(95% - ${OFFSET_BOTTOM} - 100px)`
: "100%",
}}
>
Expand All @@ -69,8 +71,8 @@ export function CopilotWidget({
<div
className="fixed z-50 pointer-events-auto transition-all ease-in-out duration-300"
style={{
bottom: TRIGGER_BOTTOM,
right: TRIGGER_RIGHT,
bottom: OFFSET_BOTTOM,
right: OFFSET_RIGHT,
}}
>
<button
Expand Down
3 changes: 3 additions & 0 deletions copilot-widget/lib/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const cssColors = {
"--opencopilot-primary-clr": "hsl(200 18% 46%)",
"--opencopilot-accent-clr": "hsl(300, 7%, 97%)",
};

type RootProps = {
children: React.ReactNode;
options: ConfigDataContextType;
Expand All @@ -22,8 +23,10 @@ type RootProps = {
HTMLDivElement
>;
};

function Root({ children, options, containerProps }: RootProps) {
const { style, ...containerProp } = containerProps || {};

return (
<root.div
{...containerProp}
Expand Down
Empty file.
Loading

0 comments on commit b417c29

Please sign in to comment.