Skip to content

Commit

Permalink
Merge branch 'release' into feat/32440-editable-ide-tabs
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-golovanov committed Oct 10, 2024
2 parents 14a6c75 + a0814e1 commit dc89acb
Show file tree
Hide file tree
Showing 15 changed files with 388 additions and 44 deletions.
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
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

0 comments on commit dc89acb

Please sign in to comment.