PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.5
4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 All 203 releases
betterdocs / react-src / gutenberg / write-with-ai / index.js

index.js in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.5, at react-src/gutenberg/write-with-ai/index.js

85 lines 4.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // ===== write-with-ai/index.js — mounts the redesigned "Write with AI" modal =====
2 // Replaces the legacy inline PHP/jQuery template. Registers a hidden editor
3 // plugin that (a) injects the "Write with AI" button into the editor header and
4 // (b) renders the StudioGenerateModal via a portal.
5
6 import { __ } from "@wordpress/i18n";
7 import { registerPlugin } from "@wordpress/plugins";
8 import { useState, useEffect, useRef, createPortal } from "@wordpress/element";
9 import { subscribe } from "@wordpress/data";
10
11 import { AIMarkWhite } from "../ai-shared/kit";
12 import StudioGenerateModal from "./StudioGenerateModal";
13
14 // Header trigger button — styled with the shared kit tokens (wrapped in
15 // `.bd-ai-scope` because it is portaled into the WP editor header, outside the
16 // modal scope).
17 const HeaderButton = ({ onClick }) => (
18 <span className="bd-ai-scope" style={{ display: "inline-flex", marginRight: 8 }}>
19 <button type="button" onClick={onClick} className="bd-write-with-ai-button" style={{
20 display: "inline-flex", alignItems: "center", gap: 8,
21 background: "var(--green)", color: "#fff", border: "none",
22 height: 36, padding: "0 14px", borderRadius: 8, cursor: "pointer",
23 fontSize: 13, fontWeight: 700, whiteSpace: "nowrap",
24 boxShadow: "0 1px 0 rgba(0,0,0,.04), inset 0 -1px 0 rgba(0,0,0,.06)",
25 transition: "background .15s",
26 }}
27 onMouseEnter={(e) => { e.currentTarget.style.background = "var(--green-600)"; }}
28 onMouseLeave={(e) => { e.currentTarget.style.background = "var(--green)"; }}>
29 <span style={{ display: "flex" }}><AIMarkWhite size={16} /></span>
30 {__("Write with AI", "betterdocs")}
31 </button>
32 </span>
33 );
34
35 const findHeaderNode = () =>
36 document.querySelector(".edit-post-header__settings") ||
37 document.querySelector(".editor-header__settings");
38
39 const WriteWithAIApp = () => {
40 const [open, setOpen] = useState(false);
41 const [host, setHost] = useState(null);
42 const hostRef = useRef(null);
43 const headerRef = useRef(null);
44
45 // Watch the editor chrome until the header settings area exists, then mount
46 // the button there. We render into our own host node and PREPEND it (first
47 // child) so it sits beside the Git Sync button — matching master, where the
48 // legacy jQuery did `$('.…-header__settings').prepend(bdAIButton)`. A plain
49 // portal would append (far right) instead.
50 useEffect(() => {
51 let mounted = true;
52 if (!hostRef.current) {
53 const node = document.createElement("span");
54 // `betterdocs-ai-button-container` is the anchor Pro's Git Sync looks
55 // for: it inserts its button at `aiContainer.nextSibling`, so this
56 // node ends up directly to the LEFT of the Git Sync button (master
57 // parity — the legacy free button used the same wrapper class).
58 node.className = "betterdocs-ai-button-container bd-write-with-ai-host";
59 hostRef.current = node;
60 }
61 const sync = () => {
62 const header = findHeaderNode();
63 if (!header || header === headerRef.current) return;
64 headerRef.current = header;
65 header.prepend(hostRef.current);
66 if (mounted && !host) setHost(hostRef.current);
67 };
68 sync();
69 const unsubscribe = subscribe(() => { window.requestAnimationFrame(sync); });
70 return () => { mounted = false; unsubscribe(); };
71 }, [host]);
72
73 return (
74 <>
75 {host && createPortal(<HeaderButton onClick={() => setOpen(true)} />, host)}
76 <StudioGenerateModal open={open} onClose={() => setOpen(false)} />
77 </>
78 );
79 };
80
81 // Registered unconditionally — the render slot (PluginArea) only mounts inside
82 // the block editor, so this is inert on the classic editor. The bundle itself is
83 // enqueued by PHP only on the `docs` editor screen.
84 registerPlugin("betterdocs-write-with-ai", { render: WriteWithAIApp });
85