Fix markdown list text wrapping on Android

- Add custom list rendering rules in message.tsx for proper flex layout
- Remove flexWrap from list content styles (causes Android clipping)
- Use flex: 1 and flexShrink: 1 for natural text wrapping
- Add minWidth: 0 to content containers for proper shrinking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Mohamed Boudra
2025-12-24 04:41:37 +00:00
co-authored by Claude Opus 4.5
parent 09f130408b
commit 37da13a38b
4 changed files with 240 additions and 45 deletions
@@ -599,6 +599,111 @@ function PermissionRequestCard({
[theme]
);
const markdownRules = useMemo(() => {
return {
text: (
node: any,
_children: React.ReactNode[],
_parent: any,
styles: any,
inheritedStyles: any = {}
) => (
<Text key={node.key} style={[inheritedStyles, styles.text]} selectable>
{node.content}
</Text>
),
textgroup: (
node: any,
children: React.ReactNode[],
_parent: any,
styles: any,
inheritedStyles: any = {}
) => (
<Text key={node.key} style={[inheritedStyles, styles.textgroup]} selectable>
{children}
</Text>
),
code_block: (
node: any,
_children: React.ReactNode[],
_parent: any,
styles: any,
inheritedStyles: any = {}
) => (
<Text key={node.key} style={[inheritedStyles, styles.code_block]} selectable>
{node.content}
</Text>
),
fence: (
node: any,
_children: React.ReactNode[],
_parent: any,
styles: any,
inheritedStyles: any = {}
) => (
<Text key={node.key} style={[inheritedStyles, styles.fence]} selectable>
{node.content}
</Text>
),
code_inline: (
node: any,
_children: React.ReactNode[],
_parent: any,
styles: any,
inheritedStyles: any = {}
) => (
<Text key={node.key} style={[inheritedStyles, styles.code_inline]} selectable>
{node.content}
</Text>
),
bullet_list: (
node: any,
children: React.ReactNode[],
_parent: any,
styles: any
) => (
<View key={node.key} style={styles.bullet_list}>
{children}
</View>
),
ordered_list: (
node: any,
children: React.ReactNode[],
_parent: any,
styles: any
) => (
<View key={node.key} style={styles.ordered_list}>
{children}
</View>
),
list_item: (
node: any,
children: React.ReactNode[],
parent: any,
styles: any
) => {
const isOrdered = parent?.type === "ordered_list";
const index = parent?.children?.indexOf(node) ?? 0;
const bullet = isOrdered ? `${index + 1}.` : "•";
const iconStyle = isOrdered
? styles.ordered_list_icon
: styles.bullet_list_icon;
const contentStyle = isOrdered
? styles.ordered_list_content
: styles.bullet_list_content;
return (
<View key={node.key} style={[styles.list_item, { flexShrink: 0 }]}>
<Text style={iconStyle}>{bullet}</Text>
<Text style={[contentStyle, { flex: 1, flexShrink: 1, minWidth: 0 }]} selectable>
{children}
</Text>
</View>
);
},
};
}, []);
const permissionResponse = useDaemonRequest<
{ agentId: string; requestId: string; response: AgentPermissionResponse },
{ agentId: string; requestId: string },
@@ -675,7 +780,7 @@ function PermissionRequestCard({
},
]}
>
<Markdown style={markdownStyles}>{planMarkdown}</Markdown>
<Markdown style={markdownStyles} rules={markdownRules}>{planMarkdown}</Markdown>
</View>
</View>
) : null}
@@ -986,6 +1091,8 @@ const permissionStyles = StyleSheet.create((theme) => ({
padding: theme.spacing[3],
borderRadius: theme.borderRadius.lg,
borderWidth: theme.borderWidth[1],
flexShrink: 1,
minWidth: 0,
},
metadataRow: {
marginBottom: theme.spacing[2],
+92
View File
@@ -234,6 +234,8 @@ const expandableBadgeStylesheet = StyleSheet.create((theme) => ({
backgroundColor: theme.colors.background,
padding: theme.spacing[2],
gap: theme.spacing[2],
flexShrink: 1,
minWidth: 0,
},
}));
@@ -417,6 +419,51 @@ export const AssistantMessage = memo(function AssistantMessage({
</Text>
);
},
bullet_list: (
node: any,
children: ReactNode[],
_parent: any,
styles: any
) => (
<View key={node.key} style={styles.bullet_list}>
{children}
</View>
),
ordered_list: (
node: any,
children: ReactNode[],
_parent: any,
styles: any
) => (
<View key={node.key} style={styles.ordered_list}>
{children}
</View>
),
list_item: (
node: any,
children: ReactNode[],
parent: any,
styles: any
) => {
const isOrdered = parent?.type === "ordered_list";
const index = parent?.children?.indexOf(node) ?? 0;
const bullet = isOrdered ? `${index + 1}.` : "•";
const iconStyle = isOrdered
? styles.ordered_list_icon
: styles.bullet_list_icon;
const contentStyle = isOrdered
? styles.ordered_list_content
: styles.bullet_list_content;
return (
<View key={node.key} style={styles.list_item}>
<Text style={iconStyle}>{bullet}</Text>
<View style={[contentStyle, { flex: 1, flexShrink: 1, minWidth: 0 }]}>
{children}
</View>
</View>
);
},
};
}, [onInlinePathPress]);
@@ -1007,6 +1054,51 @@ export function AgentThoughtMessage({ message, status = "ready" }: AgentThoughtM
{node.content}
</Text>
),
bullet_list: (
node: any,
children: ReactNode[],
_parent: any,
styles: any
) => (
<View key={node.key} style={styles.bullet_list}>
{children}
</View>
),
ordered_list: (
node: any,
children: ReactNode[],
_parent: any,
styles: any
) => (
<View key={node.key} style={styles.ordered_list}>
{children}
</View>
),
list_item: (
node: any,
children: ReactNode[],
parent: any,
styles: any
) => {
const isOrdered = parent?.type === "ordered_list";
const index = parent?.children?.indexOf(node) ?? 0;
const bullet = isOrdered ? `${index + 1}.` : "•";
const iconStyle = isOrdered
? styles.ordered_list_icon
: styles.bullet_list_icon;
const contentStyle = isOrdered
? styles.ordered_list_content
: styles.bullet_list_content;
return (
<View key={node.key} style={styles.list_item}>
<Text style={iconStyle}>{bullet}</Text>
<View style={[contentStyle, { flex: 1, flexShrink: 1, minWidth: 0 }]}>
{children}
</View>
</View>
);
},
};
}, []);
+5 -8
View File
@@ -215,13 +215,13 @@ export function createMarkdownStyles(theme: Theme) {
bullet_list: {
marginVertical: theme.spacing[2],
paddingLeft: theme.spacing[1],
paddingLeft: 0,
width: "100%" as const,
},
ordered_list: {
marginVertical: theme.spacing[2],
paddingLeft: theme.spacing[1],
paddingLeft: 0,
width: "100%" as const,
},
@@ -229,36 +229,33 @@ export function createMarkdownStyles(theme: Theme) {
marginBottom: theme.spacing[1],
flexDirection: "row" as const,
alignItems: "flex-start" as const,
width: "100%" as const,
flexShrink: 1,
},
bullet_list_content: {
flex: 1,
flexShrink: 1,
flexWrap: "wrap" as const,
},
ordered_list_content: {
flex: 1,
flexShrink: 1,
flexWrap: "wrap" as const,
},
bullet_list_icon: {
color: theme.colors.mutedForeground,
marginRight: theme.spacing[2],
marginRight: 4,
fontSize: theme.fontSize.base,
lineHeight: 22,
},
ordered_list_icon: {
color: theme.colors.mutedForeground,
marginRight: theme.spacing[2],
marginRight: 4,
fontSize: theme.fontSize.base,
fontWeight: theme.fontWeight.semibold,
lineHeight: 22,
minWidth: 20,
minWidth: 12,
},
// =========================================================================
@@ -5,48 +5,47 @@
*/
export function getOrchestratorModeInstructions(): string {
return `
<orchestrator-mode>
Activation:
- Only activate if the user explicitly says "go into orchestrator mode" (or similar).
- Otherwise, do work directly yourself; do not spawn agents.
## Orchestrator Mode Instructions
Core rules:
- In orchestrator mode, you accomplish tasks only by managing agents; do not perform the work yourself.
- Always prefix agent titles (e.g., "🎭 Feature Implementation", "🎭 Design Discussion").
- Set cwd to the repository root and choose the most permissive mode available.
- If an agent control call fails, list agents before launching another; it may just be a wait timeout.
When asked to go into orchestrator mode, you must **only accomplish tasks by managing other agents**. Do NOT perform the work yourself.
Context management:
- Reuse an existing agent when the next step needs the same context (same files/module/folder or immediate follow-up like investigate → fix in the same area).
- Start a new agent when switching to a different area/module, or when an agent has run long and its context feels stale.
- Prefer sending follow-up prompts to an existing agent to avoid reloading context.
- Use multiple agents when roles diverge (e.g., one for refactor, one for external validation), but default to reuse when context overlaps.
### Agent Control Best Practices
Conversation with agents:
- Engage actively: ask pointed questions, probe risks, and request clarifications before accepting proposals.
- Encourage agents to validate assumptions, consider edge cases, and describe how they will test/verify.
- **When agent control tool calls fail**, make sure you list agents before trying to launch another one. It could just be a wait timeout.
- **Always prefix agent titles** so we can tell which ones are running under you (e.g., "🎭 Feature Implementation", "🎭 Design Discussion").
- **Launch agents in the most permissive mode**: Use full access or bypass permissions mode.
- **Set cwd to the repository root** - The agent's working directory should usually be the repo root.
Agent selection guidance:
- Codex: methodical and slower; great for deep debugging, tracing code paths, refactoring, complex features, and design discussions.
- Claude: fast; strong at tool use (e.g., Playwright MCP, web search), agentic control, and managing other agents; may jump to conclusions—ask it to verify.
- For debugging with UI/Playwright: Claude can drive Playwright MCP and logging; Codex can audit code and propose fixes.
### Agent Use Cases
Clarifying ambiguous requests:
- Research first to understand the current state.
- Ask clarifying questions about what the user wants.
- Present options with trade-offs.
- Get explicit confirmation; never assume.
You can run agents to:
- **Implement a task** - Spawn an agent to write code and implement features
- **Have a design discussion** - Discuss architecture and design decisions
- **Have a pairing session** - Collaborate on problem-solving
- **Test some feature** - Run tests and verify functionality
- **Do investigation** - Research and explore the codebase
Investigation vs Implementation:
- Investigate only unless explicitly asked to implement.
- Report findings clearly.
- After investigation, ask for direction before implementing.
### Clarifying Ambiguous Requests
**CRITICAL:** When user requests are ambiguous or unclear:
1. **Research first** - Spawn an investigation agent to understand the current state
2. **Ask clarifying questions** - After research, ask the user specific questions about what they want
3. **Present options** - Offer multiple approaches with trade-offs
4. **Get explicit confirmation** - Never assume what the user wants
### Investigation vs Implementation
**CRITICAL:** When asked to investigate:
- **Investigate only** - Do not implement fixes during investigation unless explicitly requested
- **Report findings** - Share discovered information clearly
- **Ask for direction** - After investigation, ask the user what to do next
### Tool Usage Discipline
- **Do not ask users to run commands** — Always run the commands yourself.
- **Do not repeat the user’s instructions verbatim** — Summarize them in your own words.
- **Be explicit about results** — Tell the user what happened after every command.
Tool usage discipline:
- Do not ask users to run commands—run them yourself.
- Do not repeat the user’s instructions verbatim—summarize them in your own words.
- Be explicit about results—tell the user what happened after every command.
</orchestrator-mode>
`;
}