diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 7e0c36e141..83ffae7e6f 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "instill-sdk", - "version": "0.12.0-rc.2", + "version": "0.12.0-rc.3", "description": "Instill AI's Typescript SDK", "repository": "https://github.com/instill-ai/typescript-sdk.git", "bugs": "https://github.com/instill-ai/community/issues", diff --git a/packages/sdk/src/vdp/types.ts b/packages/sdk/src/vdp/types.ts index ea1e6925a3..ccf8a8f0bb 100644 --- a/packages/sdk/src/vdp/types.ts +++ b/packages/sdk/src/vdp/types.ts @@ -90,6 +90,7 @@ export const PipelineComponentMapSchema = z.record(z.any()); export type PipelineRunOnEventItem = { type: string; + event: string; }; export type PipelineRunOnEventMap = Record; diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index e17ca272b9..c257268da0 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@instill-ai/toolkit", - "version": "0.111.0-rc.1", + "version": "0.111.0-rc.2", "description": "Instill AI's frontend toolkit", "repository": "https://github.com/instill-ai/design-system.git", "bugs": "https://github.com/instill-ai/design-system/issues", diff --git a/packages/toolkit/src/constant/pipeline.ts b/packages/toolkit/src/constant/pipeline.ts index 9a92cd543c..d5eae904da 100644 --- a/packages/toolkit/src/constant/pipeline.ts +++ b/packages/toolkit/src/constant/pipeline.ts @@ -169,6 +169,15 @@ version: v1beta # # variable: +# Run on event +# Structure example: +# on: +# key: # Unique identifier for the variable. +# config: # The configuration for the event. +# setup: # The setup for the event. +# +# on: + # Custom user-defined output # Structure example: # output: diff --git a/packages/toolkit/src/lib/react-query-service/connector/useComponentDefinitions.ts b/packages/toolkit/src/lib/react-query-service/connector/useComponentDefinitions.ts index 45ac99bbaf..7c6438cce7 100644 --- a/packages/toolkit/src/lib/react-query-service/connector/useComponentDefinitions.ts +++ b/packages/toolkit/src/lib/react-query-service/connector/useComponentDefinitions.ts @@ -1,7 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { ComponentType, Nullable } from "instill-sdk"; -import { env } from "../../../server"; import { getInstillAPIClient } from "../../sdk-helper"; export function useComponentDefinitions({ @@ -24,7 +23,7 @@ export function useComponentDefinitions({ const connectorDefinitions = await client.vdp.component.listComponentDefinitions({ - pageSize: env("NEXT_PUBLIC_QUERY_PAGE_SIZE"), + pageSize: 100, filter: componentType !== "all" ? `componentType=${componentType}` diff --git a/packages/toolkit/src/lib/react-query-service/pipeline/useDeleteNamespacePipeline.ts b/packages/toolkit/src/lib/react-query-service/pipeline/useDeleteNamespacePipeline.ts index 1b79b21f5b..59c8246062 100644 --- a/packages/toolkit/src/lib/react-query-service/pipeline/useDeleteNamespacePipeline.ts +++ b/packages/toolkit/src/lib/react-query-service/pipeline/useDeleteNamespacePipeline.ts @@ -60,16 +60,6 @@ export function useDeleteNamespacePipeline() { shareCode: null, }), }); - - queryClient.removeQueries({ - queryKey: queryKeyStore.pipeline.getUseNamespacePipelineQueryKey({ - namespaceId, - pipelineId, - view: null, - shareCode: null, - }), - exact: true, - }); }, }); } diff --git a/packages/toolkit/src/view/pipeline/view-pipeline/PipelinePlayground.tsx b/packages/toolkit/src/view/pipeline/view-pipeline/PipelinePlayground.tsx index 7e58417e15..5690016dbe 100644 --- a/packages/toolkit/src/view/pipeline/view-pipeline/PipelinePlayground.tsx +++ b/packages/toolkit/src/view/pipeline/view-pipeline/PipelinePlayground.tsx @@ -42,7 +42,15 @@ import { useUserNamespaces, } from "../../../lib"; import { isArtifactRelatedInstillFormat } from "../../../lib/isArtifactRelatedInstillFormat"; -import { recursiveHelpers } from "../../pipeline-builder"; +import { + getReferencesFromString, + recursiveHelpers, +} from "../../pipeline-builder"; +import { VariableConnectToRunOnEvent } from "../../recipe-editor/input"; +import { + EventField, + listenWithType, +} from "../../recipe-editor/input/EventField"; import { RunButton } from "./RunButton"; const selector = (store: InstillStore) => ({ @@ -479,6 +487,55 @@ export const PipelinePlayground = ({ return true; }, [outputs]); + const variablesConnectToRunOnEvent = React.useMemo(() => { + const on = pipeline?.recipe?.on; + const variable = pipeline?.recipe?.variable; + + if (!on || !variable) { + return []; + } + + const variablesConnectToRunOnEvent: VariableConnectToRunOnEvent[] = []; + + Object.entries(variable).forEach(([key, value]) => { + if (!value) { + return; + } + + if (value.listen) { + const listensWithType: listenWithType[] = []; + + for (const listenItem of value.listen) { + const reference = getReferencesFromString(listenItem)[0]; + if (!reference) { + continue; + } + + // The referenceValue will looks like ${on.slack-0.message.text} + const referenceValueFrag = + reference.referenceValue.withoutCurlyBraces.split("."); + const eventKey = referenceValueFrag[1]; + const eventType = eventKey ? on[eventKey] : undefined; + + if (eventType) { + listensWithType.push({ + reference: listenItem, + type: eventType.type, + }); + } + } + + variablesConnectToRunOnEvent.push({ + listens: listensWithType, + key, + title: value.title, + }); + } + }); + + return variablesConnectToRunOnEvent; + }, [pipeline?.recipe?.on, pipeline?.recipe?.variable]); + if (!formSchema || !formSchema.input || !formSchema.output) { return ( ) : ( - -
-
{fields}
-
- {pipeline ? ( - - ) : ( -
- )} -
- - +
+
+ {variablesConnectToRunOnEvent.map((variable) => ( + + ))} +
+ +
+
{fields}
+
+ {pipeline ? ( + + ) : ( +
+ )} +
+ + +
) ) : ( diff --git a/packages/toolkit/src/view/recipe-editor/EditorViewBarItem.tsx b/packages/toolkit/src/view/recipe-editor/EditorViewBarItem.tsx index 3d8a311262..3470b7183b 100644 --- a/packages/toolkit/src/view/recipe-editor/EditorViewBarItem.tsx +++ b/packages/toolkit/src/view/recipe-editor/EditorViewBarItem.tsx @@ -60,9 +60,9 @@ export const EditorViewBarItem = ({
onClick(id)} diff --git a/packages/toolkit/src/view/recipe-editor/Output.tsx b/packages/toolkit/src/view/recipe-editor/Output.tsx index dc0e494439..8debbc24ee 100644 --- a/packages/toolkit/src/view/recipe-editor/Output.tsx +++ b/packages/toolkit/src/view/recipe-editor/Output.tsx @@ -23,97 +23,6 @@ export const Output = ({ }) => { const { triggerPipelineStreamMap } = useInstillStore(useShallow(selector)); - console.log(triggerPipelineStreamMap); - - // React.useEffect(() => { - // const downloadArtifact = async () => { - // if ( - // !triggerPipelineStreamMap?.pipeline?.output || - // !outputSchema?.properties || - // !accessToken - // ) { - // return; - // } - - // let output = structuredClone(triggerPipelineStreamMap?.pipeline?.output); - - // const downloadedFromArtifactKeys: string[] = []; - - // Object.entries(outputSchema.properties).forEach(([key, value]) => { - // if ( - // value?.instillFormat === "file" || - // value?.instillFormat === "array:file" || - // value?.instillFormat === "image" || - // value?.instillFormat === "array:image" || - // value?.instillFormat === "video" || - // value?.instillFormat === "array:video" || - // value?.instillFormat === "audio" || - // value?.instillFormat === "array:audio" - // ) { - // downloadedFromArtifactKeys.push(key); - // } - // }); - - // try { - // for (const key of downloadedFromArtifactKeys) { - // const targetValue = output[key]; - // if (!targetValue) { - // continue; - // } - - // if (Array.isArray(targetValue)) { - // const downloadedArtifacts: string[] = []; - // for (const item of targetValue) { - // if (isValidURL(item)) { - // const response = await downloadNamespaceObject.mutateAsync({ - // payload: { - // downloadUrl: item, - // }, - // accessToken, - // }); - - // if (!response.ok) { - // continue; - // } - - // const blob = await response.blob(); - // const url = URL.createObjectURL(blob); - // downloadedArtifacts.push(url); - // } - // } - // output[key] = downloadedArtifacts; - // } else { - // if (isValidURL(targetValue)) { - // const response = await downloadNamespaceObject.mutateAsync({ - // payload: { - // downloadUrl: targetValue, - // }, - // accessToken, - // }); - - // if (!response.ok) { - // continue; - // } - - // const blob = await response.blob(); - // const url = URL.createObjectURL(blob); - // output[key] = url; - // } - // } - - // console.log("output", output); - - // setOutputWithDownloadedArtifacts(output); - // } - // } catch (error) { - // setOutputWithDownloadedArtifacts(null); - // console.log(error); - // } - // }; - - // downloadArtifact(); - // }, [triggerPipelineStreamMap?.pipeline?.output, outputSchema, accessToken]); - const componentOutputFields = useComponentOutputFields({ mode: "build", schema: outputSchema, diff --git a/packages/toolkit/src/view/recipe-editor/RecipeEditorView.tsx b/packages/toolkit/src/view/recipe-editor/RecipeEditorView.tsx index 8026954610..44b00440f8 100644 --- a/packages/toolkit/src/view/recipe-editor/RecipeEditorView.tsx +++ b/packages/toolkit/src/view/recipe-editor/RecipeEditorView.tsx @@ -241,29 +241,52 @@ export const RecipeEditorView = () => { ) ?? []), ]; + let addGettingStartedView = false; + if ( pipelineIsNew && prev.topRight?.views.findIndex( (e) => e.id === DefaultEditorViewIDs.GETTING_STARTED, ) === -1 ) { + addGettingStartedView = true; topRightViews.push(getGettingStartedEditorView()); } return { topRight: { - views: topRightViews, + views: addGettingStartedView + ? [ + ...prev.topRight.views.filter( + (view) => + view.id !== DefaultEditorViewIDs.GETTING_STARTED && + view.id !== DefaultEditorViewIDs.MAIN_PREVIEW_FLOW, + ), + ...topRightViews, + getGettingStartedEditorView(), + ] + : [ + ...prev.topRight.views.filter( + (view) => view.id !== DefaultEditorViewIDs.MAIN_PREVIEW_FLOW, + ), + ...topRightViews, + ], currentViewId: pipelineIsNew ? DefaultEditorViewIDs.GETTING_STARTED : (prev.topRight?.currentViewId ?? DefaultEditorViewIDs.MAIN_PREVIEW_FLOW), }, main: { - views: [], + views: [...prev.main.views], currentViewId: null, }, bottomRight: { views: [ + ...prev.bottomRight.views.filter( + (view) => + view.id !== DefaultEditorViewIDs.MAIN_INPUT && + view.id !== DefaultEditorViewIDs.MAIN_OUTPUT, + ), { id: DefaultEditorViewIDs.MAIN_INPUT, title: "Input", diff --git a/packages/toolkit/src/view/recipe-editor/commands/ComponentCmdo.tsx b/packages/toolkit/src/view/recipe-editor/commands/ComponentCmdo.tsx index 8be1401346..0159b0b17e 100644 --- a/packages/toolkit/src/view/recipe-editor/commands/ComponentCmdo.tsx +++ b/packages/toolkit/src/view/recipe-editor/commands/ComponentCmdo.tsx @@ -379,6 +379,33 @@ export const ComponentCmdo = () => { setSelectedComponentDefinition(definition); if (isComponentDefinition(definition)) { + if ( + selectingComponentType === "event" && + definition.spec.eventSpecifications + ) { + const defaultEvent = Object.keys( + definition.spec.eventSpecifications, + )[0]; + + if (!defaultEvent) { + return; + } + + const doc = generateEventDefaultYamlString( + definition, + pipeline.data.recipe, + defaultEvent, + ); + + if (!doc) { + return; + } + + setSelectedComponentDefaultValue(doc); + setSelectedTaskName(defaultEvent); + return; + } + // set default task const defaultTask = definition.tasks[0]; diff --git a/packages/toolkit/src/view/recipe-editor/flow/nodes/EventMessage.tsx b/packages/toolkit/src/view/recipe-editor/flow/nodes/EventMessage.tsx new file mode 100644 index 0000000000..6b3326b3e8 --- /dev/null +++ b/packages/toolkit/src/view/recipe-editor/flow/nodes/EventMessage.tsx @@ -0,0 +1,36 @@ +import { CodeBlock } from "../../../../components"; + +export const EventMessage = ({ + id, + messageSnippet, +}: { + id: string; + messageSnippet: string; +}) => { + return ( +
+

+ {id} +

+

+ This is the fake message of this event. +

+ +
+ ); +}; diff --git a/packages/toolkit/src/view/recipe-editor/flow/nodes/NodeBase.tsx b/packages/toolkit/src/view/recipe-editor/flow/nodes/NodeBase.tsx index 5068e719fe..f3cd40e350 100644 --- a/packages/toolkit/src/view/recipe-editor/flow/nodes/NodeBase.tsx +++ b/packages/toolkit/src/view/recipe-editor/flow/nodes/NodeBase.tsx @@ -1,7 +1,7 @@ "use client"; import * as React from "react"; -import { Position } from "reactflow"; +import { Position, useStore } from "reactflow"; import { Button, cn, Icons, Tooltip } from "@instill-ai/design-system"; @@ -63,6 +63,8 @@ export const NodeBase = ({ customHandleClassName?: string; nodeClassName?: string; }) => { + const zoom = useStore((state) => state.transform[2]); + const { flowIsUnderDemoMode } = useInstillStore(useShallow(selector)); const isDisabledOpenDocumentationButton = React.useMemo(() => { @@ -185,7 +187,12 @@ export const NodeBase = ({ )} >
-

+

{id}

diff --git a/packages/toolkit/src/view/recipe-editor/flow/nodes/RunOnEventNode.tsx b/packages/toolkit/src/view/recipe-editor/flow/nodes/RunOnEventNode.tsx index 84e7362b57..b34957764d 100644 --- a/packages/toolkit/src/view/recipe-editor/flow/nodes/RunOnEventNode.tsx +++ b/packages/toolkit/src/view/recipe-editor/flow/nodes/RunOnEventNode.tsx @@ -5,7 +5,14 @@ import { NodeProps, useEdges } from "reactflow"; import { Icons } from "@instill-ai/design-system"; -import { InstillStore, useInstillStore, useShallow } from "../../../../lib"; +import { + InstillStore, + useComponentDefinitions, + useInstillStore, + useShallow, +} from "../../../../lib"; +import { RunOnEventNodeData } from "../types"; +import { EventMessage } from "./EventMessage"; import { NodeBase } from "./NodeBase"; const selector = (store: InstillStore) => ({ @@ -18,7 +25,9 @@ const selector = (store: InstillStore) => ({ updateSelectedComponentId: store.updateSelectedComponentId, }); -export const RunOnEventNode = ({ id, data }: NodeProps) => { +export const RunOnEventNode = ({ id, data }: NodeProps) => { + console.log("data", data); + const reactflowEdges = useEdges(); const hasTargetEdges = React.useMemo(() => { return reactflowEdges.some( @@ -36,12 +45,64 @@ export const RunOnEventNode = ({ id, data }: NodeProps) => { selectedComponentId, featureFlagWebhookEnabled, updateSelectedComponentId, + updateEditorMultiScreenModel, + enabledQuery, + accessToken, } = useInstillStore(useShallow(selector)); const isSelected = selectedComponentId === id; + const definitions = useComponentDefinitions({ + componentType: "all", + enabled: enabledQuery, + accessToken, + }); + const handleClick = React.useCallback(() => { updateSelectedComponentId(() => id); + + const viewId = `${id}-event-message`; + + const targetDefinition = definitions.data?.find( + (definition) => definition.id === data.type, + ); + + if (!targetDefinition) { + return; + } + + const eventSpec = targetDefinition.spec.eventSpecifications?.[data.event]; + + if (!eventSpec || !eventSpec.messageExamples[0]) { + return; + } + + updateEditorMultiScreenModel((prev) => ({ + ...prev, + bottomRight: { + ...prev.bottomRight, + views: [ + ...prev.bottomRight.views.filter((view) => view.id !== viewId), + { + id: viewId, + type: "output", + view: ( + + ), + title: viewId, + closeable: true, + }, + ], + currentViewId: viewId, + }, + })); }, [id, updateSelectedComponentId]); console.log("data", data);