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