Skip to content

feat: make each theme have independent settings (color, font, size, c…#1567

Merged
yanglbme merged 2 commits into
mainfrom
feat/per-theme-settings
May 21, 2026
Merged

feat: make each theme have independent settings (color, font, size, c…#1567
yanglbme merged 2 commits into
mainfrom
feat/per-theme-settings

Conversation

@yanglbme
Copy link
Copy Markdown
Member

…ode theme)

Previously all themes shared the same primaryColor, fontFamily, fontSize, codeBlockTheme, headingStyles, isShowLineNumber, and isMacCodeBlock. Now each theme stores its own configuration in a single localStorage key (MD__themeSettings), and switching themes automatically loads the corresponding settings.

Added backward-compatible migration that reads legacy localStorage keys and merges them into the current theme's settings on first load.

…ode theme)

Previously all themes shared the same primaryColor, fontFamily, fontSize,
codeBlockTheme, headingStyles, isShowLineNumber, and isMacCodeBlock. Now
each theme stores its own configuration in a single localStorage key
(MD__themeSettings), and switching themes automatically loads the
corresponding settings.

Added backward-compatible migration that reads legacy localStorage keys
and merges them into the current theme's settings on first load.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 21, 2026 05:49
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 21, 2026

🚀 Cloudflare Workers Preview has been successfully deployed!

Preview URL: https://md-pr-1567.doocs.workers.dev

Built with commit 3eb4559

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 21, 2026

🚀 Surge Preview has been successfully deployed!

Preview URL: https://doocs-md-preview-pr-1567.surge.sh

Built with commit 3eb4559

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces per-theme style configuration in the web app so that switching themes also switches associated settings (color/font/size/code theme/heading styles/line numbers/Mac code block), while adding a one-time migration from legacy localStorage keys.

Changes:

  • Adds shared types/helpers for per-theme settings (PerThemeSettings, defaultPerThemeSettings, and a theme→settings map type).
  • Updates the theme Pinia store to persist settings per theme under a single key and expose per-theme settings via writable computed refs.
  • Adds backward-compatible migration from legacy unprefixed localStorage keys into the current theme’s per-theme settings.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
packages/shared/src/configs/style.ts Introduces per-theme settings types and a default factory function used by the web store.
apps/web/src/stores/theme.ts Reworks theme-related persisted state into a per-theme settings map and adds legacy key migration logic.

Comment thread apps/web/src/stores/theme.ts Outdated
Comment on lines +22 to +82
// 每个主题的独立配置(持久化到 localStorage)
const themeSettings = store.reactive<PerThemeSettingsMap>(
addPrefix(`themeSettings`),
{},
)

// 文本大小
const fontSize = store.reactive(`size`, defaultStyleConfig.fontSize)
// --- Legacy migration: run once on store init ---
const hasAnyLegacyKey = LEGACY_KEYS.some(key => localStorage.getItem(key) !== null)
const migrationKey = `MD__legacy_migrated`
const alreadyMigrated = localStorage.getItem(migrationKey) !== null

// 主色
const primaryColor = store.reactive(`color`, defaultStyleConfig.primaryColor)
if (hasAnyLegacyKey && !alreadyMigrated) {
const targetTheme = theme.value
const existing = themeSettings.value[targetTheme] ?? defaultPerThemeSettings()
const settings: PerThemeSettings = { ...existing }

// 代码块主题
const codeBlockTheme = store.reactive(`codeBlockTheme`, defaultStyleConfig.codeBlockTheme)
const legacyFont = localStorage.getItem(`fonts`)
if (legacyFont)
settings.fontFamily = legacyFont

// 图注格式
const legend = store.reactive(`legend`, defaultStyleConfig.legend)
const legacySize = localStorage.getItem(`size`)
if (legacySize)
settings.fontSize = legacySize

const legacyColor = localStorage.getItem(`color`)
if (legacyColor)
settings.primaryColor = legacyColor

const legacyCodeTheme = localStorage.getItem(`codeBlockTheme`)
if (legacyCodeTheme)
settings.codeBlockTheme = legacyCodeTheme

const legacyHeading = localStorage.getItem(`headingStyles`)
if (legacyHeading) {
try {
settings.headingStyles = JSON.parse(legacyHeading)
}
catch { /* ignore parse error */ }
}

const legacyMacBlock = localStorage.getItem(`isMacCodeBlock`)
if (legacyMacBlock !== null)
settings.isMacCodeBlock = legacyMacBlock === `true`

const legacyLineNum = localStorage.getItem(`isShowLineNumber`)
if (legacyLineNum !== null)
settings.isShowLineNumber = legacyLineNum === `true`

themeSettings.value = {
...themeSettings.value,
[targetTheme]: settings,
}

// Mark migration as done
localStorage.setItem(migrationKey, `1`)

// Clean up legacy keys
for (const key of LEGACY_KEYS) {
localStorage.removeItem(key)
}
}
Comment thread apps/web/src/stores/theme.ts Outdated
const fontSize = store.reactive(`size`, defaultStyleConfig.fontSize)
// --- Legacy migration: run once on store init ---
const hasAnyLegacyKey = LEGACY_KEYS.some(key => localStorage.getItem(key) !== null)
const migrationKey = `MD__legacy_migrated`
Comment thread apps/web/src/stores/theme.ts Outdated
Comment on lines +28 to +82
// --- Legacy migration: run once on store init ---
const hasAnyLegacyKey = LEGACY_KEYS.some(key => localStorage.getItem(key) !== null)
const migrationKey = `MD__legacy_migrated`
const alreadyMigrated = localStorage.getItem(migrationKey) !== null

