Major refactoring cleanup: - Add new controller architecture (class-controller-*.php) - Add new settings-v2 UI (views/settings-v2/) - Add new CSS architecture (agentic-sidebar.css, tokens) - Add esbuild build pipeline (scripts/build.js, package.json) - Add composer dependencies (vendor/) - Add frontend src directory (assets/js/src/index.jsx) - Add documentation files - Remove old/obsolete files (class-settings.php, old CSS) This commits all pending changes from previous refactoring efforts.
8.3 KiB
WP Agentic Writer - Migration Guide
Source: assets/js/sidebar.js (12,363 lines)
Goal: 1:1 mechanical translation
The Migration is Mechanical
- Copy lines X-Y from
sidebar.js - Convert
wp.element.createElement()to JSX - Done
JSX Conversion Rules
// sidebar.js (verbatim copy)
wp.element.createElement('div', {
className: 'wpaw-status-icon-btn',
onClick: handleClick,
style: { display: 'flex', alignItems: 'center' }
}, 'text');
// Convert to JSX
<div
className="wpaw-status-icon-btn"
onClick={handleClick}
style={{ display: 'flex', alignItems: 'center' }}
>
text
</div>
| 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)
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-447saveWritingState()L511-538persistWritingStatePatch()L540-549runSeoAudit()L1195-1268generateMetaDescription()L1460-1562persistSessionMessages()L1782-1837acquireSessionLock()L1890-1925releaseSessionLock()L1927-1946loadChatHistory()L2074-2207loadPostSessions()L2301-2397openSessionById()L2398-2514loadSectionBlocks()L3633-3673saveSectionBlocks()L3674-3696
src/helpers/session.js
Lines: L1640-1753, L1755-1883, L2043-2207, L2301-2514, L8830-9002
Functions:
updateSnapshot()L1640-1648sanitizeMessagesForStorage()L1717-1753hydrateSessionStateFromMessages()L1755-1780flushOnUnload()L1847-1883loadChatHistory()L2074-2207loadPostSessions()L2301-2397openSessionById()L2398-2514startNewConversation()L8830-8915deleteConversationSession()L8917-8958getSessionDisplayTitle()L8960-8983
src/helpers/editor.js
Lines: L753-811, L843-968, L1020-1028
Functions:
captureEditorSnapshot()L753-763pushUndoSnapshot()L765-774undoLastAiOperation()L776-811blockEditorDispatch()L843shouldBlockEditorInput()L927-943keydownHandler()L945-960blockMutationEvent()L962-968toTextValue()L1020-1028
src/helpers/timeline.js
Lines: L586-673
Functions:
updateOrCreateTimelineEntry()L637-659addActivityTimeline()L660-673
src/helpers/operations.js
Lines: L674-750
Functions:
setActiveOperationState()L674-682beginAgentOperation()L683-691finishAgentOperation()L692-700markActiveOperationStopping()L701-721requestRefineAllConfirmation()L729-742resolveRefineAllConfirmation()L743-750
src/helpers/keywords.js
Lines: L1072-1157, L4097-4155
Functions:
handleFocusKeywordChange()L1072-1078handleKeywordSelect()L1080-1087extractFocusKeywordSuggestions()L1090-1135addFocusKeywordSuggestions()L1154-1157suggestKeywordsFromPlan()L4097-4155
src/helpers/seo.js
Lines: L1195-1347
Functions:
runSeoAudit()L1195-1268buildSeoAuditFixInstruction()L1270-1308getSeoFixKey()L1309-1310buildAuditRefinementContext()L1329-1347
src/helpers/refinement.js
Lines: L1349-1458, L2570-2689, L4954-5279
Functions:
handleSeoAuditFix()L1349-1458handleTitleRefinement()L2570-2689reformatBlocks()L4954-5056applyEditPlan()L5143-5240cancelEditPlan()L5241-5279
src/helpers/blocks.js
Lines: L4871-5050, L5363-5420
Functions:
createBlocksFromSerialized()L4871-4953getRefineableBlocks()L5363-5390getListItemBlocks()L5391-5420
src/helpers/blocks-context.js
Lines: L5443-5622
Functions:
getBlockContentForContext()L5443-5453getHeadingContextForBlock()L5454-5470getNearbyParagraphContext()L5471-5494getContextFromMentions()L5495-5503selectLikelySlangBlocks()L5521-5533getAiSlopFindingsForBlock()L5538-5589buildContextBlocksForRefinement()L5607-5622buildRefinementDiagnosis()L5623-5703
src/helpers/plan.js
Lines: L3474-3891, L4321-4767
Functions:
createBlockFromPlan()L3474-3534normalizePlanActions()L3535-3543buildPlanPreviewItem()L3544-3604upsertSectionBlock()L3612-3622removeSectionBlock()L3623-3632findBestPlanSectionMatch()L3744-3828updatePlanSectionStatus()L3829-3851findSectionInsertIndex()L3852-3891executePlanFromCard()L4321-4767
src/helpers/agent.js
Lines: L3907-4320
Functions:
summarizeChatHistory()L3907-3954detectUserIntent()L3957-4005buildOptimizedContext()L4008-4025classifyAgentIntent()L4222-4260decideAgentAction()L4261-4320getPlanRuntimeSummary()L4199-4218
src/helpers/mentions.js
Lines: L2516-2569, L5705-5797, L6454-6605, L6613-6757
Functions:
resolveStreamTarget()L2516-2526normalizeMentionToken()L2527-2536extractMentionsFromText()L2537-2550resolveBlockMentions()L5705-5797getMentionOptions()L6456-6578handleInsertMention()L6581-6605insertMention()L6702-6726
src/helpers/commands.js
Lines: L2688-2758
Functions:
parseInsertCommand()L2690-2712getSlashOptions()L2713-2747getBlockIndex()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-10014inlineMarkdownToHtml()L10015-10031markdownToHtml()L10032-10270
src/helpers/welcome.js
Lines: L1176-1192
Functions:
handleWelcomeStart()L1176-1192
src/helpers/retry.js
Lines: L3189-3257
Functions:
retryLastGeneration()L3189-3213retryLastExecute()L3214-3228retryLastRefinement()L3229-3257
Phase 4: State Objects (Plain Objects, Not React State)
src/state/config.js
Lines: L163-222
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
const isEditorLocked = false;
const isRefinementLocked = false;
const refiningBlockIds = [];
const refineAllConfirm = { isOpen: false, blockCount: 0, dontAskAgain: false };
Extraction Order
- Keep everything in
Sidebar.jsxfirst - Verify build works
- Then extract helpers one by one
Verification
- Build compiles
- Plugin loads in Gutenberg
- All buttons work
- No console errors