# WP Agentic Writer - Migration Guide **Source:** `assets/js/sidebar.js` (12,363 lines) **Goal:** 1:1 mechanical translation --- ## The Migration is Mechanical 1. Copy lines X-Y from `sidebar.js` 2. Convert `wp.element.createElement()` to JSX 3. Done --- ## JSX Conversion Rules ```jsx // sidebar.js (verbatim copy) wp.element.createElement('div', { className: 'wpaw-status-icon-btn', onClick: handleClick, style: { display: 'flex', alignItems: 'center' } }, 'text'); // Convert to JSX
text
``` | sidebar.js | JSX | |-----------|-----| | `{ key: value }` | `{ value }` | | `{ className: 'foo' }` | `className="foo"` | | `{ style: { x: y } }` | `style={{ x: y }}` | | `null` children | `null` | --- ## Phase 1: ENTRY POINT ### `src/index.jsx` (NEW) ```jsx import { createElement } from "@wordpress/element"; import { registerPlugin } from "@wordpress/plugins"; import { PluginSidebar, PluginSidebarMoreMenuItem } from "@wordpress/edit-post"; import { useSelect } from "@wordpress/data"; import { AgenticWriterSidebar } from "./components/Sidebar"; const pluginIcon = createElement("img", { src: (window.wpAgenticWriter?.pluginUrl || "") + "/assets/img/icon.svg", alt: "WP Agentic Writer", style: { width: "20px", height: "20px" }, }); function WPAWPlugin() { const postId = useSelect( (select) => select("core/editor")?.getCurrentPostId() || 0, [] ); return createElement( createElement.Fragment, null, createElement(PluginSidebarMoreMenuItem, { target: "wp-agentic-writer", icon: pluginIcon, }, "WP Agentic Writer"), createElement(PluginSidebar, { name: "wp-agentic-writer", title: "WP Agentic Writer", icon: pluginIcon, }, createElement(AgenticWriterSidebar, { postId })) ); } registerPlugin("wp-agentic-writer", { icon: pluginIcon, render: WPAWPlugin, }); ``` --- ## Phase 2: MAIN COMPONENT (Keep as Monolith First) ### `src/components/Sidebar.jsx` **Lines from sidebar.js: L38 - L12363** Copy the entire `AgenticWriterSidebar` component verbatim, then convert createElement to JSX. --- ## Phase 3: Extract Helpers (Pure Functions) After main component works, extract these as standalone modules: ### `src/helpers/logging.js` **Lines: L17-136** - `formatAiErrorMessage()` ### `src/helpers/api.js` **Lines: L380-490, L511-538, L540-549, L1195-1562, L1782-1837, L1890-2024, L2074-2207, L2301-2397, L2398-2514, L3633-3696** Functions: - `savePostConfig()` L402-447 - `saveWritingState()` L511-538 - `persistWritingStatePatch()` L540-549 - `runSeoAudit()` L1195-1268 - `generateMetaDescription()` L1460-1562 - `persistSessionMessages()` L1782-1837 - `acquireSessionLock()` L1890-1925 - `releaseSessionLock()` L1927-1946 - `loadChatHistory()` L2074-2207 - `loadPostSessions()` L2301-2397 - `openSessionById()` L2398-2514 - `loadSectionBlocks()` L3633-3673 - `saveSectionBlocks()` L3674-3696 ### `src/helpers/session.js` **Lines: L1640-1753, L1755-1883, L2043-2207, L2301-2514, L8830-9002** Functions: - `updateSnapshot()` L1640-1648 - `sanitizeMessagesForStorage()` L1717-1753 - `hydrateSessionStateFromMessages()` L1755-1780 - `flushOnUnload()` L1847-1883 - `loadChatHistory()` L2074-2207 - `loadPostSessions()` L2301-2397 - `openSessionById()` L2398-2514 - `startNewConversation()` L8830-8915 - `deleteConversationSession()` L8917-8958 - `getSessionDisplayTitle()` L8960-8983 ### `src/helpers/editor.js` **Lines: L753-811, L843-968, L1020-1028** Functions: - `captureEditorSnapshot()` L753-763 - `pushUndoSnapshot()` L765-774 - `undoLastAiOperation()` L776-811 - `blockEditorDispatch()` L843 - `shouldBlockEditorInput()` L927-943 - `keydownHandler()` L945-960 - `blockMutationEvent()` L962-968 - `toTextValue()` L1020-1028 ### `src/helpers/timeline.js` **Lines: L586-673** Functions: - `updateOrCreateTimelineEntry()` L637-659 - `addActivityTimeline()` L660-673 ### `src/helpers/operations.js` **Lines: L674-750** Functions: - `setActiveOperationState()` L674-682 - `beginAgentOperation()` L683-691 - `finishAgentOperation()` L692-700 - `markActiveOperationStopping()` L701-721 - `requestRefineAllConfirmation()` L729-742 - `resolveRefineAllConfirmation()` L743-750 ### `src/helpers/keywords.js` **Lines: L1072-1157, L4097-4155** Functions: - `handleFocusKeywordChange()` L1072-1078 - `handleKeywordSelect()` L1080-1087 - `extractFocusKeywordSuggestions()` L1090-1135 - `addFocusKeywordSuggestions()` L1154-1157 - `suggestKeywordsFromPlan()` L4097-4155 ### `src/helpers/seo.js` **Lines: L1195-1347** Functions: - `runSeoAudit()` L1195-1268 - `buildSeoAuditFixInstruction()` L1270-1308 - `getSeoFixKey()` L1309-1310 - `buildAuditRefinementContext()` L1329-1347 ### `src/helpers/refinement.js` **Lines: L1349-1458, L2570-2689, L4954-5279** Functions: - `handleSeoAuditFix()` L1349-1458 - `handleTitleRefinement()` L2570-2689 - `reformatBlocks()` L4954-5056 - `applyEditPlan()` L5143-5240 - `cancelEditPlan()` L5241-5279 ### `src/helpers/blocks.js` **Lines: L4871-5050, L5363-5420** Functions: - `createBlocksFromSerialized()` L4871-4953 - `getRefineableBlocks()` L5363-5390 - `getListItemBlocks()` L5391-5420 ### `src/helpers/blocks-context.js` **Lines: L5443-5622** Functions: - `getBlockContentForContext()` L5443-5453 - `getHeadingContextForBlock()` L5454-5470 - `getNearbyParagraphContext()` L5471-5494 - `getContextFromMentions()` L5495-5503 - `selectLikelySlangBlocks()` L5521-5533 - `getAiSlopFindingsForBlock()` L5538-5589 - `buildContextBlocksForRefinement()` L5607-5622 - `buildRefinementDiagnosis()` L5623-5703 ### `src/helpers/plan.js` **Lines: L3474-3891, L4321-4767** Functions: - `createBlockFromPlan()` L3474-3534 - `normalizePlanActions()` L3535-3543 - `buildPlanPreviewItem()` L3544-3604 - `upsertSectionBlock()` L3612-3622 - `removeSectionBlock()` L3623-3632 - `findBestPlanSectionMatch()` L3744-3828 - `updatePlanSectionStatus()` L3829-3851 - `findSectionInsertIndex()` L3852-3891 - `executePlanFromCard()` L4321-4767 ### `src/helpers/agent.js` **Lines: L3907-4320** Functions: - `summarizeChatHistory()` L3907-3954 - `detectUserIntent()` L3957-4005 - `buildOptimizedContext()` L4008-4025 - `classifyAgentIntent()` L4222-4260 - `decideAgentAction()` L4261-4320 - `getPlanRuntimeSummary()` L4199-4218 ### `src/helpers/mentions.js` **Lines: L2516-2569, L5705-5797, L6454-6605, L6613-6757** Functions: - `resolveStreamTarget()` L2516-2526 - `normalizeMentionToken()` L2527-2536 - `extractMentionsFromText()` L2537-2550 - `resolveBlockMentions()` L5705-5797 - `getMentionOptions()` L6456-6578 - `handleInsertMention()` L6581-6605 - `insertMention()` L6702-6726 ### `src/helpers/commands.js` **Lines: L2688-2758** Functions: - `parseInsertCommand()` L2690-2712 - `getSlashOptions()` L2713-2747 - `getBlockIndex()` L2748-2758 ### `src/helpers/chat.js` **Lines: L6759-8054, L8057-8474** Functions: - `sendMessage()` L6759-8054 (the main chat function) - `submitAnswers()` L8057-8474 ### `src/helpers/markdown.js` **Lines: L10007-10270** Functions: - `escapeHtml()` L10007-10014 - `inlineMarkdownToHtml()` L10015-10031 - `markdownToHtml()` L10032-10270 ### `src/helpers/welcome.js` **Lines: L1176-1192** Functions: - `handleWelcomeStart()` L1176-1192 ### `src/helpers/retry.js` **Lines: L3189-3257** Functions: - `retryLastGeneration()` L3189-3213 - `retryLastExecute()` L3214-3228 - `retryLastRefinement()` L3229-3257 --- ## Phase 4: State Objects (Plain Objects, Not React State) ### `src/state/config.js` **Lines: L163-222** ```js const defaultPostConfig = { article_length: 'medium', language: 'auto', tone: '', audience: '', experience_level: 'general', include_images: true, web_search: false, default_mode: 'chat', seo_focus_keyword: '', focus_keyword: '', seo_secondary_keywords: [], seo_meta_description: '', seo_enabled: false, }; const applyProviderMetadata = (config, providerInfo) => { ... }; ``` ### `src/state/editor.js` **Lines: L224-237** ```js const isEditorLocked = false; const isRefinementLocked = false; const refiningBlockIds = []; const refineAllConfirm = { isOpen: false, blockCount: 0, dontAskAgain: false }; ``` --- ## Extraction Order 1. [ ] Keep everything in `Sidebar.jsx` first 2. [ ] Verify build works 3. [ ] Then extract helpers one by one --- ## Verification - [ ] Build compiles - [ ] Plugin loads in Gutenberg - [ ] All buttons work - [ ] No console errors