PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
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
extendify / src / QuickEdit / lib / ask-ai.js

ask-ai.js in Extendify 3.1.5, at src/QuickEdit/lib/ask-ai.js

83 lines 2.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { useQuickEditStore } from '../state/store';
2 import { buildAgentBlockDescriptor } from './agent-block-descriptor';
3
4 const AGENT_ATTR = 'data-extendify-agent-block-id';
5 const AGENT_PART_ID = 'data-extendify-part-block-id';
6
7 export const isAgentAvailable = () => !!window.extAgentData;
8
9 export const hasAgentBlockSelected = () => {
10 if (!isAgentAvailable()) return false;
11 return !!useQuickEditStore.getState().agentBlock;
12 };
13
14 // listener(hasBlock: boolean). Returns an unsubscribe fn.
15 export const subscribeToAgentBlock = (listener) => {
16 if (!isAgentAvailable()) return () => {};
17 let prev = !!useQuickEditStore.getState().agentBlock;
18 return useQuickEditStore.subscribe((state) => {
19 const next = !!state.agentBlock;
20 if (next !== prev) {
21 prev = next;
22 listener(next);
23 }
24 });
25 };
26
27 const findAgentTagged = (el) => {
28 let node = el;
29 while (node && node !== document.body) {
30 if (node.hasAttribute?.(AGENT_ATTR) || node.hasAttribute?.(AGENT_PART_ID)) {
31 return node;
32 }
33 node = node.parentElement;
34 }
35 return null;
36 };
37
38 // Focus the agent chat textarea so the user can type their request right
39 // away. The textarea is created when the panel mounts; the brief retry
40 // covers the open-from-closed case where it isn't in the DOM yet (when the
41 // sidebar is already open it's found on the first try). preventScroll keeps
42 // the block the user just clicked in view rather than yanking to the chat.
43 const focusChatInput = (tries = 20) => {
44 // Pinning a block puts focus on its pill; a late retry would yank it away.
45 if (document.activeElement?.closest?.('.extendify-quick-edit-bar')) return;
46 const ta = document.querySelector('#extendify-agent-chat-textarea');
47 if (ta) {
48 ta.focus({ preventScroll: true });
49 return;
50 }
51 if (tries > 0) setTimeout(() => focusChatInput(tries - 1), 50);
52 };
53
54 export const askAiAboutElement = async (el) => {
55 if (!isAgentAvailable()) return;
56
57 const match = findAgentTagged(el);
58
59 // setBlock before setOpen so DOMHighlighter sees the block on mount.
60 const { useGlobalStore } = await import('@agent/state/global');
61 const { useEditModeStore } = await import('@quick-edit/state/edit-mode');
62
63 if (match) {
64 // Edit mode is the gate that mounts DOMHighlighter — turn it on so
65 // the X-close indicator renders alongside the selection. Skip
66 // re-setting agentBlock when the user is re-clicking Ask AI on
67 // the already-staged block: pushing a new descriptor would churn
68 // DOMHighlighter and any workflow that pinned the block.
69 useEditModeStore.getState().setOn(true);
70 const next = buildAgentBlockDescriptor(match);
71 const current = useQuickEditStore.getState().agentBlock;
72 if (!current || current.id !== next.id || current.target !== next.target) {
73 useQuickEditStore.setState({
74 agentBlock: next,
75 agentBlockCode: null,
76 });
77 }
78 }
79 useGlobalStore.getState().setOpen(true);
80
81 focusChatInput();
82 };
83