diff --git a/packages/cli/src/ui/utils/InlineMarkdownRenderer.test.ts b/packages/cli/src/ui/utils/InlineMarkdownRenderer.test.ts
deleted file mode 100644
index 11fb6d56eb..0000000000
--- a/packages/cli/src/ui/utils/InlineMarkdownRenderer.test.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { getPlainTextLength } from './InlineMarkdownRenderer.js';
-import { describe, it, expect } from 'vitest';
-
-describe('getPlainTextLength', () => {
- it.each([
- ['**Primary Go', 12],
- ['*Primary Go', 11],
- ['**Primary Go**', 10],
- ['*Primary Go*', 10],
- ['**', 2],
- ['*', 1],
- ['compile-time**', 14],
- ])(
- 'should measure markdown text length correctly for "%s"',
- (input, expected) => {
- expect(getPlainTextLength(input)).toBe(expected);
- },
- );
-});
diff --git a/packages/cli/src/ui/utils/InlineMarkdownRenderer.test.tsx b/packages/cli/src/ui/utils/InlineMarkdownRenderer.test.tsx
new file mode 100644
index 0000000000..2b9e0bf645
--- /dev/null
+++ b/packages/cli/src/ui/utils/InlineMarkdownRenderer.test.tsx
@@ -0,0 +1,53 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { describe, it, expect } from 'vitest';
+import { RenderInline, getPlainTextLength } from './InlineMarkdownRenderer.js';
+import { renderWithProviders } from '../../test-utils/render.js';
+import { Text } from 'ink';
+
+describe('InlineMarkdownRenderer', () => {
+ describe('getPlainTextLength', () => {
+ it.each([
+ ['**Primary Go', 12],
+ ['*Primary Go', 11],
+ ['**Primary Go**', 10],
+ ['*Primary Go*', 10],
+ ['**', 2],
+ ['*', 1],
+ ['compile-time**', 14],
+ ['$\\rightarrow$', 1],
+ ['Sign Out $\\rightarrow$ Sign In', 18],
+ ])(
+ 'should measure markdown text length correctly for "%s"',
+ (input, expected) => {
+ expect(getPlainTextLength(input)).toBe(expected);
+ },
+ );
+ });
+
+ describe('', () => {
+ it('renders LaTeX rightarrow correctly', () => {
+ const text = 'Sign Out $\\rightarrow$ Sign In';
+ const { lastFrame } = renderWithProviders(
+
+
+ ,
+ );
+ expect(lastFrame()).toContain('Sign Out → Sign In');
+ });
+
+ it('renders other LaTeX arrows correctly', () => {
+ const text = '$\\leftarrow$ $\\uparrow$ $\\downarrow$';
+ const { lastFrame } = renderWithProviders(
+
+
+ ,
+ );
+ expect(lastFrame()).toContain('← ↑ ↓');
+ });
+ });
+});
diff --git a/packages/cli/src/ui/utils/InlineMarkdownRenderer.tsx b/packages/cli/src/ui/utils/InlineMarkdownRenderer.tsx
index 8d4c6a7da6..dcba8bf80e 100644
--- a/packages/cli/src/ui/utils/InlineMarkdownRenderer.tsx
+++ b/packages/cli/src/ui/utils/InlineMarkdownRenderer.tsx
@@ -36,7 +36,7 @@ const RenderInlineInternal: React.FC = ({
const nodes: React.ReactNode[] = [];
let lastIndex = 0;
const inlineRegex =
- /(\*\*.*?\*\*|\*.*?\*|_.*?_|~~.*?~~|\[.*?\]\(.*?\)|`+.+?`+|.*?<\/u>|https?:\/\/\S+)/g;
+ /(\*\*.*?\*\*|\*.*?\*|_.*?_|~~.*?~~|\[.*?\]\(.*?\)|`+.+?`+|.*?<\/u>|https?:\/\/\S+|\$\\[a-z]+\$)/g;
let match;
while ((match = inlineRegex.exec(text)) !== null) {
@@ -143,6 +143,21 @@ const RenderInlineInternal: React.FC = ({
{fullMatch}
);
+ } else if (fullMatch.startsWith('$') && fullMatch.endsWith('$')) {
+ const latexMap: Record = {
+ '$\\rightarrow$': '→',
+ '$\\leftarrow$': '←',
+ '$\\uparrow$': '↑',
+ '$\\downarrow$': '↓',
+ };
+ const replacement = latexMap[fullMatch];
+ if (replacement) {
+ renderedNode = (
+
+ {replacement}
+
+ );
+ }
}
} catch (e) {
debugLogger.warn('Error parsing inline markdown part:', fullMatch, e);
@@ -184,6 +199,7 @@ export const getPlainTextLength = (text: string): number => {
.replace(/~~(.*?)~~/g, '$1')
.replace(/`(.*?)`/g, '$1')
.replace(/(.*?)<\/u>/g, '$1')
- .replace(/.*\[(.*?)\]\(.*\)/g, '$1');
+ .replace(/.*\[(.*?)\]\(.*\)/g, '$1')
+ .replace(/\$\\(rightarrow|leftarrow|uparrow|downarrow)\$/g, '→');
return stringWidth(cleanText);
};