{"id":"69167a26adcd0bfee0252037","title":"2025 10 25 Issue 67","path":"how-to/committee/release-notes/2025-10-25-issue-67","contentMarkdown":"# 25th October 2025 — Optimized HTML/Markdown Paste Pipeline [#67](https://github.com/nbarrett/ngx-ramblers/issues/67)\n## [build 321](https://github.com/nbarrett/ngx-ramblers/actions/runs/18803315196) — [#67](https://github.com/nbarrett/ngx-ramblers/issues/67) — [commit 07644ee](https://github.com/nbarrett/ngx-ramblers/commit/07644ee)\n\nDramatically improved paste experience with client-side HTML detection, server-side processing enhancements, URL auto-fetch, and admin clipboard tools.\n\n**Where to Find It**\n\n- **Markdown Editor**: Paste HTML content, toolbar \"Convert to Rows\" button\n- **Admin → Migration Settings**: Paste/Copy buttons for site configuration JSON\n- **Fragment Index**: Recursive detection of nested row usage\n\n**Benefits**\n\n- **Instant Plain Pastes**: Client filters trivial HTML/browser wrappers before server call\n- **Cleaner Rows**: Enhanced preprocessing removes artifacts, produces better Markdown\n- **Smart URL Handling**: Paste view-source:// or http(s):// URLs to auto-fetch and convert pages\n- **Base URL Suggestions**: Dialog prompts with migration config URLs for relative link resolution\n- **Nested Content Support**: Choose nested/sibling placement with smart column-count defaults\n- **Admin Efficiency**: Copy/paste site config JSON directly in migration settings\n- **Fragment Awareness**: Recursive detection includes nested rows and normalized paths\n- **Reduced Duplication**: DRY Puppeteer utilities shared across migration functions\n\n**Technical Details**\n\nClient-Side Detection (PasteDetectionService):\n- Filter browser wrappers, trivial tags, images, links before server call\n- Defer to server only after user confirms in prompt\n- Toolbar \"Convert to Rows\" button with preview count (24 tests)\n- Detect view-source:// and http(s):// URL pastes\n- Auto-fetch HTML via /api/migration/html-from-url\n- Auto-fill derived base URL from <base href> or URL path\n\nServer Processing:\n- Preprocess: entity decode, strip Chrome code-table wrappers\n- Unwrap anchors in src/href attributes\n- Normalize <img> sources (data-src/srcset fallbacks)\n- Resolve relative URLs against base URL\n- Post-processing: remove tags/comments, normalize &nbsp;\n- Strip stray CSS/property declarations and font-family suffixes\n- Collapse whitespace and blockquote artifacts\n- Turndown integration via createTurndownService\n- Final markdown cleanup for cleaner rows\n\nNew APIs:\n- POST /api/migration/html-from-url: loads page with Puppeteer, extracts HTML, derives base URL\n- POST /api/migration/html-paste-preview: returns markdown + rows for preview\n- POST /api/migration/markdown-paste-preview: returns markdown + rows for insertion\n- Safe fallback to fetch() when Puppeteer unavailable\n\nUX Enhancements:\n- Base URL prompt with suggestions from migration config\n- Directory extraction via ensureTrailingSlash\n- Paste dialog: \"Split into rows\" vs \"Convert without splitting\"\n- Row placement radios: nested vs sibling (smart default by column count)\n- Split images and adjacent short captions into rows\n- Keep Content Together (Nested Rows) configuration toggle\n- Unnest action for moving content to previous/next outer row\n\nAdmin Tools:\n- Site configuration Paste/Copy buttons (JSON textarea)\n- ClipboardService for reliable copy operation\n- Immediate JSON apply with validation and Cancel\n- Paste auto-expands the site config section\n- Duplicate site action retained\n\nPuppeteer DRY:\n- New puppeteer-utils.ts centralizes launchBrowser() and deriveBaseUrl()\n- Refactored migrate-static-site.ts, migrate-static-site-engine.ts, migration-routes.ts\n- Shared launcher eliminates code duplication\n\nFragments:\n- Fragment index includes nested row usages\n- Normalize fragment path comparison (leading slash handling)\n- Recursive usage detection\n- Redirect /fragments to /admin/fragment-index\n\nTesting:\n- Comprehensive unit tests: turndown-service-factory.spec.ts\n  - Tests: entity decoding, wrapper removal, anchor unwrapping\n  - Relative image resolution, nbsp normalization, blockquote cleanup, CSS stripping\n- Related route specs and e2e previews\n- Frontend builds succeed, server tests pass\n- No database schema changes","contentHtml":"<h1>25th October 2025 — Optimized HTML/Markdown Paste Pipeline <a href=\"https://github.com/nbarrett/ngx-ramblers/issues/67\">#67</a></h1>\n<h2><a href=\"https://github.com/nbarrett/ngx-ramblers/actions/runs/18803315196\">build 321</a> — <a href=\"https://github.com/nbarrett/ngx-ramblers/issues/67\">#67</a> — <a href=\"https://github.com/nbarrett/ngx-ramblers/commit/07644ee\">commit 07644ee</a></h2>\n<p>Dramatically improved paste experience with client-side HTML detection, server-side processing enhancements, URL auto-fetch, and admin clipboard tools.</p>\n<p><strong>Where to Find It</strong></p>\n<ul>\n<li><strong>Markdown Editor</strong>: Paste HTML content, toolbar &quot;Convert to Rows&quot; button</li>\n<li><strong>Admin → Migration Settings</strong>: Paste/Copy buttons for site configuration JSON</li>\n<li><strong>Fragment Index</strong>: Recursive detection of nested row usage</li>\n</ul>\n<p><strong>Benefits</strong></p>\n<ul>\n<li><strong>Instant Plain Pastes</strong>: Client filters trivial HTML/browser wrappers before server call</li>\n<li><strong>Cleaner Rows</strong>: Enhanced preprocessing removes artifacts, produces better Markdown</li>\n<li><strong>Smart URL Handling</strong>: Paste view-source:// or http(s):// URLs to auto-fetch and convert pages</li>\n<li><strong>Base URL Suggestions</strong>: Dialog prompts with migration config URLs for relative link resolution</li>\n<li><strong>Nested Content Support</strong>: Choose nested/sibling placement with smart column-count defaults</li>\n<li><strong>Admin Efficiency</strong>: Copy/paste site config JSON directly in migration settings</li>\n<li><strong>Fragment Awareness</strong>: Recursive detection includes nested rows and normalized paths</li>\n<li><strong>Reduced Duplication</strong>: DRY Puppeteer utilities shared across migration functions</li>\n</ul>\n<p><strong>Technical Details</strong></p>\n<p>Client-Side Detection (PasteDetectionService):</p>\n<ul>\n<li>Filter browser wrappers, trivial tags, images, links before server call</li>\n<li>Defer to server only after user confirms in prompt</li>\n<li>Toolbar &quot;Convert to Rows&quot; button with preview count (24 tests)</li>\n<li>Detect view-source:// and http(s):// URL pastes</li>\n<li>Auto-fetch HTML via /api/migration/html-from-url</li>\n<li>Auto-fill derived base URL from <base href> or URL path</li>\n</ul>\n<p>Server Processing:</p>\n<ul>\n<li>Preprocess: entity decode, strip Chrome code-table wrappers</li>\n<li>Unwrap anchors in src/href attributes</li>\n<li>Normalize <img> sources (data-src/srcset fallbacks)</li>\n<li>Resolve relative URLs against base URL</li>\n<li>Post-processing: remove tags/comments, normalize &nbsp;</li>\n<li>Strip stray CSS/property declarations and font-family suffixes</li>\n<li>Collapse whitespace and blockquote artifacts</li>\n<li>Turndown integration via createTurndownService</li>\n<li>Final markdown cleanup for cleaner rows</li>\n</ul>\n<p>New APIs:</p>\n<ul>\n<li>POST /api/migration/html-from-url: loads page with Puppeteer, extracts HTML, derives base URL</li>\n<li>POST /api/migration/html-paste-preview: returns markdown + rows for preview</li>\n<li>POST /api/migration/markdown-paste-preview: returns markdown + rows for insertion</li>\n<li>Safe fallback to fetch() when Puppeteer unavailable</li>\n</ul>\n<p>UX Enhancements:</p>\n<ul>\n<li>Base URL prompt with suggestions from migration config</li>\n<li>Directory extraction via ensureTrailingSlash</li>\n<li>Paste dialog: &quot;Split into rows&quot; vs &quot;Convert without splitting&quot;</li>\n<li>Row placement radios: nested vs sibling (smart default by column count)</li>\n<li>Split images and adjacent short captions into rows</li>\n<li>Keep Content Together (Nested Rows) configuration toggle</li>\n<li>Unnest action for moving content to previous/next outer row</li>\n</ul>\n<p>Admin Tools:</p>\n<ul>\n<li>Site configuration Paste/Copy buttons (JSON textarea)</li>\n<li>ClipboardService for reliable copy operation</li>\n<li>Immediate JSON apply with validation and Cancel</li>\n<li>Paste auto-expands the site config section</li>\n<li>Duplicate site action retained</li>\n</ul>\n<p>Puppeteer DRY:</p>\n<ul>\n<li>New puppeteer-utils.ts centralizes launchBrowser() and deriveBaseUrl()</li>\n<li>Refactored migrate-static-site.ts, migrate-static-site-engine.ts, migration-routes.ts</li>\n<li>Shared launcher eliminates code duplication</li>\n</ul>\n<p>Fragments:</p>\n<ul>\n<li>Fragment index includes nested row usages</li>\n<li>Normalize fragment path comparison (leading slash handling)</li>\n<li>Recursive usage detection</li>\n<li>Redirect /fragments to /admin/fragment-index</li>\n</ul>\n<p>Testing:</p>\n<ul>\n<li>Comprehensive unit tests: turndown-service-factory.spec.ts<ul>\n<li>Tests: entity decoding, wrapper removal, anchor unwrapping</li>\n<li>Relative image resolution, nbsp normalization, blockquote cleanup, CSS stripping</li>\n</ul>\n</li>\n<li>Related route specs and e2e previews</li>\n<li>Frontend builds succeed, server tests pass</li>\n<li>No database schema changes</li>\n</ul>\n"}