diff --git a/docs/docs/guidelines/chat-widget.mdx b/docs/docs/guidelines/chat-widget.mdx new file mode 100644 index 000000000..e515e4e94 --- /dev/null +++ b/docs/docs/guidelines/chat-widget.mdx @@ -0,0 +1,200 @@ +import ThemedImage from "@theme/ThemedImage"; +import useBaseUrl from "@docusaurus/useBaseUrl"; +import ZoomableImage from "/src/theme/ZoomableImage.js"; +import ReactPlayer from "react-player"; +import Admonition from '@theme/Admonition'; + +# Chat Widget + +
+ The Langflow Chat Widget is a powerful web component that enables + communication with a Langflow project. This widget allows for a chat interface embedding, + allowing the integration of Langflow into web applications effortlessly. +
+ +## Features + +🌟 **Seamless Integration:** Easily integrate the Langflow Chat Widget into your website or web application with just a few lines of JavaScript. + +🚀 **Interactive Chat Interface:** Engage your users with a user-friendly conversation, powered by Langflow's advanced language understanding capabilities. + +🎛️ **Customizable Styling:** Customize the appearance of the chat widget to match your application's design and branding. + +🌐 **Multilingual Support:** Communicate with users in multiple languages, opening up your application to a global audience. + +--- + +## Usage + +
+ You can get the HTML code embedded with the chat by clicking the Code button + at the Sidebar after building a flow. +
+ +
+ +
+ +
+ Clicking the Chat Widget HTML tab, you'll get the code to be + inserted. Read below to learn how to use it with HTML, React and Angular. +
+ +
+ +
+ +--- + +### HTML + +The Chat Widget can be embedded into any HTML page, inside a _``_ tag, as demonstrated in the video below. + +
+ +
+ +--- + +### React + +To embed the Chat Widget using React, you'll need to insert this _` +``` + +Then, declare your Web Component and encapsulate it in a React component. + +```jsx +declare global { + namespace JSX { + interface IntrinsicElements { + "langflow-chat": any; + } + } +} + +export default function ChatWidget({ className }) { + return ( +
+ +
+ ); +} +``` + +Finally, you can place the component anywhere in your code to display the Chat Widget. + +--- + +### Angular + +To use it in Angular, first add this _` +``` + +When you use a custom web component in an Angular template, the Angular compiler might show a warning when it doesn't recognize the custom elements by default. To suppress this warning, add _`CUSTOM_ELEMENTS_SCHEMA`_ to the module's _`@NgModule.schemas`_. + +- Open the module file (it typically ends with *.module.ts*) where you'd add the _`langflow-chat`_ web component. +- Import _`CUSTOM_ELEMENTS_SCHEMA`_ at the top of the file: + +```ts +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; +``` + +- Add _`CUSTOM_ELEMENTS_SCHEMA`_ to the 'schemas' array inside the '@NgModule' decorator: + +```ts +@NgModule({ + declarations: [ + // ... Other components and directives ... + ], + imports: [ + // ... Other imported modules ... + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], // Add the CUSTOM_ELEMENTS_SCHEMA here +}) +export class YourModule {} +``` + +In your Angular project, find the component belonging to the module where _`CUSTOM_ELEMENTS_SCHEMA`_ was added. + +- Inside the template, add the _`langflow-chat`_ tag to include the Chat Widget in your component's view: + +```jsx + +``` + + + + + +--- + +## Configuration + +Use the widget API to customize your Chat Widget: + + + Props with the type JSON need to be passed as Stringified JSONs, with the format {"key":"value"}. + + + +| Prop | Type | Required | Description | +| --------------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| bot_message_style | JSON | No | Applies custom formatting to bot messages. | +| chat_input_field | String | Yes | Defines the type of the input field for chat messages. | +| chat_inputs | JSON | Yes | Determines the chat input elements and their respective values. | +| chat_output_key | String | No | Specifies which output to display if multiple outputs are available. | +| chat_position | String | No | Positions the chat window on the screen (options include: top-left, top-center, top-right, center-left, center-right, bottom-right, bottom-center, bottom-left). | +| chat_trigger_style | JSON | No | Styles the chat trigger button. | +| chat_window_style | JSON | No | Customizes the overall appearance of the chat window. | +| error_message_style | JSON | No | Sets the format for error messages within the chat window. | +| flow_id | String | Yes | Identifies the flow that the component is associated with. | +| height | Number | No | Sets the height of the chat window in pixels. | +| host_url | String | Yes | Specifies the URL of the host for chat component communication. | +| input_container_style | JSON | No | Applies styling to the container where chat messages are entered. | +| input_style | JSON | No | Sets the style for the chat input field. | +| online | Boolean | No | Toggles the online status of the chat component. | +| online_message | String | No | Sets a custom message to display when the chat component is online. | +| placeholder | String | No | Sets the placeholder text for the chat input field. | +| placeholder_sending | String | No | Sets the placeholder text to display while a message is being sent. | +| send_button_style | JSON | No | Sets the style for the send button in the chat window. | +| send_icon_style | JSON | No | Sets the style for the send icon in the chat window. | +| tweaks | JSON | No | Applies additional custom adjustments for the associated flow. | +| user_message_style | JSON | No | Determines the formatting for user messages in the chat window. | +| width | Number | No | Sets the width of the chat window in pixels. | +| window_title | String | No | Sets the title displayed in the chat window's header or title bar. | diff --git a/docs/sidebars.js b/docs/sidebars.js index 38592719d..a2f710d08 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -21,6 +21,7 @@ module.exports = { "guidelines/collection", "guidelines/prompt-customization", "guidelines/chat-interface", + "guidelines/chat-widget", "guidelines/custom-component", ], }, diff --git a/docs/static/img/widget-code.png b/docs/static/img/widget-code.png new file mode 100644 index 000000000..389945a58 Binary files /dev/null and b/docs/static/img/widget-code.png differ diff --git a/docs/static/img/widget-sidebar.png b/docs/static/img/widget-sidebar.png new file mode 100644 index 000000000..a3e818216 Binary files /dev/null and b/docs/static/img/widget-sidebar.png differ diff --git a/docs/static/videos/langflow_widget.mp4 b/docs/static/videos/langflow_widget.mp4 new file mode 100644 index 000000000..7894316f7 Binary files /dev/null and b/docs/static/videos/langflow_widget.mp4 differ diff --git a/src/frontend/src/components/chatComponent/index.tsx b/src/frontend/src/components/chatComponent/index.tsx index 58c058866..3cbe5bbf1 100644 --- a/src/frontend/src/components/chatComponent/index.tsx +++ b/src/frontend/src/components/chatComponent/index.tsx @@ -12,9 +12,8 @@ import { NodeType } from "../../types/flow"; export default function Chat({ flow }: ChatType) { const [open, setOpen] = useState(false); - const [isBuilt, setIsBuilt] = useState(false); const [canOpen, setCanOpen] = useState(false); - const { tabsState } = useContext(TabsContext); + const { tabsState, isBuilt, setIsBuilt } = useContext(TabsContext); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { diff --git a/src/frontend/src/components/codeAreaComponent/index.tsx b/src/frontend/src/components/codeAreaComponent/index.tsx index 37f41e2d1..05e8127da 100644 --- a/src/frontend/src/components/codeAreaComponent/index.tsx +++ b/src/frontend/src/components/codeAreaComponent/index.tsx @@ -45,7 +45,7 @@ export default function CodeAreaComponent({ editNode ? "input-edit-node input-dialog" : (disabled ? " input-disable input-ring " : "") + - " input-primary text-muted-foreground " + " primary-input text-muted-foreground " } > {myValue !== "" ? myValue : "Type something..."} diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx index 1357e0c79..9ccdb38e4 100644 --- a/src/frontend/src/components/codeTabsComponent/index.tsx +++ b/src/frontend/src/components/codeTabsComponent/index.tsx @@ -145,7 +145,7 @@ export default function CodeTabsComponent({ ))} - {Number(activeTab) < 3 && ( + {Number(activeTab) < 4 && (
- - - +
+ + + +
+
-
- -
-
- - - {flow && flow.data && ( - +
- +
-
- )} +
+ +
+ +
+ {flow && flow.data && ( + +
+ +
+
+ )} +
- - - - + onClick={(event) => { + saveFlow(flow); + setSuccessData({ title: "Changes saved successfully" }); + }} + > + + +
+
diff --git a/src/frontend/src/style/applies.css b/src/frontend/src/style/applies.css index af82fcfef..bc820d1f3 100644 --- a/src/frontend/src/style/applies.css +++ b/src/frontend/src/style/applies.css @@ -107,6 +107,12 @@ .side-bar-buttons-arrangement { @apply mb-2 mt-2 flex w-full items-center justify-between gap-2 px-2; } + .side-bar-button { + @apply flex w-full; + } + .button-disable { + @apply pointer-events-none; + } .extra-side-bar-buttons { @apply relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out; } @@ -116,20 +122,20 @@ .button-div-style { @apply flex gap-2; } - .input-primary { + .primary-input { @apply form-input block w-full truncate rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm; } - /* The same as input-primary but no-truncate */ + /* The same as primary-input but no-truncate */ .textarea-primary { @apply form-input block w-full rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm; } .input-edit-node { - @apply input-primary w-full border-border pb-0.5 pt-0.5 text-left; + @apply primary-input w-full pb-0.5 pt-0.5 text-left; } .input-search { - @apply input-primary mx-2 pr-7; + @apply primary-input mx-2 pr-7; } .input-disable { @apply border-transparent bg-border placeholder:text-ring; @@ -414,7 +420,7 @@ @apply input-edit-node relative pr-8; } .dropdown-component-false-outline { - @apply input-primary py-2 pl-3 pr-10 text-left; + @apply primary-input py-2 pl-3 pr-10 text-left; } .dropdown-component-display { @apply block w-full truncate bg-background; @@ -780,6 +786,9 @@ .node-modal-button-box { @apply flex-max-width flex-row-reverse bg-input px-4 pb-3; } + .link-color { + @apply font-semibold text-foreground; + } .node-modal-button { @apply inline-flex w-full justify-center rounded-md border border-transparent bg-status-red px-4 py-2 text-base font-medium text-background shadow-sm hover:bg-ring sm:ml-3 sm:w-auto sm:text-sm; } diff --git a/src/frontend/src/types/tabs/index.ts b/src/frontend/src/types/tabs/index.ts index 1a873f651..82934cf35 100644 --- a/src/frontend/src/types/tabs/index.ts +++ b/src/frontend/src/types/tabs/index.ts @@ -18,6 +18,8 @@ export type TabsContextType = { ) => void; downloadFlows: () => void; uploadFlows: () => void; + isBuilt: boolean; + setIsBuilt: (state: boolean) => void; uploadFlow: (newFlow?: boolean, file?: File) => void; hardReset: () => void; getNodeId: (nodeType: string) => string; diff --git a/src/frontend/src/utils/utils.ts b/src/frontend/src/utils/utils.ts index a96655bd9..6ece6f4b6 100644 --- a/src/frontend/src/utils/utils.ts +++ b/src/frontend/src/utils/utils.ts @@ -467,7 +467,7 @@ export function getCurlCode( /** * Function to get the python code for the API - * @param {string} flowName - The name of the flow + * @param {string} flow - The current flow * @returns {string} - The python code */ export function getPythonCode( @@ -489,3 +489,33 @@ flow = load_flow_from_json("${flowName}.json", tweaks=TWEAKS) inputs = ${inputs} flow(inputs)`; } + +/** + * Function to get the widget code for the API + * @param {string} flow - The current flow. + * @returns {string} - The widget code + */ +export function getWidgetCode(flow: FlowType, tabsState?: TabsState): string { + const flowId = flow.id; + const flowName = flow.name; + const inputs = buildInputs(tabsState, flow.id); + + return ` + + +`; +}