PluginProbe
Extendify / 3.2.2
Extendify v3.2.2
3.2.2 3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 All 128 releases
← All changes | src/Agent/state/workflows.js +53 -26 3.1.0 → 3.2.2 View file →
@@ -1,22 +1,12 @@
1 -import { isChangeSiteDesignWorkflowAvailable } from '@agent/lib/util';
2 -import changeSiteDesignWorkflow from '@agent/workflows/theme/change-site-design';
3 -import variationsWorkflow from '@agent/workflows/theme/change-theme-variation';
4 -import { workflows } from '@agent/workflows/workflows';
1 +import { isAbilityWorkflow } from '@agent/lib/abilities';
2 +import { AbilityRun } from '@agent/workflows/abilities/components/run';
3 +import { abilityWorkflows, workflows } from '@agent/workflows/workflows';
5 4 import { useQuickEditStore } from '@quick-edit/state/store';
6 5 import { deepMerge } from '@shared/lib/utils';
7 6 import { create } from 'zustand';
8 7 import { devtools, persist } from 'zustand/middleware';
9 8
10 -const onboarding = window.extAgentData.chatHistory?.length === 0;
11 -const agentResponse = isChangeSiteDesignWorkflowAvailable()
12 - ? changeSiteDesignWorkflow.example?.agentResponse
13 - : variationsWorkflow.example?.agentResponse;
14 -const onboardingToolProps = {
15 - ...agentResponse.whenFinishedTool,
16 - agentResponse,
17 -};
18 -
19 9 // Used to check case-insensitive matches for workflow examples
20 10 const collator = new Intl.Collator(undefined, {
21 11 sensitivity: 'base',
22 12 usage: 'search',
@@ -25,14 +15,25 @@
25 15 const state = (set, get) => ({
26 16 workflow: null,
27 17 // Data for the tool component that shows up at the end of a workflow
28 18 whenFinishedToolProps: null,
19 + // A staged tool restored from storage; its confirm never renders.
20 + reloadedToolProps: null,
29 21 getWorkflow: () => {
30 22 const curr = get().workflow;
31 23 // Workflows may define a "parent" workflow via templateId
32 24 const currId = curr?.templateId || curr?.id;
33 25 const wf = workflows.find(({ id }) => id === currId);
34 - if (!wf?.id) return curr || null;
26 + if (!wf?.id) {
27 + if (!curr) return null;
28 + if (!isAbilityWorkflow(curr.id)) return curr;
29 + // The backend never sends whenFinished, so this supplies the gate.
30 + const ability = deepMerge(
31 + { whenFinished: { component: AbilityRun } },
32 + abilityWorkflows[curr.id] ?? {},
33 + );
34 + return { ...deepMerge(curr, ability), id: curr.id };
35 + }
35 36 return { ...deepMerge(curr, wf || {}), id: curr?.id };
36 37 },
37 38 getWorkflowByExample: (example) => {
38 39 const wf = workflows.find(
@@ -38,8 +39,9 @@
38 39 const wf = workflows.find(
39 40 ({ example: ex }) => collator.compare(ex?.text, example) === 0,
40 41 );
41 42 if (!wf?.id) return null;
43 + if (!wf.available()) return null;
42 44 return wf;
43 45 },
44 46 // Gets the workflows available to the user
45 47 // TODO: maybe we need to have a way to include a
@@ -73,20 +75,41 @@
73 75 });
74 76 },
75 77 setWorkflow: (workflow) => {
76 78 const agentBlockCode = useQuickEditStore.getState().agentBlockCode;
79 + const started = workflow
80 + ? { ...workflow, startingPage: window.location.href }
81 + : null;
77 82 set({
78 - workflow: workflow
79 - ? { ...workflow, startingPage: window.location.href }
80 - : null,
83 + workflow: started,
81 84 // If a block is selected, add it to the workflow data
82 85 // previousContent is named this way for legacy reasons
83 86 workflowData: agentBlockCode ? { previousContent: agentBlockCode } : null,
84 87 whenFinishedToolProps: null,
85 88 });
89 + if (!started) return;
90 + const workflowStarted = get().getWorkflow();
91 + window.dispatchEvent(
92 + new CustomEvent('extendify-workflow::started', {
93 + detail: workflowStarted,
94 + }),
95 + );
96 + try {
97 + workflowStarted?.onStarted?.(workflowStarted);
98 + } catch {
99 + // A workflow that can't read the page still has to start.
100 + }
86 101 },
87 102 setWhenFinishedToolProps: (whenFinishedToolProps) =>
88 103 set({ whenFinishedToolProps }),
104 + // Declared up front it would hide the workflow while nothing is staged.
105 + // Not setWorkflow: that resets workflowData and drops the run's data.
106 + requireBlock: () =>
107 + set((state) =>
108 + state.workflow
109 + ? { workflow: { ...state.workflow, requires: ['block'] } }
110 + : {},
111 + ),
89 112 });
90 113
91 114 export const useWorkflowStore = create()(
92 115 persist(devtools(state, { name: 'Extendify Agent Workflows' }), {
@@ -91,19 +114,23 @@
91 114 export const useWorkflowStore = create()(
92 115 persist(devtools(state, { name: 'Extendify Agent Workflows' }), {
93 116 name: `extendify-agent-workflows-${window.extSharedData.siteId}`,
94 117 merge: (persistedState, currentState) => {
95 - // if we are in onboarding mode, add the starting workflow
96 - if (onboarding) {
118 + const merged = { ...currentState, ...persistedState };
119 + // A reload can't carry the staged block a block-patching workflow
120 + // depends on, so a rehydrated-open one is always stale. A canvas
121 + // session is stale too: its field values never persist.
122 + const id = merged.workflow?.templateId || merged.workflow?.id;
123 + const registered = workflows.find((wf) => wf.id === id);
124 + if (id === 'block-patching' || registered?.whenFinished?.canvas) {
97 125 return {
98 - ...currentState,
99 - ...persistedState,
100 - workflow: isChangeSiteDesignWorkflowAvailable()
101 - ? changeSiteDesignWorkflow
102 - : variationsWorkflow,
103 - whenFinishedToolProps: onboardingToolProps,
126 + ...merged,
127 + workflow: null,
128 + whenFinishedToolProps: null,
129 + workflowData: null,
104 130 };
105 131 }
106 - return { ...currentState, ...persistedState };
132 + return { ...merged, reloadedToolProps: merged.whenFinishedToolProps };
107 133 },
134 + partialize: ({ reloadedToolProps, ...persisted }) => persisted,
108 135 }),
109 136 );