# 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
<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)
```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
