Files
wp-agentic-writer/MIGRATION_GUIDE.md
Dwindi Ramadhana 690991c526 refactor: Cleanup git state - commit all staged changes
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.
2026-06-17 05:27:58 +07:00

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

  1. Copy lines X-Y from sidebar.js
  2. Convert wp.element.createElement() to JSX
  3. 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-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

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

  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