mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 17:17:38 +00:00
fix(mobile): retain editor styling when switching color schemes (#35171)
* fix(mobile): retain editor styling when switching color schemes * test(mobile): fit theme-switch harness hooks under the file line cap --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
lucideBarrelPlugin
|
||||
} from '../build-mobile-web-app-bundle.mjs'
|
||||
import { mobileWebAppDependenciesPresent } from './mobile-web-app-bundle-dependencies.mjs'
|
||||
import { themeSwitchTest } from './mobile-web-app-theme-switch.mjs'
|
||||
import { textInputFontSizeFloor } from './platform-seams/mobile-web-app-text-input-font-size-seam.mjs'
|
||||
import {
|
||||
createBundleServer,
|
||||
@@ -77,6 +78,7 @@ 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 { applyMobileColorScheme } from '../theme/mobile-theme'
|
||||
|
||||
/**
|
||||
* What the route's own navigator supplies and a bare mount does not: react-navigation wraps every
|
||||
@@ -101,6 +103,7 @@ function Harness() {
|
||||
useEffect(() => {
|
||||
globalThis.__orcaEditor = {
|
||||
set: (next) => setState((previous) => ({ ...previous, ...next })),
|
||||
setScheme: applyMobileColorScheme,
|
||||
changes: [],
|
||||
secondChanges: [],
|
||||
links: [],
|
||||
@@ -293,9 +296,8 @@ async function setContentWithin(page, markdown, selector) {
|
||||
// Nested ul-in-p serialization once returned unchanged text, leaving next command against previous document.
|
||||
const bodyFor = (index) => `body text ${String(index)}`
|
||||
|
||||
function readSurface(page, surface = '#first-surface') {
|
||||
return page.evaluate((id) => document.querySelector(`${id} #editor`)?.innerHTML ?? null, surface)
|
||||
}
|
||||
const readSurface = (page, surface = '#first-surface') =>
|
||||
page.evaluate((id) => document.querySelector(`${id} #editor`)?.innerHTML ?? null, surface)
|
||||
|
||||
async function select(page, how) {
|
||||
await page.evaluate((mode) => {
|
||||
@@ -328,9 +330,9 @@ describeEditor(
|
||||
browser = await engine.launch()
|
||||
}, 180_000)
|
||||
|
||||
afterAll(async () => {
|
||||
await browser?.close()
|
||||
})
|
||||
afterAll(() => browser?.close())
|
||||
|
||||
themeSwitchTest(() => browser, openPage)
|
||||
|
||||
it('mounts the document, reports ready through the seam and never touches the bridge', async () => {
|
||||
const { page, consoleErrors } = await openPage(browser)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { expect, it } from 'vitest'
|
||||
|
||||
const readStyling = (page) =>
|
||||
page.evaluate(() => {
|
||||
const editor = document.querySelector('#editor')
|
||||
return {
|
||||
scheme: document.getElementById('orca-rich-markdown-document-style').dataset.scheme,
|
||||
scoped: editor.parentElement.classList.contains('orca-rich-markdown-document-host'),
|
||||
taskListStyle: getComputedStyle(editor.querySelector('ul[data-type="taskList"]'))
|
||||
.listStyleType
|
||||
}
|
||||
})
|
||||
|
||||
async function setScheme(page, scheme) {
|
||||
await page.evaluate((next) => globalThis.__orcaEditor.setScheme(next), scheme)
|
||||
await page.waitForFunction(
|
||||
(next) => document.getElementById('orca-rich-markdown-document-style').dataset.scheme === next,
|
||||
scheme
|
||||
)
|
||||
await page.evaluate(
|
||||
() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))
|
||||
)
|
||||
}
|
||||
|
||||
// React Native Web swaps the themed host View's className on a scheme change.
|
||||
export function themeSwitchTest(getBrowser, openPage) {
|
||||
it('keeps a mounted editor styled through color scheme changes', async () => {
|
||||
const { page, consoleErrors } = await openPage(getBrowser())
|
||||
try {
|
||||
await setScheme(page, 'dark')
|
||||
await page.evaluate(() => globalThis.__orcaEditor.set({ content: '- [ ] Open task' }))
|
||||
await page.waitForSelector('#editor ul[data-type="taskList"]')
|
||||
for (const scheme of ['dark', 'light', 'dark']) {
|
||||
await setScheme(page, scheme)
|
||||
expect(await readStyling(page)).toEqual({ scheme, scoped: true, taskListStyle: 'none' })
|
||||
}
|
||||
expect(consoleErrors).toEqual([])
|
||||
} finally {
|
||||
await page.close()
|
||||
}
|
||||
}, 180_000)
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
@@ -178,9 +179,11 @@ function MobileRichMarkdownEditorWebInner(
|
||||
// Mount document once; read promptForUrl via closure because rerunning on changing callback props discards live caret/document.
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (documentRef.current) {
|
||||
ensureDocumentStyle()
|
||||
// Layout effect: restore the host's scope class before paint after a themed className swap.
|
||||
useLayoutEffect(() => {
|
||||
const host = hostRef.current
|
||||
if (documentRef.current && host instanceof HTMLElement) {
|
||||
ensureDocumentStyle(host)
|
||||
}
|
||||
}, [colorScheme])
|
||||
|
||||
|
||||
@@ -28,7 +28,9 @@ export const RICH_MARKDOWN_HOST_CLASS = 'orca-rich-markdown-document-host'
|
||||
// Plant sheet once per page, scoped to host class; move document-wide theme/color/font globals onto host itself.
|
||||
// Leave sheet in head on unmount: removing host class makes it inert, and next mount reuses it.
|
||||
// Rewrite it in place when the color scheme changes; live editors keep their DOM and caret.
|
||||
export function ensureDocumentStyle() {
|
||||
export function ensureDocumentStyle(host: HTMLElement) {
|
||||
// React Native Web replaces themed View classes when its background changes.
|
||||
host.classList.add(RICH_MARKDOWN_HOST_CLASS)
|
||||
const scheme = getMobileColorScheme()
|
||||
const existing = document.getElementById(STYLE_ELEMENT_ID)
|
||||
if (existing?.dataset.scheme === scheme) {
|
||||
@@ -51,8 +53,7 @@ export function mountRichMarkdownWebDocument(
|
||||
host: HTMLElement,
|
||||
hooks: RichMarkdownWebDocumentHooks
|
||||
): RichMarkdownWebDocument {
|
||||
ensureDocumentStyle()
|
||||
host.classList.add(RICH_MARKDOWN_HOST_CLASS)
|
||||
ensureDocumentStyle(host)
|
||||
host.innerHTML = RICH_MARKDOWN_EDITOR_MARKUP
|
||||
const started = startDocumentOrGiveTheHostBack(host, hooks)
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user