PluginProbe
Extendify / 3.2.1
Extendify v3.2.1
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 +57 -76 3.0.43.2.1 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';
4 +import { useQuickEditStore } from '@quick-edit/state/store';
5 5 import { deepMerge } from '@shared/lib/utils';
6 -import apiFetch from '@wordpress/api-fetch';
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,24 +15,23 @@
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,
29 - block: null, // data-extendify-agent-block-id + details about the block
30 - setBlock: (block) => set({ block, blockCode: null }),
31 - // Used as "previousContent" in workflows that need it
32 - blockCode: null,
33 - setBlockCode: (blockCode) => set({ blockCode }),
34 - domToolEnabled: false,
35 - setDomToolEnabled: (enabled) => {
36 - if (get().block) return; // can't disable if a block is set
37 - set({ domToolEnabled: enabled });
38 - },
39 19 getWorkflow: () => {
40 20 const curr = get().workflow;
41 21 // Workflows may define a "parent" workflow via templateId
42 22 const currId = curr?.templateId || curr?.id;
43 23 const wf = workflows.find(({ id }) => id === currId);
44 - if (!wf?.id) return curr || null;
24 + if (!wf?.id) {
25 + if (!curr) return null;
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 };
33 + }
45 34 return { ...deepMerge(curr, wf || {}), id: curr?.id };
46 35 },
47 36 getWorkflowByExample: (example) => {
48 37 const wf = workflows.find(
@@ -48,8 +37,9 @@
48 37 const wf = workflows.find(
49 38 ({ example: ex }) => collator.compare(ex?.text, example) === 0,
50 39 );
51 40 if (!wf?.id) return null;
41 + if (!wf.available()) return null;
52 42 return wf;
53 43 },
54 44 // Gets the workflows available to the user
55 45 // TODO: maybe we need to have a way to include a
@@ -59,9 +49,9 @@
59 49 // If a block is set, only include those with 'block'
60 50 const blockWorkflows = wfs.filter(({ requires }) =>
61 51 requires?.includes('block'),
62 52 );
63 - if (get().block) return blockWorkflows;
53 + if (useQuickEditStore.getState().agentBlock) return blockWorkflows;
64 54 // otherwise remove all of the above
65 55 return wfs.filter(({ id }) => !blockWorkflows.some((w) => w.id === id));
66 56 },
67 57 getWorkflowsByFeature: ({ requires } = {}) => {
@@ -73,37 +63,8 @@
73 63 (!requires || workflowRequires?.some((s) => requires.includes(s))),
74 64 );
75 65 },
76 66 workflowData: null,
77 - // This is the history of the results
78 - // { answerId: '', canceled: false, reason: '', error: false, completed: false, whenFinishedTool: null }[]
79 - workflowHistory: window.extAgentData?.workflowHistory || [],
80 - addWorkflowResult: (data) => {
81 - const workflowId = get().workflow?.id;
82 -
83 - if (data.status === 'completed') {
84 - set((state) => {
85 - const max = Math.max(0, state.workflowHistory.length - 10);
86 -
87 - return {
88 - workflowHistory: [
89 - { workflowId, ...data },
90 - ...state.workflowHistory.toSpliced(0, max),
91 - ],
92 - };
93 - });
94 - }
95 -
96 - if (!workflowId) return;
97 - // Persist it to the server
98 - const path = '/extendify/v1/agent/workflows';
99 - apiFetch({
100 - method: 'POST',
101 - keepalive: true,
102 - path,
103 - data: { workflowId, ...data },
104 - });
105 - },
106 67 mergeWorkflowData: (data) => {
107 68 set((state) => {
108 69 if (!state.workflowData) return { workflowData: data };
109 70 return {
@@ -110,22 +71,43 @@
110 71 workflowData: { ...state.workflowData, ...data },
111 72 };
112 73 });
113 74 },
114 - setWorkflow: (workflow) =>
75 + setWorkflow: (workflow) => {
76 + const agentBlockCode = useQuickEditStore.getState().agentBlockCode;
77 + const started = workflow
78 + ? { ...workflow, startingPage: window.location.href }
79 + : null;
115 80 set({
116 - workflow: workflow
117 - ? { ...workflow, startingPage: window.location.href }
118 - : null,
81 + workflow: started,
119 82 // If a block is selected, add it to the workflow data
120 83 // previousContent is named this way for legacy reasons
121 - workflowData: get().blockCode
122 - ? { previousContent: get().blockCode }
123 - : null,
84 + workflowData: agentBlockCode ? { previousContent: agentBlockCode } : null,
124 85 whenFinishedToolProps: null,
125 - }),
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 + }
99 + },
126 100 setWhenFinishedToolProps: (whenFinishedToolProps) =>
127 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 + ),
128 110 });
129 111
130 112 export const useWorkflowStore = create()(
131 113 persist(devtools(state, { name: 'Extendify Agent Workflows' }), {
@@ -130,23 +112,22 @@
130 112 export const useWorkflowStore = create()(
131 113 persist(devtools(state, { name: 'Extendify Agent Workflows' }), {
132 114 name: `extendify-agent-workflows-${window.extSharedData.siteId}`,
133 115 merge: (persistedState, currentState) => {
134 - // if we are in onboarding mode, add the starting workflow
135 - 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) {
136 123 return {
137 - ...currentState,
138 - ...persistedState,
139 - workflow: isChangeSiteDesignWorkflowAvailable()
140 - ? changeSiteDesignWorkflow
141 - : variationsWorkflow,
142 - whenFinishedToolProps: onboardingToolProps,
124 + ...merged,
125 + workflow: null,
126 + whenFinishedToolProps: null,
127 + workflowData: null,
143 128 };
144 129 }
145 - return { ...currentState, ...persistedState };
146 - },
147 - partialize: (state) => {
148 - const { block, workflowHistory, ...rest } = state;
149 - return { ...rest };
130 + return merged;
150 131 },
151 132 }),
152 133 );