// 主色
const primaryColor = store.reactive(`color`, defaultStyleConfig.primaryColor)
if (hasAnyLegacyKey && !alreadyMigrated) {
const targetTheme = theme.value
const existing = themeSettings.value[targetTheme] ?? defaultPerThemeSettings()
const settings: PerThemeSettings = { ...existing }

// 代码块主题
const codeBlockTheme = store.reactive(`codeBlockTheme`, defaultStyleConfig.codeBlockTheme)
const legacyFont = localStorage.getItem(`fonts`)
if (legacyFont)
settings.fontFamily = legacyFont

// 图注格式
const legend = store.reactive(`legend`, defaultStyleConfig.legend)
const legacySize = localStorage.getItem(`size`)
if (legacySize)
settings.fontSize = legacySize

const legacyColor = localStorage.getItem(`color`)
if (legacyColor)
settings.primaryColor = legacyColor

const legacyCodeTheme = localStorage.getItem(`codeBlockTheme`)
if (legacyCodeTheme)
settings.codeBlockTheme = legacyCodeTheme

const legacyHeading = localStorage.getItem(`headingStyles`)
if (legacyHeading) {
try {
settings.headingStyles = JSON.parse(legacyHeading)
}
catch { /* ignore parse error */ }
}

const legacyMacBlock = localStorage.getItem(`isMacCodeBlock`)
if (legacyMacBlock !== null)
settings.isMacCodeBlock = legacyMacBlock === `true`

const legacyLineNum = localStorage.getItem(`isShowLineNumber`)
if (legacyLineNum !== null)
settings.isShowLineNumber = legacyLineNum === `true`

themeSettings.value = {
...themeSettings.value,
[targetTheme]: settings,
}

// Mark migration as done
localStorage.setItem(migrationKey, `1`)

// Clean up legacy keys
for (const key of LEGACY_KEYS) {
localStorage.removeItem(key)
}
}
Comment on lines +168 to 178
// 重置样式(仅重置当前主题的配置)
const resetStyle = () => {
themeSettings.value = {
...themeSettings.value,
[theme.value]: defaultPerThemeSettings(),
}
isCiteStatus.value = defaultStyleConfig.isCiteStatus
isMacCodeBlock.value = defaultStyleConfig.isMacCodeBlock
isShowLineNumber.value = defaultStyleConfig.isShowLineNumber
isCountStatus.value = defaultStyleConfig.isCountStatus

theme.value = defaultStyleConfig.theme
fontFamily.value = defaultStyleConfig.fontFamily
fontSize.value = defaultStyleConfig.fontSize
primaryColor.value = defaultStyleConfig.primaryColor
codeBlockTheme.value = defaultStyleConfig.codeBlockTheme
legend.value = defaultStyleConfig.legend
headingStyles.value = { ...defaultStyleConfig.headingStyles }

isUseIndent.value = false
isUseJustify.value = false
}
…ode theme)

Previously all themes shared the same primaryColor, fontFamily, fontSize,
codeBlockTheme, headingStyles, isShowLineNumber, and isMacCodeBlock. Now
each theme stores its own configuration in a single localStorage key
(MD__themeSettings), and switching themes automatically loads the
corresponding settings.

Added backward-compatible migration that reads legacy localStorage keys
and merges them into the current theme's per-theme settings on first load.
Migration runs synchronously before store.reactive() installs its watch,
with all localStorage access wrapped in try/catch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@yanglbme yanglbme merged commit ec5f0ca into main May 21, 2026
2 checks passed
@yanglbme yanglbme deleted the feat/per-theme-settings branch May 21, 2026 06:03
@github-actions
Copy link
Copy Markdown

🗑️ Cloudflare Workers preview deployment has been cleaned up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants