feat(web): add M0 live preview editor prototype

- CodeMirror 6 editor with Obsidian/Typora-style live preview: block
  markup revealed per line, inline markup revealed per element
- Fenced code highlighting via Shiki (github-dark, JS regex engine) in a
  Web Worker, languages loaded on demand
- Lezer syntax tree to HTML renderer for share pages, checked against
  the CommonMark 0.31.2 spec (640/652; 12 known parser deviations)
- Use markdownLanguage directly to avoid bundling lang-html/css/js
This commit is contained in:
eezd committed 2026-10-16 17:23:58 +08:00
1 parent 4636649576
commit a1597697e5
19 files changed
+3870

No files matched your search

+149
View File
@@ -0,0 +1,149 @@
/*
* 基础主题。--rm-* 变量即主题 API,第三方主题只需覆盖这些变量。
* 代码块与行内代码配色取自 vuejs.org(@vue/theme + Shiki github-dark)。
*/
:root {
--rm-font-text: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Hiragino Sans GB',
'Microsoft YaHei', 'Noto Sans CJK SC', 'Helvetica Neue', sans-serif;
--rm-font-mono: ui-monospace, 'SF Mono', 'SFMono-Regular', Menlo, Monaco, 'Cascadia Mono', Consolas,
'Liberation Mono', 'Noto Sans Mono', 'DejaVu Sans Mono', 'Roboto Mono', 'Courier New', monospace;
--rm-font-size: 16px;
--rm-line-height: 1.7;
--rm-content-width: 760px;
--rm-bg: #ffffff;
--rm-text: #213547;
--rm-text-muted: rgba(60, 60, 60, 0.7);
--rm-divider: rgba(60, 60, 60, 0.12);
--rm-accent: #42b883;
--rm-accent-strong: #33a06f;
--rm-selection: rgba(66, 184, 131, 0.22);
--rm-inline-code-bg: #f1f1f1;
--rm-inline-code-text: #476582;
--rm-code-bg: #292d3e;
--rm-code-text: #e1e4e8;
--rm-code-font-size: 14px;
}
@media (prefers-color-scheme: dark) {
:root {
--rm-bg: #1a1a1a;
--rm-text: rgba(255, 255, 255, 0.87);
--rm-text-muted: rgba(235, 235, 235, 0.6);
--rm-divider: rgba(84, 84, 84, 0.48);
--rm-accent: #42d392;
--rm-accent-strong: #42b883;
--rm-inline-code-bg: #2f2f2f;
--rm-inline-code-text: #aac8e4;
--rm-code-bg: #242424;
}
}
html,
body {
margin: 0;
background: var(--rm-bg);
color: var(--rm-text);
font-family: var(--rm-font-text);
}
.cm-editor {
font-size: var(--rm-font-size);
line-height: var(--rm-line-height);
background: var(--rm-bg);
color: var(--rm-text);
}
.cm-editor.cm-focused {
outline: none;
}
.cm-editor .cm-scroller {
font-family: var(--rm-font-text);
}
.cm-editor .cm-content {
max-width: var(--rm-content-width);
margin: 0 auto;
padding: 32px 24px 40vh;
caret-color: var(--rm-accent-strong);
}
.cm-editor .cm-cursor {
border-left: 2px solid var(--rm-accent-strong);
}
.cm-editor .cm-selectionBackground,
.cm-editor.cm-focused .cm-selectionBackground {
background: var(--rm-selection) !important;
}
/* 标题 */
.cm-line.cm-rm-h1 { font-size: 2em; line-height: 1.3; padding-top: 0.6em; }
.cm-line.cm-rm-h2 { font-size: 1.6em; line-height: 1.35; padding-top: 0.5em; }
.cm-line.cm-rm-h3 { font-size: 1.3em; line-height: 1.4; padding-top: 0.4em; }
.cm-line.cm-rm-h4 { font-size: 1.15em; padding-top: 0.3em; }
.cm-line.cm-rm-h5,
.cm-line.cm-rm-h6 { font-size: 1em; padding-top: 0.2em; }
.cm-line.cm-rm-h6 { color: var(--rm-text-muted); }
/* Markdown 标记(激活行显示源码时) */
.cm-rm-markup,
.cm-rm-url { color: var(--rm-text-muted); }
.cm-rm-link,
.cm-rm-link .cm-rm-url { color: var(--rm-accent-strong); text-decoration: none; cursor: text; }
/* 行内代码 */
.cm-rm-inline-code {
font-family: var(--rm-font-mono);
font-size: 0.875em;
background: var(--rm-inline-code-bg);
color: var(--rm-inline-code-text);
padding: 0.15em 0.4em;
border-radius: 4px;
}
/* 代码块 */
.cm-line.cm-rm-code {
font-family: var(--rm-font-mono);
font-size: var(--rm-code-font-size);
line-height: 1.5;
background: var(--rm-code-bg);
color: var(--rm-code-text);
padding: 0 24px;
tab-size: 4;
}
.cm-line.cm-rm-code .cm-rm-mono { font-family: inherit; }
.cm-line.cm-rm-code-begin { border-radius: 8px 8px 0 0; padding-top: 10px; }
.cm-line.cm-rm-code-end { border-radius: 0 0 8px 8px; padding-bottom: 10px; }
.cm-line.cm-rm-code-begin.cm-rm-code-end { border-radius: 8px; }
/* 未激活时的围栏行:压低行高,只显示右上角语言标签 */
.cm-line.cm-rm-code-fence { font-size: 12px; line-height: 1.2; text-align: right; }
.cm-rm-code-lang {
color: rgba(235, 235, 235, 0.38);
user-select: none;
}
.cm-rm-code .cm-rm-markup { color: rgba(235, 235, 235, 0.38); }
/* 引用 */
.cm-line.cm-rm-quote {
border-left: 3px solid var(--rm-divider);
padding-left: 16px;
color: var(--rm-text-muted);
}
/* 列表 */
.cm-rm-bullet { color: var(--rm-text-muted); display: inline-block; width: 1ch; }
.cm-rm-task { margin: 0 0.4em 0 0; vertical-align: -0.1em; accent-color: var(--rm-accent); cursor: pointer; }
/* 分隔线 */
.cm-rm-hr { display: inline-block; width: 100%; border-top: 1px solid var(--rm-divider); vertical-align: middle; }
/* 图片 */
.cm-rm-image { display: block; }
.cm-rm-image img { max-width: 100%; border-radius: 4px; }
.cm-rm-image-error { color: var(--rm-text-muted); font-style: italic; }
/* 表格(M2 前以等宽源码显示) */
.cm-line.cm-rm-table { font-family: var(--rm-font-mono); font-size: 0.9em; }
@media (max-width: 640px) {
.cm-editor .cm-content { padding: 16px 16px 40vh; }
.cm-line.cm-rm-code { padding: 0 12px; }
}