From 5bf36a80c5e2daaca88b8a06d225459de0648ed2 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 14 Nov 2026 08:20:20 -0800 Subject: [PATCH] refactor(mobile): organize markdown editor and preview owners (#42428) --- ...obile-localization-coverage-allowlist.json | 4 +- ...obile-web-app-html-preview-render.test.mjs | 15 +- ...obile-web-app-preview-artifact-fixture.mjs | 2 +- ...bile-web-app-rich-markdown-render.test.mjs | 2 +- .../mobile-markdown-placeholder-benchmark.mjs | 26 ++- mobile/.gitignore | 2 +- mobile/.oxlintrc.json | 4 +- mobile/scripts/build-katex-page-style.mjs | 9 +- .../MobileSourceCopyButton.tsx | 12 +- .../MobileSyntaxSegments.tsx | 8 +- .../{ => markdown}/MobileMarkdown.test.ts | 2 +- .../{ => markdown}/MobileMarkdown.tsx | 16 +- .../blocks}/MobileMarkdownCallout.tsx | 10 +- .../blocks}/MobileMarkdownCodeBlock.tsx | 18 +- .../blocks}/MobileMarkdownDetails.tsx | 8 +- .../blocks}/MobileMarkdownRemoteImage.tsx | 8 +- .../blocks}/mobile-markdown-blocks.tsx | 20 +-- .../blocks}/mobile-markdown-code-nodes.ts | 0 .../blocks}/mobile-markdown-details.ts | 0 .../blocks}/mobile-markdown-drawn-blocks.ts | 4 +- .../mobile-markdown-image-paragraph.tsx | 6 +- .../mobile-markdown-image-placement.ts | 0 .../blocks}/mobile-markdown-table-columns.ts | 2 +- .../blocks}/mobile-markdown-visual-lines.ts | 4 +- .../blocks}/mobile-mermaid-language.ts | 0 .../markdown-file-path-detection.test.ts | 0 .../links}/markdown-file-path-detection.ts | 2 +- .../links}/markdown-href-routing.ts | 2 +- .../links}/markdown-link-handler.ts | 2 +- .../links}/mobile-markdown-links-context.ts | 6 +- .../math}/MobileMarkdownMath.tsx | 0 .../math}/mobile-markdown-math-nodes.ts | 7 +- .../math}/mobile-markdown-math-typesetter.ts | 0 .../mobile-markdown-math-typesetter.web.ts | 6 +- .../math}/mobile-markdown-math.test.ts | 2 +- .../math}/mobile-markdown-math.ts | 2 +- .../mobile-markdown-mdast-nodes.d.ts | 2 +- .../{ => markdown}/mobile-markdown-styles.ts | 4 +- .../mobile-markdown-text-setup.ts | 0 .../{ => markdown}/mobile-markdown-text.tsx | 0 .../markdown-linkify-prose-boundary.ts | 5 +- .../mobile-markdown-growing-tree.test.ts | 0 .../parsing}/mobile-markdown-growing-tree.ts | 6 +- .../parsing}/mobile-markdown-html-images.ts | 0 .../parsing}/mobile-markdown-inline-html.ts | 2 +- .../parsing}/mobile-markdown-inline.tsx | 12 +- .../parsing}/mobile-markdown-plain-text.ts | 0 .../parsing}/mobile-markdown-preview-html.ts | 0 .../mobile-markdown-preview-tag-stripper.ts | 0 .../parsing}/mobile-markdown-task-items.ts | 0 .../parsing}/mobile-markdown-tree.test.ts | 0 .../parsing}/mobile-markdown-tree.ts | 10 +- .../parsing}/use-mobile-markdown-tree.ts | 8 +- .../MobileRichMarkdownEditor.tsx | 16 +- .../MobileRichMarkdownToolbar.tsx | 10 +- .../create-rich-markdown-editor-document.ts | 28 +-- .../{ => document}/document-host-root.test.ts | 2 +- .../{ => document}/document-host-seams.ts | 0 .../{ => document}/document-lifecycle.test.ts | 11 +- .../{ => document}/document-markup.ts | 0 .../{ => document}/document-scope.ts | 6 - .../{ => document}/document-style.ts | 4 +- .../{ => document}/host-bridge.ts | 0 .../{ => document}/page-stylesheet.test.ts | 2 +- .../rich-markdown-editor-host.test-support.ts | 0 .../rich-markdown-web-document-mount.test.ts | 0 .../rich-markdown-web-document-mount.ts | 6 +- .../{ => editing}/editor-commands.ts | 6 +- .../{ => editing}/editor-content.test.ts | 14 +- .../{ => editing}/editor-content.ts | 10 +- .../editor-inline-code-blocks.test.ts | 8 +- .../{ => editing}/editor-listeners.ts | 4 +- .../{ => editing}/editor-selection.test.ts | 8 +- .../{ => editing}/editor-selection.ts | 2 +- .../{ => editing}/editor-surface.ts | 4 +- .../{ => editing}/keyboard-dismiss.ts | 2 +- .../{ => editing}/keyboard-inset.ts | 6 +- .../mobile-rich-markdown-editor-contract.ts | 0 ...ile-rich-markdown-editor-keyboard-inset.ts | 0 ...bile-webview-editor-web-fallbacks.test.tsx | 2 +- .../html-block-markdown.ts | 0 .../html-code-block-markdown.ts | 0 .../html-inline-markdown.ts | 0 .../{ => serialization}/html-list-markdown.ts | 0 .../markdown-code-block-dom.test.ts | 6 +- .../markdown-code-delimiters.test.ts | 6 +- .../markdown-code-fence.ts | 0 .../markdown-code-literals.test.ts | 6 +- .../markdown-empty-tasks.test.ts | 6 +- .../{ => serialization}/markdown-escaping.ts | 0 .../markdown-heading-code-blocks.test.ts | 6 +- .../markdown-inline-render.ts | 4 +- .../markdown-list-block-boundaries.test.ts | 22 +-- .../markdown-list-code-blocks.test.ts | 17 +- .../markdown-list-linebreaks.test.ts | 20 +-- .../markdown-list-parse.ts | 0 .../markdown-list-render.ts | 2 +- .../markdown-paragraph-code-blocks.test.ts | 6 +- .../markdown-quote-blocks.test.ts | 6 +- .../markdown-round-trip.test.ts | 10 +- .../markdown-table-linebreaks.test.ts | 9 +- .../markdown-table-rows.ts | 0 .../{ => serialization}/markdown-to-html.ts | 2 +- .../task-checkbox-accessible-names.test.ts | 6 +- ...-mobile-rich-markdown-editor-controller.ts | 0 .../markdown/MobileFileMarkdownPreview.tsx | 4 +- .../markdown/MobileMarkdownPreviewModeBar.tsx | 2 +- .../mobile-markdown-file-link.test.ts | 2 +- .../markdown/mobile-markdown-file-link.ts | 2 +- .../markdown/mobile-markdown-image-source.ts | 2 +- .../presentation}/MobilePanZoomImage.tsx | 2 +- .../presentation}/use-mobile-pan-zoom.ts | 4 +- .../files/preview/MobileFilePreviewBody.tsx | 6 +- .../preview/MobileFilePreviewSourceText.tsx | 2 +- .../preview/documents}/MobileHtmlPreview.tsx | 6 +- .../documents}/MobilePreviewSourceTabs.tsx | 8 +- .../preview/documents}/MobileSvgPreview.tsx | 8 +- .../html-preview-inert-links.test.ts | 0 .../documents}/html-preview-inert-links.ts | 0 .../html-preview-link-affordance.test.tsx | 0 .../documents}/use-html-preview-link-grant.ts | 0 .../use-html-preview-link-grant.web.ts | 4 +- .../mobile-file-preview-retry.test.tsx | 161 ------------------ .../MobileSessionFileReader.tsx | 8 +- .../MobileSessionMarkdownReader.tsx | 2 +- .../text-selection}/inline-text-selection.ts | 0 .../web-document}/inline-script-json.ts | 0 .../__mocks__/mobile-prompt-render-mocks.ts | 2 +- .../native-chat/MobileNativeChatOverlay.tsx | 2 +- .../use-mobile-native-chat-markdown-links.ts | 4 +- .../MobileNativeChatAgentMessageCard.tsx | 2 +- ...ile-agent-message-renderer.test-fixture.ts | 4 +- .../MobileNativeChatResponseActions.tsx | 2 +- .../MobileNativeChatRowOverlaysHost.tsx | 2 +- .../MobileNativeChatFullContent.tsx | 2 +- .../MobileNativeChatMarkdown.tsx | 2 +- .../message-content/MobileNativeChatProse.tsx | 2 +- .../MobileNativeChatQuestionText.tsx | 2 +- .../MobileNativeChatUserMessageFold.tsx | 2 +- ...bile-native-chat-markdown-image-sources.ts | 6 +- .../MobileNativeChatAwaitingInputRow.tsx | 2 +- .../MobileNativeChatBackgroundTaskRun.tsx | 2 +- .../messages/MobileNativeChatLiveLine.tsx | 2 +- ...tMessage.tool-result-availability.test.tsx | 6 +- .../messages/MobileNativeChatMessage.tsx | 2 +- .../MobileNativeChatPullRequestEventLine.tsx | 4 +- .../MobileNativeChatResolutionReceipt.tsx | 2 +- .../MobileNativeChatNoticeDisclosure.tsx | 2 +- .../notices/MobileNativeChatNoticeRow.tsx | 2 +- .../MobileNativeChatUsageLimitStatus.tsx | 2 +- .../tool-results/MobileNativeChatDiffCard.tsx | 2 +- .../tool-results/MobileNativeChatToolLine.tsx | 8 +- .../visual/MobileNativeChatImageViewer.tsx | 2 +- .../visual/mobile-native-chat-open-file.ts | 2 +- ...mobile-native-chat-visual-host-document.ts | 2 +- .../diff-screen/MobileDiffReviewLine.tsx | 2 +- .../review/pull-request/PrMarkdown.tsx | 6 +- .../comments/PRCommentsSection.tsx | 4 +- .../review-notes/MobileSessionDiffLineRow.tsx | 2 +- .../hosted-review/MobileTasksPrFileDiff.tsx | 2 +- .../mobile-tasks-item-review-sections.tsx | 2 +- ...-mobile-tasks-detail-comment-renderers.tsx | 2 +- .../mobile-tasks-project-comments.tsx | 2 +- .../mobile-tasks-project-metadata-editors.tsx | 2 +- mobile/web-entry/web-overrides.json | 4 +- 165 files changed, 341 insertions(+), 531 deletions(-) rename mobile/src/components/{ => code-presentation}/MobileSourceCopyButton.tsx (91%) rename mobile/src/components/{ => code-presentation}/MobileSyntaxSegments.tsx (80%) rename mobile/src/components/{ => markdown}/MobileMarkdown.test.ts (99%) rename mobile/src/components/{ => markdown}/MobileMarkdown.tsx (88%) rename mobile/src/components/{ => markdown/blocks}/MobileMarkdownCallout.tsx (87%) rename mobile/src/components/{ => markdown/blocks}/MobileMarkdownCodeBlock.tsx (83%) rename mobile/src/components/{ => markdown/blocks}/MobileMarkdownDetails.tsx (82%) rename mobile/src/components/{ => markdown/blocks}/MobileMarkdownRemoteImage.tsx (85%) rename mobile/src/components/{ => markdown/blocks}/mobile-markdown-blocks.tsx (94%) rename mobile/src/components/{ => markdown/blocks}/mobile-markdown-code-nodes.ts (100%) rename mobile/src/components/{ => markdown/blocks}/mobile-markdown-details.ts (100%) rename mobile/src/components/{ => markdown/blocks}/mobile-markdown-drawn-blocks.ts (95%) rename mobile/src/components/{ => markdown/blocks}/mobile-markdown-image-paragraph.tsx (93%) rename mobile/src/components/{ => markdown/blocks}/mobile-markdown-image-placement.ts (100%) rename mobile/src/components/{ => markdown/blocks}/mobile-markdown-table-columns.ts (96%) rename mobile/src/components/{ => markdown/blocks}/mobile-markdown-visual-lines.ts (93%) rename mobile/src/components/{ => markdown/blocks}/mobile-mermaid-language.ts (100%) rename mobile/src/components/{ => markdown/links}/markdown-file-path-detection.test.ts (100%) rename mobile/src/components/{ => markdown/links}/markdown-file-path-detection.ts (98%) rename mobile/src/components/{ => markdown/links}/markdown-href-routing.ts (83%) rename mobile/src/components/{ => markdown/links}/markdown-link-handler.ts (74%) rename mobile/src/components/{ => markdown/links}/mobile-markdown-links-context.ts (67%) rename mobile/src/components/{ => markdown/math}/MobileMarkdownMath.tsx (100%) rename mobile/src/components/{ => markdown/math}/mobile-markdown-math-nodes.ts (96%) rename mobile/src/components/{ => markdown/math}/mobile-markdown-math-typesetter.ts (100%) rename mobile/src/components/{ => markdown/math}/mobile-markdown-math-typesetter.web.ts (88%) rename mobile/src/components/{ => markdown/math}/mobile-markdown-math.test.ts (96%) rename mobile/src/components/{ => markdown/math}/mobile-markdown-math.ts (99%) rename mobile/src/components/{ => markdown}/mobile-markdown-mdast-nodes.d.ts (96%) rename mobile/src/components/{ => markdown}/mobile-markdown-styles.ts (98%) rename mobile/src/components/{ => markdown}/mobile-markdown-text-setup.ts (100%) rename mobile/src/components/{ => markdown}/mobile-markdown-text.tsx (100%) rename mobile/src/components/{ => markdown/parsing}/markdown-linkify-prose-boundary.ts (89%) rename mobile/src/components/{ => markdown/parsing}/mobile-markdown-growing-tree.test.ts (100%) rename mobile/src/components/{ => markdown/parsing}/mobile-markdown-growing-tree.ts (95%) rename mobile/src/components/{ => markdown/parsing}/mobile-markdown-html-images.ts (100%) rename mobile/src/components/{ => markdown/parsing}/mobile-markdown-inline-html.ts (97%) rename mobile/src/components/{ => markdown/parsing}/mobile-markdown-inline.tsx (94%) rename mobile/src/components/{ => markdown/parsing}/mobile-markdown-plain-text.ts (100%) rename mobile/src/components/{ => markdown/parsing}/mobile-markdown-preview-html.ts (100%) rename mobile/src/components/{ => markdown/parsing}/mobile-markdown-preview-tag-stripper.ts (100%) rename mobile/src/components/{ => markdown/parsing}/mobile-markdown-task-items.ts (100%) rename mobile/src/components/{ => markdown/parsing}/mobile-markdown-tree.test.ts (100%) rename mobile/src/components/{ => markdown/parsing}/mobile-markdown-tree.ts (97%) rename mobile/src/components/{ => markdown/parsing}/use-mobile-markdown-tree.ts (85%) rename mobile/src/components/{ => rich-markdown}/MobileRichMarkdownEditor.tsx (94%) rename mobile/src/components/{ => rich-markdown}/MobileRichMarkdownToolbar.tsx (93%) rename mobile/src/components/rich-markdown/{ => document}/document-host-root.test.ts (97%) rename mobile/src/components/rich-markdown/{ => document}/document-host-seams.ts (100%) rename mobile/src/components/rich-markdown/{ => document}/document-lifecycle.test.ts (93%) rename mobile/src/components/rich-markdown/{ => document}/document-markup.ts (100%) rename mobile/src/components/rich-markdown/{ => document}/document-scope.ts (95%) rename mobile/src/components/rich-markdown/{ => document}/document-style.ts (97%) rename mobile/src/components/rich-markdown/{ => document}/host-bridge.ts (100%) rename mobile/src/components/rich-markdown/{ => document}/page-stylesheet.test.ts (92%) rename mobile/src/components/rich-markdown/{ => document}/rich-markdown-editor-host.test-support.ts (100%) rename mobile/src/components/rich-markdown/{ => document}/rich-markdown-web-document-mount.test.ts (100%) rename mobile/src/components/rich-markdown/{ => document}/rich-markdown-web-document-mount.ts (93%) rename mobile/src/components/rich-markdown/{ => editing}/editor-commands.ts (96%) rename mobile/src/components/rich-markdown/{ => editing}/editor-content.test.ts (82%) rename mobile/src/components/rich-markdown/{ => editing}/editor-content.ts (91%) rename mobile/src/components/rich-markdown/{ => editing}/editor-inline-code-blocks.test.ts (91%) rename mobile/src/components/rich-markdown/{ => editing}/editor-listeners.ts (97%) rename mobile/src/components/rich-markdown/{ => editing}/editor-selection.test.ts (94%) rename mobile/src/components/rich-markdown/{ => editing}/editor-selection.ts (98%) rename mobile/src/components/rich-markdown/{ => editing}/editor-surface.ts (80%) rename mobile/src/components/rich-markdown/{ => editing}/keyboard-dismiss.ts (87%) rename mobile/src/components/rich-markdown/{ => editing}/keyboard-inset.ts (81%) rename mobile/src/components/{ => rich-markdown}/mobile-rich-markdown-editor-contract.ts (100%) rename mobile/src/components/{ => rich-markdown}/mobile-rich-markdown-editor-keyboard-inset.ts (100%) rename mobile/src/components/{ => rich-markdown}/mobile-webview-editor-web-fallbacks.test.tsx (98%) rename mobile/src/components/rich-markdown/{ => serialization}/html-block-markdown.ts (100%) rename mobile/src/components/rich-markdown/{ => serialization}/html-code-block-markdown.ts (100%) rename mobile/src/components/rich-markdown/{ => serialization}/html-inline-markdown.ts (100%) rename mobile/src/components/rich-markdown/{ => serialization}/html-list-markdown.ts (100%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-code-block-dom.test.ts (84%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-code-delimiters.test.ts (90%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-code-fence.ts (100%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-code-literals.test.ts (93%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-empty-tasks.test.ts (88%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-escaping.ts (100%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-heading-code-blocks.test.ts (91%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-inline-render.ts (95%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-list-block-boundaries.test.ts (79%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-list-code-blocks.test.ts (85%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-list-linebreaks.test.ts (81%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-list-parse.ts (100%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-list-render.ts (97%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-paragraph-code-blocks.test.ts (93%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-quote-blocks.test.ts (93%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-round-trip.test.ts (94%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-table-linebreaks.test.ts (91%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-table-rows.ts (100%) rename mobile/src/components/rich-markdown/{ => serialization}/markdown-to-html.ts (98%) rename mobile/src/components/rich-markdown/{ => serialization}/task-checkbox-accessible-names.test.ts (86%) rename mobile/src/components/{ => rich-markdown}/use-mobile-rich-markdown-editor-controller.ts (100%) rename mobile/src/{components => files/media/presentation}/MobilePanZoomImage.tsx (96%) rename mobile/src/{components => files/media/presentation}/use-mobile-pan-zoom.ts (98%) rename mobile/src/{components => files/preview/documents}/MobileHtmlPreview.tsx (95%) rename mobile/src/{components => files/preview/documents}/MobilePreviewSourceTabs.tsx (91%) rename mobile/src/{components => files/preview/documents}/MobileSvgPreview.tsx (86%) rename mobile/src/{components => files/preview/documents}/html-preview-inert-links.test.ts (100%) rename mobile/src/{components => files/preview/documents}/html-preview-inert-links.ts (100%) rename mobile/src/{components => files/preview/documents}/html-preview-link-affordance.test.tsx (100%) rename mobile/src/{components => files/preview/documents}/use-html-preview-link-grant.ts (100%) rename mobile/src/{components => files/preview/documents}/use-html-preview-link-grant.web.ts (69%) delete mode 100644 mobile/src/files/preview/mobile-file-preview-retry.test.tsx rename mobile/src/{components => platform/text-selection}/inline-text-selection.ts (100%) rename mobile/src/{components => platform/web-document}/inline-script-json.ts (100%) 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 = '