From bc8236ea000b472ac9bee9566400de4e96717654 Mon Sep 17 00:00:00 2001 From: tianma-if Date: Sun, 2 Aug 2026 13:42:03 +0800 Subject: [PATCH] refactor: share memo content styling --- .../src/screens/WorkspaceMemoDetail.tsx | 20 ++++++++++++------- apps/mobile/src/screens/workspace-styles.ts | 5 +++-- apps/web/src/components/EditorPane.tsx | 15 +++++++++----- apps/web/src/styles/globals.css | 6 ++++-- packages/shared/src/index.ts | 1 + .../shared/src/memo-content-style.test.mjs | 13 ++++++++++++ packages/shared/src/memo-content-style.ts | 14 +++++++++++++ 7 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 packages/shared/src/memo-content-style.test.mjs create mode 100644 packages/shared/src/memo-content-style.ts diff --git a/apps/mobile/src/screens/WorkspaceMemoDetail.tsx b/apps/mobile/src/screens/WorkspaceMemoDetail.tsx index 44fe5596..c79cdc4f 100644 --- a/apps/mobile/src/screens/WorkspaceMemoDetail.tsx +++ b/apps/mobile/src/screens/WorkspaceMemoDetail.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useRef, useState, type ReactNode } from "react"; -import { resolveMemoContentMarkdown, type MemoDetail } from "@edgeever/shared"; +import { MEMO_CONTENT_STYLE, resolveMemoContentMarkdown, type MemoDetail } from "@edgeever/shared"; import { ActivityIndicator, Image as RNImage, Platform, ScrollView, StyleSheet, Text as RNText, useWindowDimensions, View, type ImageStyle, type StyleProp, type TextStyle, type ViewStyle } from "react-native"; import { Modal } from "react-native"; import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; @@ -231,7 +231,13 @@ export const MemoDetailModal = ({ const [activeMatchIndex, setActiveMatchIndex] = useState(0); const localePreference = useMobileLocalePreference(); const themedDetailMarkdownStyles = useMemo( - () => resolveMobileThemeStyles(detailMarkdownStyles, resolvedTheme), + () => ({ + ...resolveMobileThemeStyles(detailMarkdownStyles, resolvedTheme), + hr: { + ...detailMarkdownStyles.hr, + backgroundColor: MEMO_CONTENT_STYLE.divider.color[resolvedTheme], + }, + }), [resolvedTheme] ); const detailMarkdownRules = useMemo(() => { @@ -559,8 +565,8 @@ const HighlightedDetailText = ({ const detailMarkdownStyles = StyleSheet.create({ body: { color: "#0f172a", - fontSize: 17, - lineHeight: 27, + fontSize: MEMO_CONTENT_STYLE.body.fontSize, + lineHeight: MEMO_CONTENT_STYLE.body.lineHeight, }, blockquote: { backgroundColor: "#f8fafc", @@ -613,9 +619,9 @@ const detailMarkdownStyles = StyleSheet.create({ marginTop: 14, }, hr: { - backgroundColor: "#66ca80", - height: 1, - marginVertical: 24, + backgroundColor: MEMO_CONTENT_STYLE.divider.color.light, + height: MEMO_CONTENT_STYLE.divider.thickness, + marginVertical: MEMO_CONTENT_STYLE.divider.marginVertical, }, link: { color: "#059669", diff --git a/apps/mobile/src/screens/workspace-styles.ts b/apps/mobile/src/screens/workspace-styles.ts index ddcadaa1..ca9f1e34 100644 --- a/apps/mobile/src/screens/workspace-styles.ts +++ b/apps/mobile/src/screens/workspace-styles.ts @@ -1,4 +1,5 @@ import { StyleSheet } from "react-native"; +import { MEMO_CONTENT_STYLE } from "@edgeever/shared"; import { MOBILE_UI_METRICS } from "@edgeever/shared/mobile-ui"; import { resolveMobileThemeStyles, type MobileResolvedTheme } from "../lib/mobile-theme"; @@ -1556,8 +1557,8 @@ const baseWorkspaceStyles = StyleSheet.create({ }, detailMarkdown: { color: "#1f2937", - fontSize: 16, - lineHeight: 25, + fontSize: MEMO_CONTENT_STYLE.body.fontSize, + lineHeight: MEMO_CONTENT_STYLE.body.lineHeight, marginTop: 20, }, noteSearchPanel: { diff --git a/apps/web/src/components/EditorPane.tsx b/apps/web/src/components/EditorPane.tsx index 4c4beb07..0520579b 100644 --- a/apps/web/src/components/EditorPane.tsx +++ b/apps/web/src/components/EditorPane.tsx @@ -76,6 +76,7 @@ import { EDITOR_CONTENT_MAX_WIDTH, EDITOR_CONTENT_MAX_WIDTH_COLLAPSED } from "@/ import { countMemoCharacters, docToMarkdown, + MEMO_CONTENT_STYLE, markdownToDoc, resolveMemoContentDoc, type Notebook, @@ -2718,8 +2719,12 @@ const RichEditorPane = ({ ? editorTheme : "custom" } - style={ - editorTheme !== "default" && editorTheme !== "minimal-emerald" && editorTheme !== "outline-emerald" + style={{ + "--editor-body-font-size": `${MEMO_CONTENT_STYLE.body.fontSize}px`, + "--editor-body-line-height": String(MEMO_CONTENT_STYLE.body.lineHeight / MEMO_CONTENT_STYLE.body.fontSize), + "--memo-content-divider-color": MEMO_CONTENT_STYLE.divider.color[resolvedTheme], + "--memo-content-divider-spacing": `${MEMO_CONTENT_STYLE.divider.marginVertical}px`, + ...(editorTheme !== "default" && editorTheme !== "minimal-emerald" && editorTheme !== "outline-emerald" ? { "--editor-theme-bg": (resolvedTheme === "dark" ? customEditorTheme.dark : customEditorTheme.light).background, "--editor-theme-text": (resolvedTheme === "dark" ? customEditorTheme.dark : customEditorTheme.light).text, @@ -2727,9 +2732,9 @@ const RichEditorPane = ({ "--editor-theme-accent": (resolvedTheme === "dark" ? customEditorTheme.dark : customEditorTheme.light).accent, "--editor-theme-soft": (resolvedTheme === "dark" ? customEditorTheme.dark : customEditorTheme.light).soft, "--editor-theme-border": (resolvedTheme === "dark" ? customEditorTheme.dark : customEditorTheme.light).border, - } as CSSProperties - : undefined - } + } + : {}), + } as CSSProperties} className={cn( "edgeever-editor relative min-h-0 flex-1 bg-white", useMobilePlainTextEditor ? "overflow-visible" : "overflow-y-auto" diff --git a/apps/web/src/styles/globals.css b/apps/web/src/styles/globals.css index 919791a6..2674d9d7 100644 --- a/apps/web/src/styles/globals.css +++ b/apps/web/src/styles/globals.css @@ -42,6 +42,8 @@ --editor-body-font-size: 0.9375rem; --editor-body-line-height: 1.6; --editor-paragraph-spacing: 0.45rem; + --memo-content-divider-color: rgb(var(--brand-green-300-rgb)); + --memo-content-divider-spacing: 1.5rem; --editor-outline-width: 300px; --editor-content-max-width: 880px; @@ -565,9 +567,9 @@ textarea { } .ProseMirror hr { - margin: 1.5rem 0; + margin: var(--memo-content-divider-spacing) 0; border: 0; - border-top: 1px solid rgb(var(--brand-green-300-rgb)); + border-top: 1px solid var(--memo-content-divider-color); } .ProseMirror img { diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index bd7e6407..5ebdb441 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -3,6 +3,7 @@ export * from "./backup"; export * from "./github-feedback"; export * from "./image-display"; export * from "./memo-safety"; +export * from "./memo-content-style"; export * from "./note-links"; export * from "./notebooks"; export * from "./revision-diff"; diff --git a/packages/shared/src/memo-content-style.test.mjs b/packages/shared/src/memo-content-style.test.mjs new file mode 100644 index 00000000..f3069724 --- /dev/null +++ b/packages/shared/src/memo-content-style.test.mjs @@ -0,0 +1,13 @@ +import { describe, expect, test } from "bun:test"; +import { MEMO_CONTENT_STYLE } from "./memo-content-style.ts"; + +describe("shared memo content style", () => { + test("matches the established mobile PWA reading scale", () => { + expect(MEMO_CONTENT_STYLE.body).toEqual({ fontSize: 15, lineHeight: 24 }); + expect(MEMO_CONTENT_STYLE.divider).toEqual({ + color: { dark: "#4ade80", light: "#66ca80" }, + marginVertical: 24, + thickness: 1, + }); + }); +}); diff --git a/packages/shared/src/memo-content-style.ts b/packages/shared/src/memo-content-style.ts new file mode 100644 index 00000000..e8dd14fd --- /dev/null +++ b/packages/shared/src/memo-content-style.ts @@ -0,0 +1,14 @@ +export const MEMO_CONTENT_STYLE = { + body: { + fontSize: 15, + lineHeight: 24, + }, + divider: { + color: { + dark: "#4ade80", + light: "#66ca80", + }, + marginVertical: 24, + thickness: 1, + }, +} as const;