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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const _AIChat = (props: AIChatProps, ref: ForwardedRef<HTMLDivElement>) => {
// assistantName,
chatTitle,
isWaitingForResponse = false,
onApplyAssistantSuggestion,
onPromptChange,
onSubmit,
prompt,
Expand Down Expand Up @@ -56,7 +57,12 @@ const _AIChat = (props: AIChatProps, ref: ForwardedRef<HTMLDivElement>) => {

<ul className={styles.thread} data-testid="t--aichat-thread">
{thread.map((message: ChatMessage) => (
<ThreadMessage {...message} key={message.id} username={username} />
<ThreadMessage
{...message}
key={message.id}
onApplyAssistantSuggestion={onApplyAssistantSuggestion}
username={username}
/>
))}
</ul>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Text } from "@appsmith/wds";
import { clsx } from "clsx";
import React from "react";
import { Button as HeadlessButton } from "react-aria-components";
import styles from "./styles.module.css";
import type { AssistantSuggestionButtonProps } from "./types";

export const AssistantSuggestionButton = ({
children,
className,
...rest
}: AssistantSuggestionButtonProps) => {
return (
<HeadlessButton className={clsx(styles.root, className)} {...rest}>
<Text>{children}</Text>
</HeadlessButton>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./AssistantSuggestionButton";
export * from "./types";
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.root {
height: 30px;
padding: 0 var(--inner-spacing-4);
background-color: var(--bg-neutral-subtle-alt, #e7e8e8);
border-radius: var(--radius-inner-button, 1.8px);

&:hover {
background-color: var(--bg-neutral-subtle-alt-hover, #f0f1f1);
}

&:focus-visible {
box-shadow:
0 0 0 2px var(--color-bg),
0 0 0 4px var(--color-bd-focus);
}

&:active {
background-color: var(--bg-neutral-subtle-alt-active, #e1e2e2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { PropsWithChildren } from "react";
import type { ButtonProps as HeadlessButtonProps } from "react-aria-components";

export interface AssistantSuggestionButtonProps
extends PropsWithChildren<HeadlessButtonProps> {}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Text } from "@appsmith/wds";
import { Flex, Text } from "@appsmith/wds";
import { clsx } from "clsx";
import React from "react";
import Markdown from "react-markdown";
import SyntaxHighlighter from "react-syntax-highlighter";
import { monokai } from "react-syntax-highlighter/dist/cjs/styles/hljs";
import { AssistantSuggestionButton } from "../AssistantSuggestionButton";
import { UserAvatar } from "../UserAvatar";
import styles from "./styles.module.css";
import type { ThreadMessageProps } from "./types";
Expand All @@ -12,6 +13,8 @@ export const ThreadMessage = ({
className,
content,
isAssistant,
onApplyAssistantSuggestion,
promptSuggestions = [],
username,
...rest
}: ThreadMessageProps) => {
Expand Down Expand Up @@ -50,6 +53,25 @@ export const ThreadMessage = ({
{content}
</Markdown>
</Text>

{promptSuggestions.length > 0 && (
<Flex
className={styles.suggestions}
gap="var(--inner-spacing-5)"
paddingTop="spacing-4"
wrap="wrap"
>
{promptSuggestions.map((suggestion) => (
<AssistantSuggestionButton
key={suggestion}
// eslint-disable-next-line react-perf/jsx-no-new-function-as-prop
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not suppress linter errors. If you don't agree on how this rule works, then you can bring it up for discussion and do something about it.

onPress={() => onApplyAssistantSuggestion?.(suggestion)}
>
{suggestion}
</AssistantSuggestionButton>
))}
</Flex>
)}
</div>
) : (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ export interface ThreadMessageProps extends HTMLProps<HTMLLIElement> {
content: string;
isAssistant: boolean;
username: string;
promptSuggestions?: string[];
onApplyAssistantSuggestion?: (suggestion: string) => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export interface ChatMessage {
id: string;
content: string;
isAssistant: boolean;
promptSuggestions?: string[];
}

export interface AIChatProps {
Expand All @@ -15,4 +16,5 @@ export interface AIChatProps {
isWaitingForResponse?: boolean;
onPromptChange: (prompt: string) => void;
onSubmit?: () => void;
onApplyAssistantSuggestion?: (suggestion: string) => void;
}
1 change: 0 additions & 1 deletion app/client/packages/rts/src/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
ATTR_SERVICE_INSTANCE_ID,
} from "@opentelemetry/semantic-conventions/incubating";
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
import { registerInstrumentations } from "@opentelemetry/instrumentation";
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
Expand Down
152 changes: 152 additions & 0 deletions app/client/src/components/propertyControls/ArrayComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { Button } from "@appsmith/ads";
import { debounce } from "lodash";
import React, { useCallback, useEffect, useState } from "react";
import styled from "styled-components";
import { ControlWrapper, InputGroup } from "./StyledControls";

function updateOptionLabel<T>(
items: Array<T>,
index: number,
updatedLabel: string,
) {
return items.map((option: T, optionIndex) => {
if (index !== optionIndex) {
return option;
}

return updatedLabel;
});
}

const StyledBox = styled.div`
width: 10px;
`;

type UpdateItemsFunction = (
items: string[],
isUpdatedViaKeyboard?: boolean,
) => void;

interface ArrayComponentProps {
items: string[];
updateItems: UpdateItemsFunction;
addLabel?: string;
}

const StyledInputGroup = styled(InputGroup)`
> .ads-v2-input__input-section > div {
flex: 1;
min-width: 0px;
}
`;

export function ArrayComponent(props: ArrayComponentProps) {
const [renderItems, setRenderItems] = useState<string[]>([]);
const [typing, setTyping] = useState<boolean>(false);
const { items } = props;

useEffect(() => {
let { items } = props;

items = Array.isArray(items) ? items.slice() : [];

items.length !== 0 && !typing && setRenderItems(items);
}, [props, items.length, renderItems.length, typing]);

const debouncedUpdateItems = useCallback(
debounce((updatedItems: string[]) => {
props.updateItems(updatedItems, true);
}, 200),
[props.updateItems],
);

function updateKey(index: number, updatedKey: string) {
let { items } = props;

items = Array.isArray(items) ? items : [];
const updatedItems = updateOptionLabel(items, index, updatedKey);
const updatedRenderItems = updateOptionLabel(
renderItems,
index,
updatedKey,
);

setRenderItems(updatedRenderItems);
debouncedUpdateItems(updatedItems);
}

function deleteItem(index: number, isUpdatedViaKeyboard = false) {
let { items } = props;

items = Array.isArray(items) ? items : [];

const newItems = items.filter((o, i) => i !== index);
const newRenderItems = renderItems.filter((o, i) => i !== index);

setRenderItems(newRenderItems);
props.updateItems(newItems, isUpdatedViaKeyboard);
}

function addItem(e: React.MouseEvent) {
let { items } = props;

items = Array.isArray(items) ? items.slice() : [];

items.push("");

const updatedRenderItems = renderItems.slice();

updatedRenderItems.push("");

setRenderItems(updatedRenderItems);
props.updateItems(items, e.detail === 0);
}

function onInputFocus() {
setTyping(true);
}

function onInputBlur() {
setTyping(false);
}

return (
<>
{renderItems.map((item: string, index) => {
return (
<ControlWrapper key={index} orientation={"HORIZONTAL"}>
<StyledInputGroup
dataType={"text"}
onBlur={onInputBlur}
onChange={(value: string) => updateKey(index, value)}
onFocus={onInputFocus}
value={item}
/>
<StyledBox />
<Button
isIconButton
kind="tertiary"
onClick={(e: React.MouseEvent) =>
deleteItem(index, e.detail === 0)
}
size="sm"
startIcon="delete-bin-line"
/>
</ControlWrapper>
);
})}

<div className="flex flex-row-reverse mt-1">
<Button
className="t--property-control-options-add"
kind="tertiary"
onClick={addItem}
size="sm"
startIcon="plus"
>
{props.addLabel || "Add suggestion"}
</Button>
</div>
</>
);
}
48 changes: 48 additions & 0 deletions app/client/src/components/propertyControls/ArrayControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { objectKeys } from "@appsmith/utils";
import type { DropdownOption } from "components/constants";
import React from "react";
import { isDynamicValue } from "utils/DynamicBindingUtils";
import { ArrayComponent } from "./ArrayComponent";
import type { ControlData, ControlProps } from "./BaseControl";
import BaseControl from "./BaseControl";

class ArrayControl extends BaseControl<ControlProps> {
render() {
return (
<ArrayComponent
items={this.props.propertyValue}
updateItems={this.updateItems}
/>
);
}

updateItems = (items: string[], isUpdatedViaKeyboard = false) => {
this.updateProperty(this.props.propertyName, items, isUpdatedViaKeyboard);
};

static getControlType() {
return "ARRAY_INPUT";
}

static canDisplayValueInUI(_config: ControlData, value: string): boolean {
if (isDynamicValue(value)) return false;

try {
const items: DropdownOption[] = JSON.parse(value);

for (const x of items) {
const keys = objectKeys(x);

if (!keys.includes("label") || !keys.includes("value")) {
return false;
}
}
} catch {
return false;
}

return true;
}
}

export default ArrayControl;
2 changes: 2 additions & 0 deletions app/client/src/components/propertyControls/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,14 @@ import type { IconSelectControlV2Props } from "./IconSelectControlV2";
import IconSelectControlV2 from "./IconSelectControlV2";
import PrimaryColumnsControlWDS from "./PrimaryColumnsControlWDS";
import ToolbarButtonListControl from "./ToolbarButtonListControl";
import ArrayControl from "./ArrayControl";

export const PropertyControls = {
InputTextControl,
DropDownControl,
SwitchControl,
OptionControl,
ArrayControl,
CodeEditorControl,
DatePickerControl,
ActionSelectorControl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ export const defaultsConfig = {
widgetType: "AI_CHAT",
version: 1,
responsiveBehavior: ResponsiveBehavior.Fill,
initialAssistantMessage: "",
initialAssistantSuggestions: [],
} as unknown as WidgetDefaultProps;
Loading