Skip to content

fix: align numbered markdown items with bold leading content#424

Open
jeffscottward wants to merge 7 commits intoRunMaestro:mainfrom
jeffscottward:fix/issue-53-markdown-list-alignment
Open

fix: align numbered markdown items with bold leading content#424
jeffscottward wants to merge 7 commits intoRunMaestro:mainfrom
jeffscottward:fix/issue-53-markdown-list-alignment

Conversation

@jeffscottward
Copy link
Contributor

@jeffscottward jeffscottward commented Feb 19, 2026

Chained Scope (what this PR bundles)

This PR intentionally chains two related tracks so we can ship the bug fix safely while starting the renderer unification work from issue #315.

  1. Bug fix for Seeking Assistance: Pesky Markdown Formatting Issue #53

    • fixes ordered-list marker alignment when list items start with styled inline content
    • normalizes li > p rendering so markers stay on the same line as bold list titles
    • covers shared .prose paths and wizard markdown path
  2. Phase 1 of Unified Markdown Renderer #315 (unification kickoff)

    • centralizes duplicated wizard markdown overrides into shared presets in markdownConfig.ts
    • migrates duplicate wizard/chat bubble renderers:
      • ConversationScreen
      • WizardMessageBubble
    • centralizes release notes markdown overrides and migrates:
      • UpdateCheckModal

Why these are bundled

Files touched (phase 1 refactor)

  • src/renderer/utils/markdownConfig.ts
  • src/renderer/components/Wizard/screens/ConversationScreen.tsx
  • src/renderer/components/InlineWizard/WizardMessageBubble.tsx
  • src/renderer/components/UpdateCheckModal.tsx
  • src/__tests__/renderer/utils/markdownConfig.test.ts

Validation

  • npm run test -- src/__tests__/renderer/utils/markdownConfig.test.ts src/__tests__/renderer/components/InlineWizard/WizardMessageBubble.test.tsx src/__tests__/renderer/components/UpdateCheckModal.test.tsx
    • 190 tests passed

Visual proof

Follow-up for #315

  • This does not complete full renderer unification across all markdown surfaces yet.
  • This PR establishes shared presets and migrates the highest-duplication paths first.

Summary by CodeRabbit

  • Bug Fixes

    • Improved list rendering: first-paragraphs render inline with baseline alignment and normalized nested-list spacing across prose and wizard previews.
  • New Features

    • Theme-aware Markdown presets for wizard bubbles and release notes, plus a shared Markdown plugin set for consistent processing.
  • Refactor

    • Centralized and memoized Markdown rendering utilities for more consistent, performant rendering app-wide.
  • Tests

    • Expanded coverage for shared Markdown presets, inline prose styling, and component creation.

@jeffscottward
Copy link
Contributor Author

