diff --git a/config/mobile-localization-coverage-allowlist.json b/config/mobile-localization-coverage-allowlist.json index d528f3983ca..08e1fb1f9cf 100644 --- a/config/mobile-localization-coverage-allowlist.json +++ b/config/mobile-localization-coverage-allowlist.json @@ -133,14 +133,14 @@ "count": 1 }, { - "filePath": "mobile/src/components/mobile-markdown-blocks.tsx", + "filePath": "mobile/src/components/markdown/blocks/mobile-markdown-blocks.tsx", "kind": "jsx-expression", "text": "[x]", "dynamic": false, "count": 1 }, { - "filePath": "mobile/src/components/MobileRichMarkdownEditor.tsx", + "filePath": "mobile/src/components/rich-markdown/MobileRichMarkdownEditor.tsx", "kind": "jsx-attribute:placeholder", "text": "https://", "dynamic": false, diff --git a/config/scripts/mobile-web-app/preview/mobile-web-app-html-preview-render.test.mjs b/config/scripts/mobile-web-app/preview/mobile-web-app-html-preview-render.test.mjs index 7ed7e9dc360..17132e2e95c 100644 --- a/config/scripts/mobile-web-app/preview/mobile-web-app-html-preview-render.test.mjs +++ b/config/scripts/mobile-web-app/preview/mobile-web-app-html-preview-render.test.mjs @@ -300,14 +300,12 @@ for (const engine of ['chromium', 'webkit']) { if (engine === 'chromium') { expect(shown.links?.cursor).toBe('pointer') expect(hidden.links?.cursor).not.toBe('pointer') - } else { - expect(shown.links?.cursor).toBe(hidden.links?.cursor) } expect(shown.mountedSandbox).toBe('allow-top-navigation-by-user-activation') }, 240_000) - // Parser round-trip must preserve artifact rendering beyond links; real browser catches doctype/newline losses happy-dom misses. Granted arm is unchanged control. - it('changes nothing an engine renders except that links are not links', async (ctx) => { + // Check painted fixtures, granted document content and inert fragment targets. + it('paints both artifact variants and keeps hidden fragment links inert', async (ctx) => { // A doctype with a public identifier and no system identifier, which is quirks. The bare // name is not, so a pass that rewrote one as the other moves the whole artifact between // layout modes -- which is what this arm exists to catch. @@ -318,21 +316,18 @@ for (const engine of ['chromium', 'webkit']) { doctype: quirks, grants: ['navigate', 'storage'] }) - // Both painted, so every comparison below is between two rendered documents. + // Both fixture variants painted. expect(shown.pixel).toBe(ARTIFACT_RGB) expect(hidden.pixel).toBe(ARTIFACT_RGB) - // Doctype public/system IDs discriminate preservation where inherited compatMode cannot. + // Known granted doctype remains distinct from the bare-doctype control. expect(shown.inside?.doctypePublicId).toBe('-//W3C//DTD HTML 4.01 Transitional//EN') - expect(hidden.inside?.doctypePublicId).toBe(shown.inside?.doctypePublicId) - expect(hidden.inside?.doctypeSystemId).toBe(shown.inside?.doctypeSystemId) // Bare-doctype control proves ID reader can distinguish lost identifier. const bare = await open(browser(), { signal: ctx.signal, grants: ['navigate', 'storage'] }) expect(bare.inside?.doctypePublicId).toBe('') - // Browser parser drops leading pre newline; inert-link serializer must restore it. + // Granted fixture retains the leading pre newline. expect(shown.inside?.preText).toBe('\nkept') - expect(hidden.inside?.preText).toBe(shown.inside?.preText) // Granted fragment remains known broken navigation; hidden arm must strip it rather than preserve false scroll affordance. expect(shown.inside?.fragmentHref).toBe('#fragtarget') diff --git a/config/scripts/mobile-web-app/preview/mobile-web-app-preview-artifact-fixture.mjs b/config/scripts/mobile-web-app/preview/mobile-web-app-preview-artifact-fixture.mjs index 76a91e81d8b..6c3bcd20cdf 100644 --- a/config/scripts/mobile-web-app/preview/mobile-web-app-preview-artifact-fixture.mjs +++ b/config/scripts/mobile-web-app/preview/mobile-web-app-preview-artifact-fixture.mjs @@ -14,7 +14,7 @@ import { MobileHtmlPreview, MOBILE_HTML_PREVIEW_SANDBOX, MOBILE_HTML_PREVIEW_SEALED_SANDBOX -} from './MobileHtmlPreview' +} from '../files/preview/documents/MobileHtmlPreview' window.__sandbox = MOBILE_HTML_PREVIEW_SANDBOX window.__sealedSandbox = MOBILE_HTML_PREVIEW_SEALED_SANDBOX diff --git a/config/scripts/mobile-web-app/session/mobile-web-app-rich-markdown-render.test.mjs b/config/scripts/mobile-web-app/session/mobile-web-app-rich-markdown-render.test.mjs index af884f5aa77..efd89faaadb 100644 --- a/config/scripts/mobile-web-app/session/mobile-web-app-rich-markdown-render.test.mjs +++ b/config/scripts/mobile-web-app/session/mobile-web-app-rich-markdown-render.test.mjs @@ -52,7 +52,7 @@ const PAGE_ENTRY = ` import { createElement, useEffect, useRef, useState } from 'react' import { createRoot } from 'react-dom/client' import { SafeAreaProvider } from 'react-native-safe-area-context' -import { MobileRichMarkdownEditor } from './MobileRichMarkdownEditor' +import { MobileRichMarkdownEditor } from './rich-markdown/MobileRichMarkdownEditor' import { applyMobileColorScheme } from '../theme/mobile-theme' /** diff --git a/config/scripts/performance/mobile-projection/mobile-markdown-placeholder-benchmark.mjs b/config/scripts/performance/mobile-projection/mobile-markdown-placeholder-benchmark.mjs index 207a2d54bf2..e76d6f15e75 100644 --- a/config/scripts/performance/mobile-projection/mobile-markdown-placeholder-benchmark.mjs +++ b/config/scripts/performance/mobile-projection/mobile-markdown-placeholder-benchmark.mjs @@ -5,7 +5,8 @@ import { dirname, resolve } from 'node:path' import { performance } from 'node:perf_hooks' import { build } from 'esbuild' -const sourcePath = 'mobile/src/components/mobile-markdown-preview-html.ts' +const currentSourcePath = 'mobile/src/components/markdown/parsing/mobile-markdown-preview-html.ts' +const legacyBaselineSourcePath = 'mobile/src/components/mobile-markdown-preview-html.ts' const baselineRef = process.argv[2] if (!baselineRef) { throw new Error( @@ -14,7 +15,7 @@ if (!baselineRef) { } async function load(source) { const result = await build({ - stdin: { contents: source, resolveDir: dirname(resolve(sourcePath)), loader: 'ts' }, + stdin: { contents: source, resolveDir: dirname(resolve(currentSourcePath)), loader: 'ts' }, bundle: true, write: false, platform: 'node', @@ -26,10 +27,25 @@ async function load(source) { ) ).normalizeMobileMarkdownPreviewHtml } -const before = await load( - execFileSync('git', ['show', `${baselineRef}:${sourcePath}`], { encoding: 'utf8' }) +const baselinePaths = execFileSync( + 'git', + ['ls-tree', '-r', '--name-only', baselineRef, '--', currentSourcePath, legacyBaselineSourcePath], + { encoding: 'utf8' } ) -const after = await load(readFileSync(sourcePath, 'utf8')) + .trim() + .split('\n') +const baselineSourcePath = baselinePaths.includes(currentSourcePath) + ? currentSourcePath + : baselinePaths.includes(legacyBaselineSourcePath) + ? legacyBaselineSourcePath + : null +if (baselineSourcePath === null) { + throw new Error(`Baseline contains neither Markdown preview source path: ${baselineRef}`) +} +const before = await load( + execFileSync('git', ['show', `${baselineRef}:${baselineSourcePath}`], { encoding: 'utf8' }) +) +const after = await load(readFileSync(currentSourcePath, 'utf8')) function measure(fn, input, repeats) { const samples = [] for (let run = 0; run < repeats; run++) { diff --git a/mobile/.gitignore b/mobile/.gitignore index 3fe8348c3f8..429bb0e26ce 100644 --- a/mobile/.gitignore +++ b/mobile/.gitignore @@ -3,7 +3,7 @@ src/terminal/document/terminal-webview-engine.generated.ts src/terminal/document/terminal-webview-engine-css.generated.ts src/terminal/document/terminal-webview-document-script.generated.ts src/components/mermaid/mermaid-page-engine.generated.ts -src/components/katex-page-style.generated.ts +src/components/markdown/math/katex-page-style.generated.ts .expo/ dist/ /android/ diff --git a/mobile/.oxlintrc.json b/mobile/.oxlintrc.json index 6782a9e2661..a6f501215b0 100644 --- a/mobile/.oxlintrc.json +++ b/mobile/.oxlintrc.json @@ -2,10 +2,10 @@ "$schema": "./node_modules/oxlint/configuration_schema.json", "extends": ["../.oxlintrc.json"], "ignorePatterns": [ - "src/components/mobile-markdown-mdast-nodes.d.ts", + "src/components/markdown/mobile-markdown-mdast-nodes.d.ts", "src/terminal/document/terminal-webview-engine.generated.ts", "src/components/mermaid/mermaid-page-engine.generated.ts", - "src/components/katex-page-style.generated.ts" + "src/components/markdown/math/katex-page-style.generated.ts" ], "rules": { "react-hooks/exhaustive-deps": "off", diff --git a/mobile/scripts/build-katex-page-style.mjs b/mobile/scripts/build-katex-page-style.mjs index a9a37c88742..25fbdd0b812 100644 --- a/mobile/scripts/build-katex-page-style.mjs +++ b/mobile/scripts/build-katex-page-style.mjs @@ -4,7 +4,14 @@ import { createRequire } from 'node:module' const require = createRequire(import.meta.url) const mobileRoot = path.resolve(import.meta.dirname, '..') -const outputPath = path.join(mobileRoot, 'src', 'components', 'katex-page-style.generated.ts') +const outputPath = path.join( + mobileRoot, + 'src', + 'components', + 'markdown', + 'math', + 'katex-page-style.generated.ts' +) // KaTeX's stylesheet for the page, woff2 only: every shell WebView reads it, and the bundle // emits each imported font as a same-origin asset, which the shell's font-src allows. diff --git a/mobile/src/components/MobileSourceCopyButton.tsx b/mobile/src/components/code-presentation/MobileSourceCopyButton.tsx similarity index 91% rename from mobile/src/components/MobileSourceCopyButton.tsx rename to mobile/src/components/code-presentation/MobileSourceCopyButton.tsx index 931f13a2ed2..aa53f2080e1 100644 --- a/mobile/src/components/MobileSourceCopyButton.tsx +++ b/mobile/src/components/code-presentation/MobileSourceCopyButton.tsx @@ -1,12 +1,12 @@ import { useLayoutEffect, useRef, useState } from 'react' import { Alert, Platform, Pressable, Text } from 'react-native' import { Check, Copy } from 'lucide-react-native' -import { translate } from '../i18n/mobile-i18n' -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { useClipboardWriter } from '../platform/clipboard/clipboard' -import { colors, radii, spacing, typography } from '../theme/mobile-theme' -import { themedStyleSheet } from '../theme/themed-styles' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import { translate } from '../../i18n/mobile-i18n' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' +import { useClipboardWriter } from '../../platform/clipboard/clipboard' +import { colors, radii, spacing, typography } from '../../theme/mobile-theme' +import { themedStyleSheet } from '../../theme/themed-styles' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' type CopyFeedback = { status: 'copied'; text: string } | { status: 'failed' } diff --git a/mobile/src/components/MobileSyntaxSegments.tsx b/mobile/src/components/code-presentation/MobileSyntaxSegments.tsx similarity index 80% rename from mobile/src/components/MobileSyntaxSegments.tsx rename to mobile/src/components/code-presentation/MobileSyntaxSegments.tsx index 9f06e26fa39..537249c3e92 100644 --- a/mobile/src/components/MobileSyntaxSegments.tsx +++ b/mobile/src/components/code-presentation/MobileSyntaxSegments.tsx @@ -2,10 +2,10 @@ import { Text, type TextStyle } from 'react-native' import type { MobileSyntaxSegment, MobileSyntaxTokenKind -} from '../files/session-documents/mobile-file-syntax' -import { colors } from '../theme/mobile-theme' -import { themedStyleSheet } from '../theme/themed-styles' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +} from '../../files/session-documents/mobile-file-syntax' +import { colors } from '../../theme/mobile-theme' +import { themedStyleSheet } from '../../theme/themed-styles' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' export function MobileSyntaxSegments({ segments }: { segments: MobileSyntaxSegment[] }) { useMobileColorScheme() diff --git a/mobile/src/components/MobileMarkdown.test.ts b/mobile/src/components/markdown/MobileMarkdown.test.ts similarity index 99% rename from mobile/src/components/MobileMarkdown.test.ts rename to mobile/src/components/markdown/MobileMarkdown.test.ts index 7e4ed7fbf7e..e512ef80127 100644 --- a/mobile/src/components/MobileMarkdown.test.ts +++ b/mobile/src/components/markdown/MobileMarkdown.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { normalizeMobileMarkdownPreviewHtml } from './mobile-markdown-preview-html' +import { normalizeMobileMarkdownPreviewHtml } from './parsing/mobile-markdown-preview-html' describe('normalizeMobileMarkdownPreviewHtml', () => { it('normalizes common README HTML into readable Markdown preview text', () => { diff --git a/mobile/src/components/MobileMarkdown.tsx b/mobile/src/components/markdown/MobileMarkdown.tsx similarity index 88% rename from mobile/src/components/MobileMarkdown.tsx rename to mobile/src/components/markdown/MobileMarkdown.tsx index b632f789329..cbcd802af29 100644 --- a/mobile/src/components/MobileMarkdown.tsx +++ b/mobile/src/components/markdown/MobileMarkdown.tsx @@ -1,19 +1,19 @@ -import { INLINE_TEXT_SELECTION } from './inline-text-selection' +import { INLINE_TEXT_SELECTION } from '../../platform/text-selection/inline-text-selection' import { memo, useContext, useMemo, type ReactNode } from 'react' import { Text as NativeText, View } from 'react-native' import { MARKDOWN_TONES, scaledMarkdownText, styles } from './mobile-markdown-styles' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { renderBlocks, type MarkdownBlockContext } from './mobile-markdown-blocks' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import { renderBlocks, type MarkdownBlockContext } from './blocks/mobile-markdown-blocks' import { MarkdownText } from './mobile-markdown-text' -import { MobileMarkdownRemoteImage } from './MobileMarkdownRemoteImage' +import { MobileMarkdownRemoteImage } from './blocks/MobileMarkdownRemoteImage' import { MarkdownTextContext, type MarkdownTextSetup } from './mobile-markdown-text-setup' -import { MarkdownLinkHandlerContext } from './markdown-link-handler' -import { useMobileMarkdownTree } from './use-mobile-markdown-tree' +import { MarkdownLinkHandlerContext } from './links/markdown-link-handler' +import { useMobileMarkdownTree } from './parsing/use-mobile-markdown-tree' import { MobileMarkdownLinksContext, useNativeChatFileLinkExistsFrom -} from './mobile-markdown-links-context' -import type { NativeChatVisualDirective } from '../../../src/shared/native-chat/native-chat-visual-directive' +} from './links/mobile-markdown-links-context' +import type { NativeChatVisualDirective } from '../../../../src/shared/native-chat/native-chat-visual-directive' type Props = { content?: string diff --git a/mobile/src/components/MobileMarkdownCallout.tsx b/mobile/src/components/markdown/blocks/MobileMarkdownCallout.tsx similarity index 87% rename from mobile/src/components/MobileMarkdownCallout.tsx rename to mobile/src/components/markdown/blocks/MobileMarkdownCallout.tsx index d08ddcee680..58f0a916665 100644 --- a/mobile/src/components/MobileMarkdownCallout.tsx +++ b/mobile/src/components/markdown/blocks/MobileMarkdownCallout.tsx @@ -7,11 +7,11 @@ import { OctagonAlert, TriangleAlert } from 'lucide-react-native' -import type { GitHubCalloutKind } from '../../../src/shared/github/markdown/github-callouts' -import { translate } from '../i18n/mobile-i18n' -import { styles } from './mobile-markdown-styles' -import { colors } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import type { GitHubCalloutKind } from '../../../../../src/shared/github/markdown/github-callouts' +import { translate } from '../../../i18n/mobile-i18n' +import { styles } from '../mobile-markdown-styles' +import { colors } from '../../../theme/mobile-theme' +import { useMobileColorScheme } from '../../../theme/use-mobile-color-scheme' // Desktop's markdown-github-callout.tsx in the phone's tokens. const CALLOUTS: Record string; label: () => string }> = { diff --git a/mobile/src/components/MobileMarkdownCodeBlock.tsx b/mobile/src/components/markdown/blocks/MobileMarkdownCodeBlock.tsx similarity index 83% rename from mobile/src/components/MobileMarkdownCodeBlock.tsx rename to mobile/src/components/markdown/blocks/MobileMarkdownCodeBlock.tsx index 23e38937e2a..b32cf93c94f 100644 --- a/mobile/src/components/MobileMarkdownCodeBlock.tsx +++ b/mobile/src/components/markdown/blocks/MobileMarkdownCodeBlock.tsx @@ -1,19 +1,19 @@ import { TextWrap } from 'lucide-react-native' import { memo, useContext, useMemo } from 'react' import { Pressable, ScrollView, Text, View } from 'react-native' -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { highlightMobileCode } from '../files/session-documents/mobile-file-syntax' +import { useMobileTranslation } from '../../../i18n/use-mobile-translation' +import { highlightMobileCode } from '../../../files/session-documents/mobile-file-syntax' import { MobileNativeChatDisclosureContext, MobileNativeChatMarkdownRowKeyContext, useMobileNativeChatDisclosure -} from '../session/native-chat/transcript/viewport/mobile-native-chat-disclosure-store' -import { colors, pressedStyle } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { MobileSyntaxSegments } from './MobileSyntaxSegments' -import { MobileSourceCopyButton } from './MobileSourceCopyButton' -import { scaledMarkdownText, styles } from './mobile-markdown-styles' -import { MarkdownText } from './mobile-markdown-text' +} from '../../../session/native-chat/transcript/viewport/mobile-native-chat-disclosure-store' +import { colors, pressedStyle } from '../../../theme/mobile-theme' +import { useMobileColorScheme } from '../../../theme/use-mobile-color-scheme' +import { MobileSyntaxSegments } from '../../code-presentation/MobileSyntaxSegments' +import { MobileSourceCopyButton } from '../../code-presentation/MobileSourceCopyButton' +import { scaledMarkdownText, styles } from '../mobile-markdown-styles' +import { MarkdownText } from '../mobile-markdown-text' export const MobileMarkdownCodeBlock = memo(function MobileMarkdownCodeBlock({ code, diff --git a/mobile/src/components/MobileMarkdownDetails.tsx b/mobile/src/components/markdown/blocks/MobileMarkdownDetails.tsx similarity index 82% rename from mobile/src/components/MobileMarkdownDetails.tsx rename to mobile/src/components/markdown/blocks/MobileMarkdownDetails.tsx index f121a4d4514..557618f35d3 100644 --- a/mobile/src/components/MobileMarkdownDetails.tsx +++ b/mobile/src/components/markdown/blocks/MobileMarkdownDetails.tsx @@ -1,10 +1,10 @@ import { useState, type ReactNode } from 'react' import { Pressable, Text, View, type StyleProp, type TextStyle } from 'react-native' import { ChevronRight } from 'lucide-react-native' -import { DisclosureChevron } from './DisclosureChevron' -import { styles } from './mobile-markdown-styles' -import { colors, pressedStyle } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import { DisclosureChevron } from '../../DisclosureChevron' +import { styles } from '../mobile-markdown-styles' +import { colors, pressedStyle } from '../../../theme/mobile-theme' +import { useMobileColorScheme } from '../../../theme/use-mobile-color-scheme' /** A `
` section: closed unless authored `open`, like desktop and GitHub. */ export function MobileMarkdownDetails({ diff --git a/mobile/src/components/MobileMarkdownRemoteImage.tsx b/mobile/src/components/markdown/blocks/MobileMarkdownRemoteImage.tsx similarity index 85% rename from mobile/src/components/MobileMarkdownRemoteImage.tsx rename to mobile/src/components/markdown/blocks/MobileMarkdownRemoteImage.tsx index 238542dbba4..e8cc1eacd99 100644 --- a/mobile/src/components/MobileMarkdownRemoteImage.tsx +++ b/mobile/src/components/markdown/blocks/MobileMarkdownRemoteImage.tsx @@ -1,9 +1,9 @@ import { useContext, useEffect, useState } from 'react' import { Image, Pressable, StyleSheet, Text } from 'react-native' -import { MarkdownLinkHandlerContext } from './markdown-link-handler' -import { styles as markdownStyles } from './mobile-markdown-styles' -import { pressedStyle } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import { MarkdownLinkHandlerContext } from '../links/markdown-link-handler' +import { styles as markdownStyles } from '../mobile-markdown-styles' +import { pressedStyle } from '../../../theme/mobile-theme' +import { useMobileColorScheme } from '../../../theme/use-mobile-color-scheme' type Size = { width: number; height: number } diff --git a/mobile/src/components/mobile-markdown-blocks.tsx b/mobile/src/components/markdown/blocks/mobile-markdown-blocks.tsx similarity index 94% rename from mobile/src/components/mobile-markdown-blocks.tsx rename to mobile/src/components/markdown/blocks/mobile-markdown-blocks.tsx index df233f27e6c..0cc0a9c48be 100644 --- a/mobile/src/components/mobile-markdown-blocks.tsx +++ b/mobile/src/components/markdown/blocks/mobile-markdown-blocks.tsx @@ -15,28 +15,28 @@ import { type StyleProp, type TextStyle } from 'react-native' -import type { NativeChatVisualDirective } from '../../../src/shared/native-chat/native-chat-visual-directive' +import type { NativeChatVisualDirective } from '../../../../../src/shared/native-chat/native-chat-visual-directive' import { isMobileMermaidLanguage } from './mobile-mermaid-language' -import { mobileMathMeasureText } from './mobile-markdown-math-nodes' +import { mobileMathMeasureText } from '../math/mobile-markdown-math-nodes' import { mobileMarkdownTableColumnWidths } from './mobile-markdown-table-columns' import { mobileMarkdownVisualPlaceholderIndex } from './mobile-markdown-visual-lines' import { openMarkdownHref, renderInline, type MarkdownInlineContext -} from './mobile-markdown-inline' -import { MARKDOWN_TONES, scaledMarkdownText, styles } from './mobile-markdown-styles' -import { MarkdownText } from './mobile-markdown-text' +} from '../parsing/mobile-markdown-inline' +import { MARKDOWN_TONES, scaledMarkdownText, styles } from '../mobile-markdown-styles' +import { MarkdownText } from '../mobile-markdown-text' import { codeFenceClosed } from './mobile-markdown-code-nodes' -import { plainText } from './mobile-markdown-plain-text' -import { MermaidDiagram } from './mermaid/MermaidDiagram' +import { plainText } from '../parsing/mobile-markdown-plain-text' +import { MermaidDiagram } from '../../mermaid/MermaidDiagram' import { MobileMarkdownCallout } from './MobileMarkdownCallout' import { MobileMarkdownCodeBlock } from './MobileMarkdownCodeBlock' import { MobileMarkdownDetails } from './MobileMarkdownDetails' -import { MobileMarkdownMath } from './MobileMarkdownMath' +import { MobileMarkdownMath } from '../math/MobileMarkdownMath' import { drawnBlock } from './mobile-markdown-drawn-blocks' -import { translate } from '../i18n/mobile-i18n' -import { colors, pressedStyle } from '../theme/mobile-theme' +import { translate } from '../../../i18n/mobile-i18n' +import { colors, pressedStyle } from '../../../theme/mobile-theme' /** Prose base size — passed to MermaidDiagram fallback mono text. */ const MERMAID_BASE = 13 diff --git a/mobile/src/components/mobile-markdown-code-nodes.ts b/mobile/src/components/markdown/blocks/mobile-markdown-code-nodes.ts similarity index 100% rename from mobile/src/components/mobile-markdown-code-nodes.ts rename to mobile/src/components/markdown/blocks/mobile-markdown-code-nodes.ts diff --git a/mobile/src/components/mobile-markdown-details.ts b/mobile/src/components/markdown/blocks/mobile-markdown-details.ts similarity index 100% rename from mobile/src/components/mobile-markdown-details.ts rename to mobile/src/components/markdown/blocks/mobile-markdown-details.ts diff --git a/mobile/src/components/mobile-markdown-drawn-blocks.ts b/mobile/src/components/markdown/blocks/mobile-markdown-drawn-blocks.ts similarity index 95% rename from mobile/src/components/mobile-markdown-drawn-blocks.ts rename to mobile/src/components/markdown/blocks/mobile-markdown-drawn-blocks.ts index 84aab961dbe..5993d115dce 100644 --- a/mobile/src/components/mobile-markdown-drawn-blocks.ts +++ b/mobile/src/components/markdown/blocks/mobile-markdown-drawn-blocks.ts @@ -1,7 +1,7 @@ import type { RootContent } from 'mdast' import type { ReactNode } from 'react' -import { getMobileUiRevision } from '../i18n/mobile-i18n' -import { getMobileColorScheme } from '../theme/mobile-theme' +import { getMobileUiRevision } from '../../../i18n/mobile-i18n' +import { getMobileColorScheme } from '../../../theme/mobile-theme' import type { MarkdownBlockContext, ParagraphText } from './mobile-markdown-blocks' type DrawnBlock = { diff --git a/mobile/src/components/mobile-markdown-image-paragraph.tsx b/mobile/src/components/markdown/blocks/mobile-markdown-image-paragraph.tsx similarity index 93% rename from mobile/src/components/mobile-markdown-image-paragraph.tsx rename to mobile/src/components/markdown/blocks/mobile-markdown-image-paragraph.tsx index 1eac5a24658..b98408cdeac 100644 --- a/mobile/src/components/mobile-markdown-image-paragraph.tsx +++ b/mobile/src/components/markdown/blocks/mobile-markdown-image-paragraph.tsx @@ -1,9 +1,9 @@ import type { Image, PhrasingContent } from 'mdast' import { Fragment, type ReactNode } from 'react' import { View } from 'react-native' -import { MarkdownText } from './mobile-markdown-text' -import { renderInline } from './mobile-markdown-inline' -import { styles } from './mobile-markdown-styles' +import { MarkdownText } from '../mobile-markdown-text' +import { renderInline } from '../parsing/mobile-markdown-inline' +import { styles } from '../mobile-markdown-styles' import type { MarkdownBlockContext, ParagraphText } from './mobile-markdown-blocks' function isBlankInline(node: PhrasingContent): boolean { diff --git a/mobile/src/components/mobile-markdown-image-placement.ts b/mobile/src/components/markdown/blocks/mobile-markdown-image-placement.ts similarity index 100% rename from mobile/src/components/mobile-markdown-image-placement.ts rename to mobile/src/components/markdown/blocks/mobile-markdown-image-placement.ts diff --git a/mobile/src/components/mobile-markdown-table-columns.ts b/mobile/src/components/markdown/blocks/mobile-markdown-table-columns.ts similarity index 96% rename from mobile/src/components/mobile-markdown-table-columns.ts rename to mobile/src/components/markdown/blocks/mobile-markdown-table-columns.ts index c435ce26746..82d950319e9 100644 --- a/mobile/src/components/mobile-markdown-table-columns.ts +++ b/mobile/src/components/markdown/blocks/mobile-markdown-table-columns.ts @@ -1,4 +1,4 @@ -import { spacing } from '../theme/mobile-theme' +import { spacing } from '../../../theme/mobile-theme' // Measured 12px advances: 6.4 regular, 6.9 bold, up to 8.7 per bold glyph. Estimation affects wrapping, never column alignment. const AVERAGE_GLYPH_WIDTH = 7 diff --git a/mobile/src/components/mobile-markdown-visual-lines.ts b/mobile/src/components/markdown/blocks/mobile-markdown-visual-lines.ts similarity index 93% rename from mobile/src/components/mobile-markdown-visual-lines.ts rename to mobile/src/components/markdown/blocks/mobile-markdown-visual-lines.ts index 30911e0f8b8..a7b8aa35d1b 100644 --- a/mobile/src/components/mobile-markdown-visual-lines.ts +++ b/mobile/src/components/markdown/blocks/mobile-markdown-visual-lines.ts @@ -3,8 +3,8 @@ import { NATIVE_CHAT_VISUAL_MAX_PER_MESSAGE, parseNativeChatVisualDirectiveLine, type NativeChatVisualDirective -} from '../../../src/shared/native-chat/native-chat-visual-directive' -import { mobileMarkdownCodeLines } from './mobile-markdown-tree' +} from '../../../../../src/shared/native-chat/native-chat-visual-directive' +import { mobileMarkdownCodeLines } from '../parsing/mobile-markdown-tree' // Private-use delimiters, as the preview normalizer's own code placeholders use. const PLACEHOLDER_PREFIX = '\uE000ORCA_VISUAL_' const PLACEHOLDER = /^\uE000ORCA_VISUAL_(\d+)\uE000$/ diff --git a/mobile/src/components/mobile-mermaid-language.ts b/mobile/src/components/markdown/blocks/mobile-mermaid-language.ts similarity index 100% rename from mobile/src/components/mobile-mermaid-language.ts rename to mobile/src/components/markdown/blocks/mobile-mermaid-language.ts diff --git a/mobile/src/components/markdown-file-path-detection.test.ts b/mobile/src/components/markdown/links/markdown-file-path-detection.test.ts similarity index 100% rename from mobile/src/components/markdown-file-path-detection.test.ts rename to mobile/src/components/markdown/links/markdown-file-path-detection.test.ts diff --git a/mobile/src/components/markdown-file-path-detection.ts b/mobile/src/components/markdown/links/markdown-file-path-detection.ts similarity index 98% rename from mobile/src/components/markdown-file-path-detection.ts rename to mobile/src/components/markdown/links/markdown-file-path-detection.ts index 94707ea2eef..f9da9485b7a 100644 --- a/mobile/src/components/markdown-file-path-detection.ts +++ b/mobile/src/components/markdown/links/markdown-file-path-detection.ts @@ -1,6 +1,6 @@ // Prefer precise chat file paths over recall: missed link is tolerable, false prose/version tap is broken navigation. -import { parseFileLinkLocation } from '../../../src/shared/file-links/file-link-location' +import { parseFileLinkLocation } from '../../../../../src/shared/file-links/file-link-location' export type FilePathSegment = | { type: 'text'; value: string } diff --git a/mobile/src/components/markdown-href-routing.ts b/mobile/src/components/markdown/links/markdown-href-routing.ts similarity index 83% rename from mobile/src/components/markdown-href-routing.ts rename to mobile/src/components/markdown/links/markdown-href-routing.ts index 6c16e46dc63..f4782e9d6ee 100644 --- a/mobile/src/components/markdown-href-routing.ts +++ b/mobile/src/components/markdown/links/markdown-href-routing.ts @@ -1,4 +1,4 @@ -import { routeNativeChatHref } from '../../../src/shared/native-chat/native-chat-href-routing' +import { routeNativeChatHref } from '../../../../../src/shared/native-chat/native-chat-href-routing' export type MarkdownHrefRoute = | { kind: 'web'; url: string } diff --git a/mobile/src/components/markdown-link-handler.ts b/mobile/src/components/markdown/links/markdown-link-handler.ts similarity index 74% rename from mobile/src/components/markdown-link-handler.ts rename to mobile/src/components/markdown/links/markdown-link-handler.ts index 78b99e24bb1..10f90b207ab 100644 --- a/mobile/src/components/markdown-link-handler.ts +++ b/mobile/src/components/markdown/links/markdown-link-handler.ts @@ -1,5 +1,5 @@ import { createContext } from 'react' -import { openExternalLink } from '../platform/external-links/external-link' +import { openExternalLink } from '../../../platform/external-links/external-link' // Screens that offer choices for a tapped web link provide this; elsewhere the system browser opens it. export const MarkdownLinkHandlerContext = createContext<(url: string) => void>(openExternalLink) diff --git a/mobile/src/components/mobile-markdown-links-context.ts b/mobile/src/components/markdown/links/mobile-markdown-links-context.ts similarity index 67% rename from mobile/src/components/mobile-markdown-links-context.ts rename to mobile/src/components/markdown/links/mobile-markdown-links-context.ts index 130fbdabffd..f489cb16976 100644 --- a/mobile/src/components/mobile-markdown-links-context.ts +++ b/mobile/src/components/markdown/links/mobile-markdown-links-context.ts @@ -1,7 +1,7 @@ import { createContext, useEffect, useMemo, useRef, useSyncExternalStore } from 'react' -import { createNativeChatFileLinkHooks } from '../../../src/shared/native-chat/native-chat-file-link-hooks' -import type { NativeChatFileLinkExistence } from '../../../src/shared/native-chat/native-chat-file-link-existence' -import type { GitHubRepoReference } from '../../../src/shared/github/markdown/github-reference-links' +import { createNativeChatFileLinkHooks } from '../../../../../src/shared/native-chat/native-chat-file-link-hooks' +import type { NativeChatFileLinkExistence } from '../../../../../src/shared/native-chat/native-chat-file-link-existence' +import type { GitHubRepoReference } from '../../../../../src/shared/github/markdown/github-reference-links' /** What a chat knows about its workspace for links in its text; absent outside chat. */ export type MobileMarkdownLinks = { diff --git a/mobile/src/components/MobileMarkdownMath.tsx b/mobile/src/components/markdown/math/MobileMarkdownMath.tsx similarity index 100% rename from mobile/src/components/MobileMarkdownMath.tsx rename to mobile/src/components/markdown/math/MobileMarkdownMath.tsx diff --git a/mobile/src/components/mobile-markdown-math-nodes.ts b/mobile/src/components/markdown/math/mobile-markdown-math-nodes.ts similarity index 96% rename from mobile/src/components/mobile-markdown-math-nodes.ts rename to mobile/src/components/markdown/math/mobile-markdown-math-nodes.ts index 2c52dee74d9..3e700ca930e 100644 --- a/mobile/src/components/mobile-markdown-math-nodes.ts +++ b/mobile/src/components/markdown/math/mobile-markdown-math-nodes.ts @@ -1,6 +1,9 @@ import type { Code, Parent, PhrasingContent, RootContent } from 'mdast' -import { isDisplayMathOpener, markdownMathCloser } from '../../../src/shared/markdown-math-rules' -import type { Token } from './mobile-markdown-code-nodes' +import { + isDisplayMathOpener, + markdownMathCloser +} from '../../../../../src/shared/markdown-math-rules' +import type { Token } from '../blocks/mobile-markdown-code-nodes' // The mdast nodes mobile-markdown-math's tokens become, shaped like desktop chat's math nodes. diff --git a/mobile/src/components/mobile-markdown-math-typesetter.ts b/mobile/src/components/markdown/math/mobile-markdown-math-typesetter.ts similarity index 100% rename from mobile/src/components/mobile-markdown-math-typesetter.ts rename to mobile/src/components/markdown/math/mobile-markdown-math-typesetter.ts diff --git a/mobile/src/components/mobile-markdown-math-typesetter.web.ts b/mobile/src/components/markdown/math/mobile-markdown-math-typesetter.web.ts similarity index 88% rename from mobile/src/components/mobile-markdown-math-typesetter.web.ts rename to mobile/src/components/markdown/math/mobile-markdown-math-typesetter.web.ts index c573b9f2f96..ed0acbc065e 100644 --- a/mobile/src/components/mobile-markdown-math-typesetter.web.ts +++ b/mobile/src/components/markdown/math/mobile-markdown-math-typesetter.web.ts @@ -1,6 +1,6 @@ -import { BoundedMap } from '../../../src/shared/bounded-map' -import { MARKDOWN_MATH_KATEX_SETTINGS } from '../../../src/shared/markdown-math-rules' -import { copyMarkdownMathSelection } from '../../../src/shared/markdown-math-selection' +import { BoundedMap } from '../../../../../src/shared/bounded-map' +import { MARKDOWN_MATH_KATEX_SETTINGS } from '../../../../../src/shared/markdown-math-rules' +import { copyMarkdownMathSelection } from '../../../../../src/shared/markdown-math-selection' import { KATEX_PAGE_STYLE } from './katex-page-style.generated' import { GITHUB_MATH_SIZE_MACROS } from './mobile-markdown-math-nodes' import type { MobileMathTypesetter } from './mobile-markdown-math-typesetter' diff --git a/mobile/src/components/mobile-markdown-math.test.ts b/mobile/src/components/markdown/math/mobile-markdown-math.test.ts similarity index 96% rename from mobile/src/components/mobile-markdown-math.test.ts rename to mobile/src/components/markdown/math/mobile-markdown-math.test.ts index f8e4ed37d2d..b7589133c40 100644 --- a/mobile/src/components/mobile-markdown-math.test.ts +++ b/mobile/src/components/markdown/math/mobile-markdown-math.test.ts @@ -1,6 +1,6 @@ import type { Nodes } from 'mdast' import { describe, expect, it } from 'vitest' -import { parseMobileMarkdownTree } from './mobile-markdown-tree' +import { parseMobileMarkdownTree } from '../parsing/mobile-markdown-tree' /** Formulas as `D:` (display) or `I:` (inline) plus the text drawn around them. */ function drawn(source: string, chatMath = true): { math: string[]; text: string } { diff --git a/mobile/src/components/mobile-markdown-math.ts b/mobile/src/components/markdown/math/mobile-markdown-math.ts similarity index 99% rename from mobile/src/components/mobile-markdown-math.ts rename to mobile/src/components/markdown/math/mobile-markdown-math.ts index 8b1520e1b44..952944197b1 100644 --- a/mobile/src/components/mobile-markdown-math.ts +++ b/mobile/src/components/markdown/math/mobile-markdown-math.ts @@ -8,7 +8,7 @@ import { endsDollarAmount, rejectsSingleDollarClose, rejectsSingleDollarOpen -} from '../../../src/shared/markdown-math-rules' +} from '../../../../../src/shared/markdown-math-rules' // Desktop chat's math grammar (src/renderer/src/components/sidebar/chat-markdown-math.ts) for markdown-it. diff --git a/mobile/src/components/mobile-markdown-mdast-nodes.d.ts b/mobile/src/components/markdown/mobile-markdown-mdast-nodes.d.ts similarity index 96% rename from mobile/src/components/mobile-markdown-mdast-nodes.d.ts rename to mobile/src/components/markdown/mobile-markdown-mdast-nodes.d.ts index cbb0c832a13..91856ff29c8 100644 --- a/mobile/src/components/mobile-markdown-mdast-nodes.d.ts +++ b/mobile/src/components/markdown/mobile-markdown-mdast-nodes.d.ts @@ -1,5 +1,5 @@ import type { BlockContent, DefinitionContent, Literal, Parent, PhrasingContent } from 'mdast' -import type { GitHubCalloutKind } from '../../../src/shared/github/markdown/github-callouts' +import type { GitHubCalloutKind } from '../../../../src/shared/github/markdown/github-callouts' // The node types MobileMarkdown adds to mdast, registered by declaration merging. Merging needs // interfaces, which React Doctor's root rules flag; mobile's lint config ignores this file because diff --git a/mobile/src/components/mobile-markdown-styles.ts b/mobile/src/components/markdown/mobile-markdown-styles.ts similarity index 98% rename from mobile/src/components/mobile-markdown-styles.ts rename to mobile/src/components/markdown/mobile-markdown-styles.ts index 3a26e0dd6c8..2eb53b106a4 100644 --- a/mobile/src/components/mobile-markdown-styles.ts +++ b/mobile/src/components/markdown/mobile-markdown-styles.ts @@ -1,6 +1,6 @@ import { StyleSheet, type TextStyle } from 'react-native' -import { colors, radii, spacing, typography } from '../theme/mobile-theme' -import { themedStyleSheet, themedStyles } from '../theme/themed-styles' +import { colors, radii, spacing, typography } from '../../theme/mobile-theme' +import { themedStyleSheet, themedStyles } from '../../theme/themed-styles' /** Lifts the 13pt base to 14pt body text for documents (file preview, PR bodies); headings scale with it. */ export const MOBILE_MARKDOWN_DOCUMENT_SCALE = 14 / 13 diff --git a/mobile/src/components/mobile-markdown-text-setup.ts b/mobile/src/components/markdown/mobile-markdown-text-setup.ts similarity index 100% rename from mobile/src/components/mobile-markdown-text-setup.ts rename to mobile/src/components/markdown/mobile-markdown-text-setup.ts diff --git a/mobile/src/components/mobile-markdown-text.tsx b/mobile/src/components/markdown/mobile-markdown-text.tsx similarity index 100% rename from mobile/src/components/mobile-markdown-text.tsx rename to mobile/src/components/markdown/mobile-markdown-text.tsx diff --git a/mobile/src/components/markdown-linkify-prose-boundary.ts b/mobile/src/components/markdown/parsing/markdown-linkify-prose-boundary.ts similarity index 89% rename from mobile/src/components/markdown-linkify-prose-boundary.ts rename to mobile/src/components/markdown/parsing/markdown-linkify-prose-boundary.ts index e3e10cbb387..ab4df9a38a2 100644 --- a/mobile/src/components/markdown-linkify-prose-boundary.ts +++ b/mobile/src/components/markdown/parsing/markdown-linkify-prose-boundary.ts @@ -1,5 +1,8 @@ import type MarkdownIt from 'markdown-it' -import { findProseUrlEnd, splitAtProseUrlTerminators } from '../../../src/shared/prose-url-boundary' +import { + findProseUrlEnd, + splitAtProseUrlTerminators +} from '../../../../../src/shared/prose-url-boundary' type Linkify = MarkdownIt['linkify'] diff --git a/mobile/src/components/mobile-markdown-growing-tree.test.ts b/mobile/src/components/markdown/parsing/mobile-markdown-growing-tree.test.ts similarity index 100% rename from mobile/src/components/mobile-markdown-growing-tree.test.ts rename to mobile/src/components/markdown/parsing/mobile-markdown-growing-tree.test.ts diff --git a/mobile/src/components/mobile-markdown-growing-tree.ts b/mobile/src/components/markdown/parsing/mobile-markdown-growing-tree.ts similarity index 95% rename from mobile/src/components/mobile-markdown-growing-tree.ts rename to mobile/src/components/markdown/parsing/mobile-markdown-growing-tree.ts index b7c251e5056..fdfb4f8f89e 100644 --- a/mobile/src/components/mobile-markdown-growing-tree.ts +++ b/mobile/src/components/markdown/parsing/mobile-markdown-growing-tree.ts @@ -1,7 +1,7 @@ import type { Root, RootContent } from 'mdast' -import type { Token } from './mobile-markdown-code-nodes' -import { hasDetailsSectionTag } from './mobile-markdown-details' -import type { MathEnv } from './mobile-markdown-math' +import type { Token } from '../blocks/mobile-markdown-code-nodes' +import { hasDetailsSectionTag } from '../blocks/mobile-markdown-details' +import type { MathEnv } from '../math/mobile-markdown-math' import { markdownTokens, parseMobileMarkdownTree } from './mobile-markdown-tree' /** One parse's finished blocks, kept for the next parse of the same text grown longer. */ diff --git a/mobile/src/components/mobile-markdown-html-images.ts b/mobile/src/components/markdown/parsing/mobile-markdown-html-images.ts similarity index 100% rename from mobile/src/components/mobile-markdown-html-images.ts rename to mobile/src/components/markdown/parsing/mobile-markdown-html-images.ts diff --git a/mobile/src/components/mobile-markdown-inline-html.ts b/mobile/src/components/markdown/parsing/mobile-markdown-inline-html.ts similarity index 97% rename from mobile/src/components/mobile-markdown-inline-html.ts rename to mobile/src/components/markdown/parsing/mobile-markdown-inline-html.ts index 053082adce0..6749a8406f0 100644 --- a/mobile/src/components/mobile-markdown-inline-html.ts +++ b/mobile/src/components/markdown/parsing/mobile-markdown-inline-html.ts @@ -1,5 +1,5 @@ import type { PhrasingContent } from 'mdast' -import type { Token } from './mobile-markdown-code-nodes' +import type { Token } from '../blocks/mobile-markdown-code-nodes' import { plainText } from './mobile-markdown-plain-text' import { attrValue } from './mobile-markdown-preview-html' diff --git a/mobile/src/components/mobile-markdown-inline.tsx b/mobile/src/components/markdown/parsing/mobile-markdown-inline.tsx similarity index 94% rename from mobile/src/components/mobile-markdown-inline.tsx rename to mobile/src/components/markdown/parsing/mobile-markdown-inline.tsx index 44d6d073926..bae5b63c417 100644 --- a/mobile/src/components/mobile-markdown-inline.tsx +++ b/mobile/src/components/markdown/parsing/mobile-markdown-inline.tsx @@ -1,17 +1,17 @@ import type { PhrasingContent } from 'mdast' import { Fragment, type ReactNode } from 'react' import type { TextProps } from 'react-native' -import { openExternalLink } from '../platform/external-links/external-link' +import { openExternalLink } from '../../../platform/external-links/external-link' import { detectFilePathSegments, isFilePathCodeSpan, normalizeFilePath -} from './markdown-file-path-detection' -import { routeMarkdownHref } from './markdown-href-routing' -import { styles, type MarkdownTone } from './mobile-markdown-styles' -import { MarkdownText } from './mobile-markdown-text' +} from '../links/markdown-file-path-detection' +import { routeMarkdownHref } from '../links/markdown-href-routing' +import { styles, type MarkdownTone } from '../mobile-markdown-styles' +import { MarkdownText } from '../mobile-markdown-text' import { plainText } from './mobile-markdown-plain-text' -import { MobileMarkdownMath } from './MobileMarkdownMath' +import { MobileMarkdownMath } from '../math/MobileMarkdownMath' export type MarkdownInlineContext = { // File targets call this when viewer exists; omit to keep paths plain. diff --git a/mobile/src/components/mobile-markdown-plain-text.ts b/mobile/src/components/markdown/parsing/mobile-markdown-plain-text.ts similarity index 100% rename from mobile/src/components/mobile-markdown-plain-text.ts rename to mobile/src/components/markdown/parsing/mobile-markdown-plain-text.ts diff --git a/mobile/src/components/mobile-markdown-preview-html.ts b/mobile/src/components/markdown/parsing/mobile-markdown-preview-html.ts similarity index 100% rename from mobile/src/components/mobile-markdown-preview-html.ts rename to mobile/src/components/markdown/parsing/mobile-markdown-preview-html.ts diff --git a/mobile/src/components/mobile-markdown-preview-tag-stripper.ts b/mobile/src/components/markdown/parsing/mobile-markdown-preview-tag-stripper.ts similarity index 100% rename from mobile/src/components/mobile-markdown-preview-tag-stripper.ts rename to mobile/src/components/markdown/parsing/mobile-markdown-preview-tag-stripper.ts diff --git a/mobile/src/components/mobile-markdown-task-items.ts b/mobile/src/components/markdown/parsing/mobile-markdown-task-items.ts similarity index 100% rename from mobile/src/components/mobile-markdown-task-items.ts rename to mobile/src/components/markdown/parsing/mobile-markdown-task-items.ts diff --git a/mobile/src/components/mobile-markdown-tree.test.ts b/mobile/src/components/markdown/parsing/mobile-markdown-tree.test.ts similarity index 100% rename from mobile/src/components/mobile-markdown-tree.test.ts rename to mobile/src/components/markdown/parsing/mobile-markdown-tree.test.ts diff --git a/mobile/src/components/mobile-markdown-tree.ts b/mobile/src/components/markdown/parsing/mobile-markdown-tree.ts similarity index 97% rename from mobile/src/components/mobile-markdown-tree.ts rename to mobile/src/components/markdown/parsing/mobile-markdown-tree.ts index b511ad24a87..9b0c7a69945 100644 --- a/mobile/src/components/mobile-markdown-tree.ts +++ b/mobile/src/components/markdown/parsing/mobile-markdown-tree.ts @@ -3,13 +3,13 @@ import cjkFriendly from 'markdown-it-cjk-friendly' import footnote from 'markdown-it-footnote' import type { AlignType, Parent, PhrasingContent, Root, RootContent, Table } from 'mdast' import remend from 'remend' -import { forEachGitHubCallout } from '../../../src/shared/github/markdown/github-callouts' -import { codeNode, type Token } from './mobile-markdown-code-nodes' +import { forEachGitHubCallout } from '../../../../../src/shared/github/markdown/github-callouts' +import { codeNode, type Token } from '../blocks/mobile-markdown-code-nodes' import { inlineTagNode, inlineTagPairs } from './mobile-markdown-inline-html' import { readTaskMarker } from './mobile-markdown-task-items' import { plainText } from './mobile-markdown-plain-text' -import { appendHtmlBlock, noteInlineHtml, popContainer } from './mobile-markdown-details' -import { mobileMarkdownMath, type MathEnv } from './mobile-markdown-math' +import { appendHtmlBlock, noteInlineHtml, popContainer } from '../blocks/mobile-markdown-details' +import { mobileMarkdownMath, type MathEnv } from '../math/mobile-markdown-math' import { displayFormulas, inlineMathNode, @@ -17,7 +17,7 @@ import { mathClosed, mathFenceNode, withoutMath -} from './mobile-markdown-math-nodes' +} from '../math/mobile-markdown-math-nodes' import { withImagesSplitOut } from './mobile-markdown-html-images' import { normalizeMobileMarkdownPreviewHtml } from './mobile-markdown-preview-html' import { endLinkifyUrlsAtProsePunctuation } from './markdown-linkify-prose-boundary' diff --git a/mobile/src/components/use-mobile-markdown-tree.ts b/mobile/src/components/markdown/parsing/use-mobile-markdown-tree.ts similarity index 85% rename from mobile/src/components/use-mobile-markdown-tree.ts rename to mobile/src/components/markdown/parsing/use-mobile-markdown-tree.ts index 9d93e59bb78..a1222544620 100644 --- a/mobile/src/components/use-mobile-markdown-tree.ts +++ b/mobile/src/components/markdown/parsing/use-mobile-markdown-tree.ts @@ -3,17 +3,17 @@ import { useMemo, useRef } from 'react' import { linkGitHubReferences, type GitHubRepoReference -} from '../../../src/shared/github/markdown/github-reference-links' +} from '../../../../../src/shared/github/markdown/github-reference-links' import { linkNativeChatFilePaths, type FileLinkExists -} from '../../../src/shared/native-chat/native-chat-file-link-tree' -import type { NativeChatVisualDirective } from '../../../src/shared/native-chat/native-chat-visual-directive' +} from '../../../../../src/shared/native-chat/native-chat-file-link-tree' +import type { NativeChatVisualDirective } from '../../../../../src/shared/native-chat/native-chat-visual-directive' import { parseGrowingMobileMarkdownTree, type MobileMarkdownTreeCache } from './mobile-markdown-growing-tree' -import { protectMobileMarkdownVisualLines } from './mobile-markdown-visual-lines' +import { protectMobileMarkdownVisualLines } from '../blocks/mobile-markdown-visual-lines' const NO_DIRECTIVES: NativeChatVisualDirective[] = [] diff --git a/mobile/src/components/MobileRichMarkdownEditor.tsx b/mobile/src/components/rich-markdown/MobileRichMarkdownEditor.tsx similarity index 94% rename from mobile/src/components/MobileRichMarkdownEditor.tsx rename to mobile/src/components/rich-markdown/MobileRichMarkdownEditor.tsx index 92faf56543e..9e8683859a8 100644 --- a/mobile/src/components/MobileRichMarkdownEditor.tsx +++ b/mobile/src/components/rich-markdown/MobileRichMarkdownEditor.tsx @@ -11,24 +11,24 @@ import { type ForwardedRef } from 'react' import { View, type HostInstance } from 'react-native' -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { colors } from '../theme/mobile-theme' -import { themedStyleSheet } from '../theme/themed-styles' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { openExternalLink } from '../platform/external-links/external-link' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' +import { colors } from '../../theme/mobile-theme' +import { themedStyleSheet } from '../../theme/themed-styles' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import { openExternalLink } from '../../platform/external-links/external-link' import { MobileRichMarkdownToolbar } from './MobileRichMarkdownToolbar' -import { TextInputModal } from './TextInputModal' +import { TextInputModal } from '../TextInputModal' import { ensureDocumentStyle, mountRichMarkdownWebDocument, type RichMarkdownWebDocument -} from './rich-markdown/rich-markdown-web-document-mount' +} from './document/rich-markdown-web-document-mount' import { useMobileRichMarkdownEditorController } from './use-mobile-rich-markdown-editor-controller' import { noWorkspaceImage, type RichMarkdownEditorApi, type RichMarkdownUrlPromptKind -} from './rich-markdown/document-host-seams' +} from './document/document-host-seams' import type { MobileRichMarkdownEditorMessage, MobileRichMarkdownEditorProps diff --git a/mobile/src/components/MobileRichMarkdownToolbar.tsx b/mobile/src/components/rich-markdown/MobileRichMarkdownToolbar.tsx similarity index 93% rename from mobile/src/components/MobileRichMarkdownToolbar.tsx rename to mobile/src/components/rich-markdown/MobileRichMarkdownToolbar.tsx index a91396dc682..73857917ffa 100644 --- a/mobile/src/components/MobileRichMarkdownToolbar.tsx +++ b/mobile/src/components/rich-markdown/MobileRichMarkdownToolbar.tsx @@ -17,11 +17,11 @@ import { Quote, Strikethrough } from 'lucide-react-native' -import { colors, radii, spacing } from '../theme/mobile-theme' -import type { Translate } from '../i18n/mobile-i18n' -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { themedStyleSheet } from '../theme/themed-styles' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import { colors, radii, spacing } from '../../theme/mobile-theme' +import type { Translate } from '../../i18n/mobile-i18n' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' +import { themedStyleSheet } from '../../theme/themed-styles' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import type { MobileRichMarkdownCommand } from './mobile-rich-markdown-editor-contract' type ToolbarItem = { diff --git a/mobile/src/components/rich-markdown/create-rich-markdown-editor-document.ts b/mobile/src/components/rich-markdown/create-rich-markdown-editor-document.ts index e812bfe1479..b3c8440c90b 100644 --- a/mobile/src/components/rich-markdown/create-rich-markdown-editor-document.ts +++ b/mobile/src/components/rich-markdown/create-rich-markdown-editor-document.ts @@ -1,13 +1,21 @@ -import { createRichMarkdownEditorScope } from './document-scope' -import { currentMarkdown, setEditable, setMarkdown, stopEditorContent } from './editor-content' -import { startEditorListeners, stopEditorListeners } from './editor-listeners' -import { startEditorSurface } from './editor-surface' -import { startHostBridge } from './host-bridge' -import { dismissKeyboard } from './keyboard-dismiss' -import { runCommand } from './editor-commands' -import { startKeyboardInset, stopKeyboardInset } from './keyboard-inset' -import type { RichMarkdownEditorScope } from './document-scope' -import type { RichMarkdownEditorDocument, RichMarkdownEditorHost } from './document-host-seams' +import { createRichMarkdownEditorScope } from './document/document-scope' +import { + currentMarkdown, + setEditable, + setMarkdown, + stopEditorContent +} from './editing/editor-content' +import { startEditorListeners, stopEditorListeners } from './editing/editor-listeners' +import { startEditorSurface } from './editing/editor-surface' +import { startHostBridge } from './document/host-bridge' +import { dismissKeyboard } from './editing/keyboard-dismiss' +import { runCommand } from './editing/editor-commands' +import { startKeyboardInset, stopKeyboardInset } from './editing/keyboard-inset' +import type { RichMarkdownEditorScope } from './document/document-scope' +import type { + RichMarkdownEditorDocument, + RichMarkdownEditorHost +} from './document/document-host-seams' // Each mount owns local editor scope so overlapping editors/listeners never read another mount. // Start in order: read surface, install listeners, start keyboard measurement, then announce ready. diff --git a/mobile/src/components/rich-markdown/document-host-root.test.ts b/mobile/src/components/rich-markdown/document/document-host-root.test.ts similarity index 97% rename from mobile/src/components/rich-markdown/document-host-root.test.ts rename to mobile/src/components/rich-markdown/document/document-host-root.test.ts index a944590ba4d..a73ee0f4b6b 100644 --- a/mobile/src/components/rich-markdown/document-host-root.test.ts +++ b/mobile/src/components/rich-markdown/document/document-host-root.test.ts @@ -1,7 +1,7 @@ // @vitest-environment happy-dom import { afterEach, describe, expect, it, vi } from 'vitest' import { quietRichMarkdownEditorHost } from './rich-markdown-editor-host.test-support' -import { createRichMarkdownEditorDocument } from './create-rich-markdown-editor-document' +import { createRichMarkdownEditorDocument } from '../create-rich-markdown-editor-document' import { RICH_MARKDOWN_EDITOR_MARKUP } from './document-markup' import type { MobileRichMarkdownEditorMessage } from '../mobile-rich-markdown-editor-contract' import type { RichMarkdownEditorDocument } from './document-host-seams' diff --git a/mobile/src/components/rich-markdown/document-host-seams.ts b/mobile/src/components/rich-markdown/document/document-host-seams.ts similarity index 100% rename from mobile/src/components/rich-markdown/document-host-seams.ts rename to mobile/src/components/rich-markdown/document/document-host-seams.ts diff --git a/mobile/src/components/rich-markdown/document-lifecycle.test.ts b/mobile/src/components/rich-markdown/document/document-lifecycle.test.ts similarity index 93% rename from mobile/src/components/rich-markdown/document-lifecycle.test.ts rename to mobile/src/components/rich-markdown/document/document-lifecycle.test.ts index b7f7fe728d6..49519648d43 100644 --- a/mobile/src/components/rich-markdown/document-lifecycle.test.ts +++ b/mobile/src/components/rich-markdown/document/document-lifecycle.test.ts @@ -1,7 +1,7 @@ // @vitest-environment happy-dom import { afterEach, describe, expect, it } from 'vitest' import { quietRichMarkdownEditorHost } from './rich-markdown-editor-host.test-support' -import { createRichMarkdownEditorDocument } from './create-rich-markdown-editor-document' +import { createRichMarkdownEditorDocument } from '../create-rich-markdown-editor-document' import { RICH_MARKDOWN_EDITOR_MARKUP } from './document-markup' import type { MobileRichMarkdownEditorMessage } from '../mobile-rich-markdown-editor-contract' import type { RichMarkdownEditorDocument } from './document-host-seams' @@ -64,15 +64,6 @@ describe('an editor document that is stopped', () => { expect(posted).toEqual([]) }) - it('stops observing the viewport, so no inset of its own reaches the next mount', () => { - const posted: MobileRichMarkdownEditorMessage[] = [] - plantMarkup() - const mounted = mount(posted) - expect(mounted.keyboard.observers).toHaveLength(1) - mounted.stop() - expect(mounted.keyboard.observers).toHaveLength(0) - }) - it('leaves a second mount a document of its own, not the first one continued', () => { const first: MobileRichMarkdownEditorMessage[] = [] plantMarkup() diff --git a/mobile/src/components/rich-markdown/document-markup.ts b/mobile/src/components/rich-markdown/document/document-markup.ts similarity index 100% rename from mobile/src/components/rich-markdown/document-markup.ts rename to mobile/src/components/rich-markdown/document/document-markup.ts diff --git a/mobile/src/components/rich-markdown/document-scope.ts b/mobile/src/components/rich-markdown/document/document-scope.ts similarity index 95% rename from mobile/src/components/rich-markdown/document-scope.ts rename to mobile/src/components/rich-markdown/document/document-scope.ts index 3d47a77d8ba..1c7c8c432c7 100644 --- a/mobile/src/components/rich-markdown/document-scope.ts +++ b/mobile/src/components/rich-markdown/document/document-scope.ts @@ -6,12 +6,6 @@ import { type RichMarkdownEditorHost, type RichMarkdownEditorHostSeams } from './document-host-seams' -export type { - RichMarkdownEditorApi, - RichMarkdownEditorDocument, - RichMarkdownEditorHost, - RichMarkdownEditorHostSeams -} from './document-host-seams' // Own all mutable editor state per factory call; module let would leak generation, caret and inset between overlapping mounts (ruling 21). export type RichMarkdownEditorState = { diff --git a/mobile/src/components/rich-markdown/document-style.ts b/mobile/src/components/rich-markdown/document/document-style.ts similarity index 97% rename from mobile/src/components/rich-markdown/document-style.ts rename to mobile/src/components/rich-markdown/document/document-style.ts index a8763895e7c..5028ca01146 100644 --- a/mobile/src/components/rich-markdown/document-style.ts +++ b/mobile/src/components/rich-markdown/document/document-style.ts @@ -1,5 +1,5 @@ -import { colors, getMobileColorScheme } from '../../theme/mobile-theme' -import { TEXT_INPUT_FONT_SIZE } from '../../platform/text-input/text-input-font-size' +import { colors, getMobileColorScheme } from '../../../theme/mobile-theme' +import { TEXT_INPUT_FONT_SIZE } from '../../../platform/text-input/text-input-font-size' // Read theme at build time; native embeds sheet, page scopes it to editor host. // #editor scrolls itself: iOS WebKit stops drawing selection ~2 screens into an editable scrolled by a separate box (#34472). diff --git a/mobile/src/components/rich-markdown/host-bridge.ts b/mobile/src/components/rich-markdown/document/host-bridge.ts similarity index 100% rename from mobile/src/components/rich-markdown/host-bridge.ts rename to mobile/src/components/rich-markdown/document/host-bridge.ts diff --git a/mobile/src/components/rich-markdown/page-stylesheet.test.ts b/mobile/src/components/rich-markdown/document/page-stylesheet.test.ts similarity index 92% rename from mobile/src/components/rich-markdown/page-stylesheet.test.ts rename to mobile/src/components/rich-markdown/document/page-stylesheet.test.ts index fbf587dd23f..fabc159b2e4 100644 --- a/mobile/src/components/rich-markdown/page-stylesheet.test.ts +++ b/mobile/src/components/rich-markdown/document/page-stylesheet.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { scopeDocumentStyleToHost } from '../../style-scoping/document-style-scoping' +import { scopeDocumentStyleToHost } from '../../../style-scoping/document-style-scoping' const PREFIX = '.orca-rich-markdown-document-host' diff --git a/mobile/src/components/rich-markdown/rich-markdown-editor-host.test-support.ts b/mobile/src/components/rich-markdown/document/rich-markdown-editor-host.test-support.ts similarity index 100% rename from mobile/src/components/rich-markdown/rich-markdown-editor-host.test-support.ts rename to mobile/src/components/rich-markdown/document/rich-markdown-editor-host.test-support.ts diff --git a/mobile/src/components/rich-markdown/rich-markdown-web-document-mount.test.ts b/mobile/src/components/rich-markdown/document/rich-markdown-web-document-mount.test.ts similarity index 100% rename from mobile/src/components/rich-markdown/rich-markdown-web-document-mount.test.ts rename to mobile/src/components/rich-markdown/document/rich-markdown-web-document-mount.test.ts diff --git a/mobile/src/components/rich-markdown/rich-markdown-web-document-mount.ts b/mobile/src/components/rich-markdown/document/rich-markdown-web-document-mount.ts similarity index 93% rename from mobile/src/components/rich-markdown/rich-markdown-web-document-mount.ts rename to mobile/src/components/rich-markdown/document/rich-markdown-web-document-mount.ts index fe42a915d6c..d4f6c0b09fa 100644 --- a/mobile/src/components/rich-markdown/rich-markdown-web-document-mount.ts +++ b/mobile/src/components/rich-markdown/document/rich-markdown-web-document-mount.ts @@ -1,8 +1,8 @@ -import { createRichMarkdownEditorDocument } from './create-rich-markdown-editor-document' +import { createRichMarkdownEditorDocument } from '../create-rich-markdown-editor-document' import { RICH_MARKDOWN_EDITOR_MARKUP } from './document-markup' import { richMarkdownEditorStyle } from './document-style' -import { getMobileColorScheme } from '../../theme/mobile-theme' -import { scopeDocumentStyleToHost } from '../../style-scoping/document-style-scoping' +import { getMobileColorScheme } from '../../../theme/mobile-theme' +import { scopeDocumentStyleToHost } from '../../../style-scoping/document-style-scoping' import type { RichMarkdownEditorApi, RichMarkdownUrlPromptKind } from './document-host-seams' import type { MobileRichMarkdownEditorMessage } from '../mobile-rich-markdown-editor-contract' diff --git a/mobile/src/components/rich-markdown/editor-commands.ts b/mobile/src/components/rich-markdown/editing/editor-commands.ts similarity index 96% rename from mobile/src/components/rich-markdown/editor-commands.ts rename to mobile/src/components/rich-markdown/editing/editor-commands.ts index e8249e98bac..5ff29db99ff 100644 --- a/mobile/src/components/rich-markdown/editor-commands.ts +++ b/mobile/src/components/rich-markdown/editing/editor-commands.ts @@ -6,10 +6,10 @@ import { wrapSelection } from './editor-selection' import { editorElement } from './editor-surface' -import { isSafeUrl } from './markdown-escaping' +import { isSafeUrl } from '../serialization/markdown-escaping' import type { MobileRichMarkdownCommand } from '../mobile-rich-markdown-editor-contract' -import type { RichMarkdownEditorScope } from './document-scope' -import type { RichMarkdownUrlPromptKind } from './document-host-seams' +import type { RichMarkdownEditorScope } from '../document/document-scope' +import type { RichMarkdownUrlPromptKind } from '../document/document-host-seams' const TASK_LIST_HTML = '