Visual before/after for markdown numbered-list alignment fix (issue #53):

Markdown list alignment before/after

Left: previous behavior (marker can break onto its own line).
Right: updated behavior (marker and bold list heading stay aligned on the same line).

@greptile-apps
Copy link

greptile-apps bot commented Feb 19, 2026

Greptile Summary

Fixed ordered list marker alignment when list items start with bold or other styled inline content (resolves issue #53). The fix ensures that markers (1., 2., etc.) remain on the same baseline as bold list titles.

Key changes:

  • Normalized .prose li > p rendering to display: inline with vertical-align: baseline across all contexts (not just compactSpacing)
  • Added selector coverage for nested first-child styled content (.prose li > p > strong:first-child, etc.)
  • Added wizard-specific .wizard-markdown rules to address renderer fragmentation noted in Unified Markdown Renderer #315
  • Applied changes consistently across renderer, web, and terminal markdown rendering
  • Added comprehensive test coverage in markdownConfig.test.ts

Technical approach:
The root issue was that markdown renderers wrap list content in <p> tags (<li><p><strong>Title</strong></p></li>), causing vertical misalignment. The fix makes these paragraphs render inline and ensures styled first-child elements inherit baseline alignment.

Confidence Score: 5/5

  • This PR is safe to merge - it's a well-tested, surgical CSS fix with clear visual validation
  • The changes are focused CSS adjustments with comprehensive test coverage, visual proof of the fix, and no logic changes. Tests pass, the fix addresses a real visual issue, and the changes are applied consistently across all rendering contexts (renderer, web, terminal).
  • No files require special attention

Important Files Changed

Filename Overview
src/tests/renderer/utils/markdownConfig.test.ts added regression tests for li > p normalization and nested styled content selectors
src/renderer/utils/markdownConfig.ts normalized li > p styling and added nested first-child styled content selectors in both prose and terminal styles
src/renderer/index.css added global .prose li > p normalization, nested styled selectors, and wizard-specific markdown rules

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Markdown: 1. **Bold Text**] --> B{Renderer Output}
    B --> C[&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bold Text&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;]
    
    C --> D{CSS Applied}
    
    D --> E[.prose li > p]
    E --> F[display: inline<br/>vertical-align: baseline<br/>line-height: inherit]
    
    D --> G[.prose li > p > strong:first-child]
    G --> H[vertical-align: baseline<br/>line-height: inherit]
    
    D --> I[.wizard-markdown rules]
    I --> J[Same alignment fixes<br/>for wizard contexts]
    
    F --> K[✓ Marker aligned with text]
    H --> K
    J --> K
    
    K --> L[Visual Result:<br/>Numbered markers stay on<br/>same line as bold content]
Loading

Last reviewed commit: c81aefe

@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Exports a shared GFM plugin list and centralized Markdown component/style factories (REMARK_GFM_PLUGINS, createMarkdownComponents variants, generateInlineWizardPreviewProseStyles), replaces many inline ReactMarkdown plugin/component maps with these factories, and adds CSS rules to normalize list-item paragraph rendering and inline-element alignment.

Changes

Cohort / File(s) Summary
Markdown core & tests
src/renderer/utils/markdownConfig.ts, src/__tests__/renderer/utils/markdownConfig.test.ts
Added/exported REMARK_GFM_PLUGINS, InlineWizardPreviewVariant, generateInlineWizardPreviewProseStyles, createWizardBubbleMarkdownComponents, createReleaseNotesMarkdownComponents, and enhanced createMarkdownComponents options (codeBlockStyle, onFileClick signature). Tests updated to assert new exports and prose style behaviors.
Shared plugin
src/shared/markdownPlugins.ts
Introduced canonical REMARK_GFM_PLUGINS (wraps remarkGfm) for reuse across renderer code.
Inline wizard & streaming previews
src/renderer/components/InlineWizard/DocumentGenerationView.tsx, src/renderer/components/InlineWizard/StreamingDocumentPreview.tsx, src/renderer/components/InlineWizard/WizardMessageBubble.tsx
Replaced local remark/plugin/component logic with centralized factories: use REMARK_GFM_PLUGINS, createMarkdownComponents/createWizardBubbleMarkdownComponents, and generateInlineWizardPreviewProseStyles for themed rendering and element overrides.
Conversation / Wizard components
src/renderer/components/Wizard/screens/ConversationScreen.tsx, src/renderer/components/Wizard/shared/DocumentEditor.tsx
Switched from local remarkGfm to REMARK_GFM_PLUGINS; memoize and pass factory-generated wizardMarkdownComponents into message bubbles instead of inline mappings.
Modals & other markdown consumers
src/renderer/components/UpdateCheckModal.tsx, src/renderer/components/MarketplaceModal.tsx, src/renderer/components/SymphonyModal.tsx, src/__tests__/renderer/components/SymphonyModal.test.tsx
Replaced inline remarkGfm imports with shared REMARK_GFM_PLUGINS; UpdateCheckModal now memoizes createReleaseNotesMarkdownComponents(theme); mocks/tests adjusted for new export.
Markdown renderer, previews & file preview
src/renderer/components/MarkdownRenderer.tsx, src/renderer/components/FilePreview.tsx
Spread REMARK_GFM_PLUGINS into remarkPlugins; replaced many inline component implementations with createMarkdownComponents invocations to centralize handlers for images, links, files, mermaid, and code blocks; passed callbacks/refs into factory.
Global & web/mobile CSS prose fixes
src/renderer/index.css, src/web/index.css, src/web/mobile/MobileMarkdownRenderer.tsx
Added rules making li > p:first-child render inline with zero margins and baseline alignment, removed top margins for nested lists after first-child paragraphs, and added parallel .wizard-markdown rules to normalize list/inline element behavior across contexts.
Other consumers
src/renderer/components/AutoRun.tsx, src/renderer/components/UpdateCheckModal.tsx, src/renderer/components/MarketplaceModal.tsx, src/renderer/components/SymphonyModal.tsx, src/renderer/components/FilePreview.tsx
Adjusted various consumers to use REMARK_GFM_PLUGINS and, where applicable, the centralized component factories; replaced many inline plugin/component maps with shared factories.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • Unified Markdown Renderer #315 — Centralizes markdown plugin/component definitions and replaces inline overrides across renderer paths, aligning with the issue goal to unify the Markdown renderer.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main fix: aligning numbered markdown list items when they contain bold leading content, which matches the primary bug fix objective (#53) evident in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/renderer/components/InlineWizard/WizardMessageBubble.tsx (1)

90-90: Consider memoizing wizardMarkdownComponents for consistency.

Inside this React.memo component, createWizardBubbleMarkdownComponents(theme) is called on every render. While React.memo reduces render frequency, when the component does render, the components object is recreated. For consistency with UpdateCheckModal.tsx (which uses useMemo), consider:

♻️ Suggested improvement
+import React, { useMemo } from 'react';
-import React from 'react';
...
 export const WizardMessageBubble = React.memo(function WizardMessageBubble({
 	message,
 	theme,
 	agentName = 'Agent',
 	providerName,
 }: WizardMessageBubbleProps): JSX.Element {
 	const isUser = message.role === 'user';
 	const isSystem = message.role === 'system';
-	const wizardMarkdownComponents = createWizardBubbleMarkdownComponents(theme);
+	const wizardMarkdownComponents = useMemo(
+		() => createWizardBubbleMarkdownComponents(theme),
+		[theme]
+	);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/InlineWizard/WizardMessageBubble.tsx` at line 90, The
wizardMarkdownComponents object is recreated on every render because
createWizardBubbleMarkdownComponents(theme) is called directly inside the
React.memo component; wrap that call in useMemo (e.g., const
wizardMarkdownComponents = useMemo(() =>
createWizardBubbleMarkdownComponents(theme), [theme])) so the components object
is memoized and only recalculated when theme changes, matching the pattern used
in UpdateCheckModal.tsx and improving consistency/stability.
src/renderer/components/Wizard/screens/ConversationScreen.tsx (1)

152-152: Consider hoisting wizardMarkdownComponents to parent scope for efficiency.

MessageBubble is called for each message in the conversation history. Creating wizardMarkdownComponents inside it means the components object is recreated for every message bubble on every render of ConversationScreen.

Consider hoisting to the parent ConversationScreen component:

♻️ Suggested improvement
 export function ConversationScreen({
 	theme,
 	showThinking,
 	setShowThinking,
 }: ConversationScreenProps): JSX.Element {
+	const wizardMarkdownComponents = useMemo(
+		() => createWizardBubbleMarkdownComponents(theme),
+		[theme]
+	);
 	const {
 		state,
 ...
 function MessageBubble({
 	message,
 	theme,
 	agentName,
 	providerName,
+	wizardMarkdownComponents,
 }: {
 	message: WizardMessage;
 	theme: Theme;
 	agentName: string;
 	providerName?: string;
+	wizardMarkdownComponents: Partial<Components>;
 }): JSX.Element {
 	const isUser = message.role === 'user';
 	const isSystem = message.role === 'system';
-	const wizardMarkdownComponents = createWizardBubbleMarkdownComponents(theme);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/Wizard/screens/ConversationScreen.tsx` at line 152,
The creation of wizardMarkdownComponents inside each MessageBubble is
inefficient; move its creation up into the parent ConversationScreen (or memoize
it with React.useMemo in ConversationScreen) so the object is created once per
theme change instead of per message render; reference
createWizardBubbleMarkdownComponents and wizardMarkdownComponents and then pass
the resulting wizardMarkdownComponents down into MessageBubble as a prop (or
import/useMemo in ConversationScreen) to avoid recreating it for every message.
src/renderer/utils/markdownConfig.ts (1)

465-525: Consider memoizing or documenting the intended usage pattern.

This factory function creates a new components object on each call. While UpdateCheckModal.tsx correctly wraps this in useMemo, both WizardMessageBubble.tsx (line 90) and ConversationScreen.tsx (line 152) call it directly inside the render function without memoization.

For memoized components like WizardMessageBubble, the component object is recreated on every render, which can cause unnecessary reconciliation in ReactMarkdown. Consider either:

  1. Memoizing the result in consuming components (as UpdateCheckModal does)
  2. Documenting that callers should memoize the result
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/utils/markdownConfig.ts` around lines 465 - 525, The
createWizardBubbleMarkdownComponents factory (used by WizardMessageBubble and
ConversationScreen) returns a fresh components object on each call which forces
ReactMarkdown to re-reconcile; wrap calls to
createWizardBubbleMarkdownComponents(theme) in useMemo in WizardMessageBubble
and ConversationScreen (like UpdateCheckModal does) so the same object identity
is returned across renders, or alternatively add a clear JSDoc comment above
createWizardBubbleMarkdownComponents advising all callers to memoize the result
(e.g., via React.useMemo with theme as dependency).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/renderer/components/InlineWizard/WizardMessageBubble.tsx`:
- Line 90: The wizardMarkdownComponents object is recreated on every render
because createWizardBubbleMarkdownComponents(theme) is called directly inside
the React.memo component; wrap that call in useMemo (e.g., const
wizardMarkdownComponents = useMemo(() =>
createWizardBubbleMarkdownComponents(theme), [theme])) so the components object
is memoized and only recalculated when theme changes, matching the pattern used
in UpdateCheckModal.tsx and improving consistency/stability.

In `@src/renderer/components/Wizard/screens/ConversationScreen.tsx`:
- Line 152: The creation of wizardMarkdownComponents inside each MessageBubble
is inefficient; move its creation up into the parent ConversationScreen (or
memoize it with React.useMemo in ConversationScreen) so the object is created
once per theme change instead of per message render; reference
createWizardBubbleMarkdownComponents and wizardMarkdownComponents and then pass
the resulting wizardMarkdownComponents down into MessageBubble as a prop (or
import/useMemo in ConversationScreen) to avoid recreating it for every message.

In `@src/renderer/utils/markdownConfig.ts`:
- Around line 465-525: The createWizardBubbleMarkdownComponents factory (used by
WizardMessageBubble and ConversationScreen) returns a fresh components object on
each call which forces ReactMarkdown to re-reconcile; wrap calls to
createWizardBubbleMarkdownComponents(theme) in useMemo in WizardMessageBubble
and ConversationScreen (like UpdateCheckModal does) so the same object identity
is returned across renders, or alternatively add a clear JSDoc comment above
createWizardBubbleMarkdownComponents advising all callers to memoize the result
(e.g., via React.useMemo with theme as dependency).

@jeffscottward
Copy link
Contributor Author

jeffscottward commented Feb 19, 2026

Phase 2 update (bundled into this same PR):

Chaining summary:

  1. Seeking Assistance: Pesky Markdown Formatting Issue #53 baseline fix (list marker alignment in bold-leading list items)
  2. Unified Markdown Renderer #315 phase-1 centralization (shared wizard/release-note markdown presets)
  3. Unified Markdown Renderer #315 phase-2 minimal safe rollout (remaining impacted render paths now use shared markdown config + shared GFM plugin constant)

What phase 2 changed:

  • Inline wizard surfaces now share centralized preview prose styles and markdown component factory usage
  • Remaining impacted paths (, , ) now use instead of local plugin arrays
  • Symphony modal tests updated for the new shared export in mocks

Validation:

maestro@0.15.0 lint
tsc -p tsconfig.lint.json && tsc -p tsconfig.main.json --noEmit && tsc -p tsconfig.cli.json --noEmit

maestro@0.15.0 test
vitest run src/tests/renderer/utils/markdownConfig.test.ts src/tests/renderer/components/InlineWizard/WizardMessageBubble.test.tsx src/tests/renderer/components/UpdateCheckModal.test.tsx src/tests/renderer/components/SymphonyModal.test.tsx

RUN v4.0.15 /Users/jeffscottward/Documents/GitHub/tools/maestro-bug-full-auto/Maestro

✓ src/tests/renderer/utils/markdownConfig.test.ts (83 tests) 8ms
✓ src/tests/renderer/components/SymphonyModal.test.tsx (6 tests) 363ms
✓ src/tests/renderer/components/InlineWizard/WizardMessageBubble.test.tsx (44 tests) 189ms
✓ src/tests/renderer/components/UpdateCheckModal.test.tsx (63 tests) 518ms

Test Files 4 passed (4)
Tests 196 passed (196)
Start at 17:49:41
Duration 3.34s (transform 1.58s, setup 515ms, import 3.09s, tests 1.08s, environment 2.63s)

Visual artifact (requested):
Phase 2 markdown before/after

@jeffscottward
Copy link
Contributor Author

Correction to previous phase-2 note (shell escaping mangled component names).

Phase 2 update (bundled into this same PR):

Chaining summary:

  1. Seeking Assistance: Pesky Markdown Formatting Issue #53 baseline fix (list marker alignment in bold-leading list items)
  2. Unified Markdown Renderer #315 phase-1 centralization (shared wizard/release-notes markdown presets)
  3. Unified Markdown Renderer #315 phase-2 minimal safe rollout (remaining impacted render paths now use shared markdown config + shared GFM plugin constant)

What phase 2 changed:

  • Inline wizard surfaces now share centralized preview prose styles and markdown component factory usage
  • Remaining impacted paths (SymphonyModal, MarketplaceModal, Wizard/shared/DocumentEditor) now use REMARK_GFM_PLUGINS instead of local plugin arrays
  • Symphony modal tests updated for the new shared export in mocks

Validation:

  • npm run lint
  • npm run test -- src/__tests__/renderer/utils/markdownConfig.test.ts src/__tests__/renderer/components/InlineWizard/WizardMessageBubble.test.tsx src/__tests__/renderer/components/UpdateCheckModal.test.tsx src/__tests__/renderer/components/SymphonyModal.test.tsx

Visual artifact (requested):
Phase 2 markdown before/after

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderer/components/InlineWizard/DocumentGenerationView.tsx`:
- Around line 548-549: The scoped CSS generated by
generateInlineWizardPreviewProseStyles expects selectors like `${scopeSelector}
.prose` but the preview container currently applies both 'doc-gen-view' and
'prose' on the same element, so the rules don't match; fix by either wrapping
the preview content in a child element with class "prose" (so the selector
`${scopeSelector} .prose` matches) or modify the call/site that uses
scopeSelector '.doc-gen-view' to also generate selectors that match the
same-element case (e.g., include `${scopeSelector}.prose` in the generated
selector); update the preview markup or the selector generation in
generateInlineWizardPreviewProseStyles accordingly (refer to
generateInlineWizardPreviewProseStyles and the preview container that uses
classes 'doc-gen-view' and 'prose').

In `@src/renderer/utils/markdownConfig.ts`:
- Around line 144-146: The current CSS makes every paragraph inside list items
display:inline which collapses multi-paragraph breaks; update the selectors in
both generateProseStyles and generateTerminalProseStyles to target only the
first paragraph (use li > p:first-child) for inline styling and explicitly
restore block layout for subsequent paragraphs (e.g., li > p:not(:first-child) {
display: block; margin-top: ... }). Also adjust the related adjacent-list
selectors (li > p:first-child + ul / + ol) so they still remove top margin for
lists immediately following the first paragraph.

Comment on lines +548 to 549
() => generateInlineWizardPreviewProseStyles(theme, '.doc-gen-view', 'document'),
[theme]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Generated prose styles won’t match the current DOM structure.
generateInlineWizardPreviewProseStyles scopes rules to ${scopeSelector} .prose, but the preview container on Line 669 applies doc-gen-view and prose on the same element, so the new rules won’t match. Consider nesting a .prose wrapper (or adjusting the selector) to avoid a styling regression.

🛠️ Suggested markup adjustment so scoped prose styles apply
-					<div
-						ref={previewRef}
-						className="doc-gen-view h-full overflow-y-auto border rounded p-4 prose prose-sm max-w-none outline-none"
-						tabIndex={0}
-						onKeyDown={(e) => {
-							if ((e.metaKey || e.ctrlKey) && e.key === 'e') {
-								e.preventDefault();
-								e.stopPropagation();
-								onModeChange('edit');
-							}
-						}}
-						style={{
-							borderColor: theme.colors.border,
-							color: theme.colors.textMain,
-							fontSize: '13px',
-						}}
-					>
-						<style>{proseStyles}</style>
-						<ReactMarkdown remarkPlugins={REMARK_GFM_PLUGINS} components={markdownComponents}>
-							{content || '*No content yet.*'}
-						</ReactMarkdown>
-					</div>
+					<div
+						ref={previewRef}
+						className="doc-gen-view h-full overflow-y-auto border rounded p-4 outline-none"
+						tabIndex={0}
+						onKeyDown={(e) => {
+							if ((e.metaKey || e.ctrlKey) && e.key === 'e') {
+								e.preventDefault();
+								e.stopPropagation();
+								onModeChange('edit');
+							}
+						}}
+						style={{
+							borderColor: theme.colors.border,
+							color: theme.colors.textMain,
+							fontSize: '13px',
+						}}
+					>
+						<style>{proseStyles}</style>
+						<div className="prose prose-sm max-w-none">
+							<ReactMarkdown remarkPlugins={REMARK_GFM_PLUGINS} components={markdownComponents}>
+								{content || '*No content yet.*'}
+							</ReactMarkdown>
+						</div>
+					</div>

Also applies to: 667-686

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/InlineWizard/DocumentGenerationView.tsx` around lines
548 - 549, The scoped CSS generated by generateInlineWizardPreviewProseStyles
expects selectors like `${scopeSelector} .prose` but the preview container
currently applies both 'doc-gen-view' and 'prose' on the same element, so the
rules don't match; fix by either wrapping the preview content in a child element
with class "prose" (so the selector `${scopeSelector} .prose` matches) or modify
the call/site that uses scopeSelector '.doc-gen-view' to also generate selectors
that match the same-element case (e.g., include `${scopeSelector}.prose` in the
generated selector); update the preview markup or the selector generation in
generateInlineWizardPreviewProseStyles accordingly (refer to
generateInlineWizardPreviewProseStyles and the preview container that uses
classes 'doc-gen-view' and 'prose').

@jeffscottward
Copy link
Contributor Author

Final phase complete for #315 unification bundle in this PR.

Chaining (final):

  1. Seeking Assistance: Pesky Markdown Formatting Issue #53 baseline list-marker alignment fix
  2. Unified Markdown Renderer #315 phase 1: shared wizard/release-note presets
  3. Unified Markdown Renderer #315 phase 2: remaining impacted renderer paths
  4. Unified Markdown Renderer #315 final phase: shared GFM plugin source + final renderer/mobile consolidation

Final-phase changes in this commit:

  • Added shared plugin source used by renderer + web/mobile: src/shared/markdownPlugins.ts
  • Updated renderer shared config to re-export shared plugin source and support onFileClick open-mode options: src/renderer/utils/markdownConfig.ts
  • Migrated remaining renderer paths off direct remarkGfm imports and onto shared plugin stack:
    • src/renderer/components/AutoRun.tsx
    • src/renderer/components/FilePreview.tsx
    • src/renderer/components/MarkdownRenderer.tsx
  • Consolidated FilePreview markdown component overrides onto createMarkdownComponents (kept custom image resolver)
  • Added mobile list-alignment parity styles + shared plugin usage:
    • src/web/mobile/MobileMarkdownRenderer.tsx

Validation:

  • npm run lint
  • npm run test -- src/__tests__/renderer/utils/markdownConfig.test.ts src/__tests__/renderer/components/MarkdownRenderer.test.tsx src/__tests__/renderer/components/FilePreview.test.tsx src/__tests__/renderer/components/AutoRun.test.tsx src/__tests__/web/mobile/ResponseViewer.test.tsx src/__tests__/web/mobile/MessageHistory.test.tsx

Final screenshot artifact (requested):
Phase 3 markdown unification final

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/web/mobile/MobileMarkdownRenderer.tsx`:
- Around line 204-218: The CSS currently collapses all paragraphs inside list
items by using ".mobile-markdown-content li > p { display: inline; }"; update
the selectors to only target the first paragraph in a list item (e.g.
".mobile-markdown-content li > p:first-of-type { display: inline; ... }") and
restore block layout for subsequent paragraphs (e.g. ".mobile-markdown-content
li > p:not(:first-of-type) { display: block; margin-top: <reasonable spacing>;
}"); also adjust the nested inline rules (the selectors like
".mobile-markdown-content li > p > strong:first-child, ...") to apply only to
the first paragraph (use :first-of-type or equivalent) so only the first
paragraph inherits inline alignment while later paragraphs keep normal block
spacing.

---

Duplicate comments:
In `@src/renderer/utils/markdownConfig.ts`:
- Around line 144-146: The rule making all list-item paragraphs inline is
collapsing multi-paragraph list items; change the selectors so only the first
paragraph inside an li is rendered inline (e.g., target li > p:first-child) and
keep subsequent paragraphs block-level, and update the related selectors (the
ones matching li > p + ul / li > p + ol and the first-child inline rules for
strong/b/em/code/a) to use the same first-child-scoped selector so
multi-paragraph list items are not flattened.

@jeffscottward
Copy link
Contributor Author

Addressed the requested changes from the latest review cycle.

What changed

  1. Preserve multi-paragraph list items (renderer + terminal prose styles)
  • Updated generateProseStyles and generateTerminalProseStyles so only li > p:first-child is inlined for marker alignment.
  • Added explicit block restoration for subsequent paragraphs via li > p:not(:first-child).
  • Updated adjacent-list selectors to li > p:first-child + ul/ol.
  • Updated styled-first-child selectors to li > p:first-child > ....
  1. Fix scoped selector mismatch for document preview styles
  • Updated generateInlineWizardPreviewProseStyles to support both same-element and descendant matching:
    • ${scopeSelector}.prose
    • ${scopeSelector} .prose
  • This fixes the DocumentGenerationView case where the container had both doc-gen-view and prose classes on the same element.
  1. Mobile parity for multi-paragraph list items
  • Updated MobileMarkdownRenderer inline CSS to only inline first paragraph in list items (:first-of-type) and keep later paragraphs block-level.
  1. Prior nitpicks cleaned up as well
  • Memoized wizard markdown component factory usage in:
    • WizardMessageBubble
    • ConversationScreen (hoisted/memoized once per theme and passed down)
  1. Global CSS parity
  • Applied first-paragraph-only inline behavior in src/renderer/index.css and src/web/index.css to avoid flattening multi-paragraph list items in global .prose paths.

Validation

  • npm run test -- src/__tests__/renderer/utils/markdownConfig.test.ts src/__tests__/renderer/components/InlineWizard/WizardMessageBubble.test.tsx src/__tests__/renderer/components/Wizard/screens/ConversationScreen.test.ts
  • npm run lint

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/__tests__/renderer/utils/markdownConfig.test.ts (1)

16-19: Consider loosening the fixed plugin-count assertion.
Asserting REMARK_GFM_PLUGINS.length === 1 can create churn if more shared plugins are added. A looser assertion (array existence + non‑empty) keeps the test focused on contract, not implementation detail.

🧪 Suggested adjustment
 	it('should export a shared remark-gfm plugin array', () => {
 		expect(Array.isArray(REMARK_GFM_PLUGINS)).toBe(true);
-		expect(REMARK_GFM_PLUGINS.length).toBe(1);
+		expect(REMARK_GFM_PLUGINS.length).toBeGreaterThan(0);
 	});

Also applies to: 656-688

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/renderer/utils/markdownConfig.test.ts` around lines 16 - 19,
The test currently asserts a strict plugin count (REMARK_GFM_PLUGINS.length ===
1); change this to a looser contract check that ensures REMARK_GFM_PLUGINS is an
array and not empty (e.g., Array.isArray(REMARK_GFM_PLUGINS) &&
REMARK_GFM_PLUGINS.length > 0) so the test verifies presence rather than exact
count; apply the same relaxation to the other similar assertions noted (around
the other REMARK_* plugin assertions referenced in the comment).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderer/utils/markdownConfig.ts`:
- Around line 483-559: generateInlineWizardPreviewProseStyles is missing the
list-item paragraph normalization rules present in
generateProseStyles/generateTerminalProseStyles, which causes list markers to
misalign when a list item starts with formatted inline content; fix it by adding
the same selectors and rules used there (the li p:first-child, li > :first-child
selectors that normalize margin/padding for paragraphs/strong/em/code at the
start of a list item) into the returned template string in
generateInlineWizardPreviewProseStyles so list markers align correctly for
formatted-first children.

---

Nitpick comments:
In `@src/__tests__/renderer/utils/markdownConfig.test.ts`:
- Around line 16-19: The test currently asserts a strict plugin count
(REMARK_GFM_PLUGINS.length === 1); change this to a looser contract check that
ensures REMARK_GFM_PLUGINS is an array and not empty (e.g.,
Array.isArray(REMARK_GFM_PLUGINS) && REMARK_GFM_PLUGINS.length > 0) so the test
verifies presence rather than exact count; apply the same relaxation to the
other similar assertions noted (around the other REMARK_* plugin assertions
referenced in the comment).

@jeffscottward
Copy link
Contributor Author

jeffscottward commented Feb 19, 2026

Addressed the latest CodeRabbit follow-up:

  • Added list-item paragraph normalization rules to so first paragraphs are inline and subsequent paragraphs remain block-level.
  • Added/updated selector coverage for styled first-child content alignment in inline wizard preview prose styles.
  • Added focused tests for selector behavior and list normalization.
  • Relaxed strict count assertion to non-empty array contract.

Validation run in this branch:

maestro@0.15.0 test
vitest run src/tests/renderer/utils/markdownConfig.test.ts

RUN v4.0.15 /Users/jeffscottward/Documents/GitHub/tools/maestro-bug-full-auto/Maestro.jeffscottward-fix-issue-53-markdown-list-alignment

✓ src/tests/renderer/utils/markdownConfig.test.ts (86 tests) 7ms

Test Files 1 passed (1)
Tests 86 passed (86)
Start at 18:52:41
Duration 595ms (transform 87ms, setup 81ms, import 95ms, tests 7ms, environment 277ms) ✅

(Full

maestro@0.15.0 lint
tsc -p tsconfig.lint.json && tsc -p tsconfig.main.json --noEmit && tsc -p tsconfig.cli.json --noEmit

src/prompts/index.ts(51,8): error TS2307: Cannot find module '../generated/prompts' or its corresponding type declarations. still reports the existing generated-module issue: cannot resolve in this worktree context.)

@jeffscottward
Copy link
Contributor Author

jeffscottward commented Feb 19, 2026

Correction to previous comment (shell escaped markdown got mangled).

Latest follow-up is now pushed in commit b3d9d4b:

  • Added list-item paragraph normalization to inline wizard preview prose styles (first paragraph inline, later paragraphs block).
  • Added styled first-child alignment selectors for inline wizard preview list items.
  • Added focused unit tests for generateInlineWizardPreviewProseStyles coverage.
  • Relaxed strict REMARK_GFM_PLUGINS count assertion to non-empty.

Validation run:

  • npm run test -- src/tests/renderer/utils/markdownConfig.test.ts (pass)

@jeffscottward
Copy link
Contributor Author

src/prompts/index.ts(51,8): error TS2307: Cannot find module '../generated/prompts' or its corresponding type declarations. still reports the existing generated-module issue: cannot resolve in this worktree context.)

This may not be solvable by AI; needs review. @pedramamini

Copy link
Collaborator

@pedramamini pedramamini left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeffscottward — nice work on the markdown unification. The bug fix portion already landed via #425, so what's left here is the Phase 1 refactor which looks solid. A few things to address before we can merge:

1. Rebase onto main (required)
PR #425 was merged into main and touches the same 4 files, so you've got conflicts in:

  • src/__tests__/renderer/utils/markdownConfig.test.ts
  • src/renderer/index.css
  • src/renderer/utils/markdownConfig.ts
  • src/web/index.css

Since the bug fix rules are already on main, take main's version of the alignment CSS and layer your unification additions on top.

2. Remove PNG files from repo root
markdown-list-alignment-before-after.png, phase2-markdown-before-after-final.png, phase3-markdown-unification-final.png — these shouldn't be committed. Remove them or move to the PR description as uploaded images.

3. CSS selector bug in generateInlineWizardPreviewProseStyles
The scope selector pattern "${scopeSelector}.prose, ${scopeSelector} .prose" used as a prefix for descendant rules is structurally wrong — the comma splits into two independent selectors, so rules like ${prefix} li > p expand to both ${scopeSelector}.prose li > p AND ${scopeSelector} .prose li > p. The first form targets descendants of the container itself (correct if .prose is on the container), but the second targets a .prose descendant. Make sure the generated selectors match the actual DOM structure in DocumentGenerationView.tsx.

4. Mobile :first-of-type vs :first-child inconsistency
MobileMarkdownRenderer.tsx uses :first-of-type while every other CSS path uses :first-child. Align to :first-child for consistency.

Once rebased and these are addressed, we're good to merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants