xfy 90fc6977b0 feat(editor): Tiptap 编辑器配色迁移到 Catppuccin(Latte/Mocha)
编辑器 CSS 此前用硬编码 GitHub 风格色(#0366d6/#d73a49/#24292e 等),
独立于 paper-* 语义 token。逐元素重映射到 Catppuccin 调色板:

亮色 Latte:正文 text、标题 text、placeholder overlay0、
  行内 code surface0/red、pre crust、blockquote surface1/subtext0、
  hr surface1、链接 blue、selection surface0、表格 surface1/crust、
  slash 弹层 mantle/surface0、checkbox/选中态 green(主强调)
暗色 Mocha:对应角色映射(text/cdd6f4 等)

上传遮罩的 rgba(255,255,255,*) 白字与 rgba(239,68,68,*) 红删除按钮
属中性覆盖层,保留不动。
2026-07-03 14:18:03 +08:00

655 lines
13 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Tiptap Editor - Typora-inspired, theme-aware */
.tiptap-editor {
position: relative;
width: 100%;
height: 100%;
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
font-size: 16px;
line-height: 1.75;
/* Catppuccin Latte text */
color: #4c4f69;
outline: none;
}
.tiptap-editor .ProseMirror {
width: 100%;
height: 100%;
padding: 24px 32px;
outline: none;
overflow-y: auto;
}
.tiptap-editor .ProseMirror-focused {
outline: none;
}
/* Placeholder - shown when document has only one empty paragraph */
.tiptap-editor .ProseMirror
p:first-child:last-child:has(br.ProseMirror-trailingBreak:only-child)::before {
content: '在此输入内容...';
float: left;
/* Latte overlay0 */
color: #9ca0b0;
pointer-events: none;
height: 0;
}
/* Typography */
.tiptap-editor h1 {
font-size: 2em;
font-weight: 700;
margin: 1.2em 0 0.6em;
line-height: 1.3;
/* Latte text */
color: #4c4f69;
}
.tiptap-editor h2 {
font-size: 1.5em;
font-weight: 600;
margin: 1em 0 0.5em;
line-height: 1.35;
color: #4c4f69;
}
.tiptap-editor h3 {
font-size: 1.25em;
font-weight: 600;
margin: 0.8em 0 0.4em;
color: #4c4f69;
}
.tiptap-editor p {
margin: 0.6em 0;
}
.tiptap-editor p:first-child {
margin-top: 0;
}
.tiptap-editor strong {
font-weight: 700;
color: #4c4f69;
}
.tiptap-editor em {
font-style: italic;
}
.tiptap-editor s {
text-decoration: line-through;
opacity: 0.7;
}
.tiptap-editor code {
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;
font-size: 0.875em;
/* Latte surface0 / red */
background: #ccd0da;
padding: 0.15em 0.4em;
border-radius: 3px;
color: #d20f39;
}
.tiptap-editor pre {
/* Latte crust */
background: #dce0e8;
border-radius: 6px;
padding: 16px;
overflow-x: auto;
margin: 1em 0;
}
.tiptap-editor pre code {
background: none;
padding: 0;
color: #4c4f69;
font-size: 0.85em;
line-height: 1.6;
}
.tiptap-editor blockquote {
/* Latte surface1 / subtext0 */
border-left: 4px solid #bcc0cc;
padding-left: 16px;
margin: 1em 0;
color: #6c6f85;
font-style: italic;
}
.tiptap-editor ul {
list-style-type: disc;
margin: 0.6em 0;
padding-left: 2em;
}
.tiptap-editor ol {
list-style-type: decimal;
margin: 0.6em 0;
padding-left: 2em;
}
.tiptap-editor li {
margin: 0.2em 0;
}
.tiptap-editor li > p {
margin: 0.2em 0;
}
.tiptap-editor hr {
/* Latte surface1 */
border: none;
border-top: 1px solid #bcc0cc;
margin: 1.5em 0;
}
/* Selection */
.tiptap-editor .ProseMirror ::selection {
/* Latte surface0 */
background: #ccd0da;
}
/* Cursor */
.tiptap-editor .ProseMirror-focused .ProseMirror-gapcursor {
display: none;
}
/* ========== Dark Theme (Catppuccin Mocha) ========== */
.dark .tiptap-editor {
/* Mocha text */
color: #cdd6f4;
}
.dark .tiptap-editor h1,
.dark .tiptap-editor h2,
.dark .tiptap-editor h3,
.dark .tiptap-editor strong {
color: #cdd6f4;
}
.dark .tiptap-editor .ProseMirror p.is-editor-empty:first-child::before {
/* Mocha overlay0 */
color: #6c7086;
}
.dark .tiptap-editor code {
/* Mocha surface0 / red */
background: #313244;
color: #f38ba8;
}
.dark .tiptap-editor pre {
background: #313244;
}
.dark .tiptap-editor pre code {
color: #cdd6f4;
}
.dark .tiptap-editor blockquote {
/* Mocha surface1 / subtext0 */
border-left-color: #45475a;
color: #a6adc8;
}
.dark .tiptap-editor hr {
border-top-color: #45475a;
}
.dark .tiptap-editor a {
/* Mocha blue */
color: #89b4fa;
}
.dark .tiptap-editor .ProseMirror ::selection {
background: rgba(30, 102, 245, 0.3);
}
/* ========== Link ========== */
.tiptap-editor a {
/* Latte blue */
color: #1e66f5;
text-decoration: none;
}
.tiptap-editor a:hover {
text-decoration: underline;
}
/* ========== Table ========== */
.tiptap-editor table {
border-collapse: collapse;
table-layout: fixed;
width: 100%;
margin: 1em 0;
overflow: hidden;
/* Latte surface1 */
border: 1px solid #bcc0cc;
border-radius: 6px;
}
.tiptap-editor table td,
.tiptap-editor table th {
min-width: 80px;
border: 1px solid #bcc0cc;
padding: 8px 12px;
position: relative;
vertical-align: top;
box-sizing: border-box;
}
.tiptap-editor table th {
/* Latte crust */
background: #dce0e8;
font-weight: 600;
text-align: left;
}
.tiptap-editor table .selectedCell {
/* Latte surface0 */
background: #ccd0da;
}
.tiptap-editor table .column-resize-handle {
position: absolute;
right: -2px;
top: 0;
bottom: -2px;
width: 4px;
/* Latte blue */
background-color: #1e66f5;
pointer-events: none;
}
.tiptap-editor .tableWrapper {
padding: 1rem 0;
overflow-x: auto;
}
.tiptap-editor .resize-cursor {
cursor: ew-resize;
cursor: col-resize;
}
/* ========== Slash Command ========== */
.slash-command {
position: fixed;
z-index: 9999;
/* Latte mantle */
background: #e6e9ef;
border: 1px solid #bcc0cc;
border-radius: 8px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12);
padding: 4px;
min-width: 260px;
max-height: 320px;
overflow-y: auto;
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
.slash-command-list {
display: flex;
flex-direction: column;
}
.slash-command-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
transition: background 0.15s;
}
.slash-command-item:hover,
.slash-command-item.is-selected {
/* Latte surface0 */
background: #ccd0da;
}
.slash-command-item-icon {
flex-shrink: 0;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background: #ccd0da;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
/* Latte subtext1 */
color: #5c5f77;
}
.slash-command-item-text {
flex: 1;
min-width: 0;
}
.slash-command-item-title {
font-size: 14px;
font-weight: 500;
color: #4c4f69;
line-height: 1.3;
}
.slash-command-item-desc {
font-size: 12px;
/* Latte overlay0 */
color: #9ca0b0;
line-height: 1.3;
}
/* ========== Task List (Enhanced) ========== */
.tiptap-editor ul[data-type='taskList'] {
list-style: none;
padding-left: 0;
}
.tiptap-editor ul[data-type='taskList'] li {
display: flex;
align-items: center;
gap: 0.5em;
margin: 0.3em 0;
}
.tiptap-editor ul[data-type='taskList'] li > label {
flex-shrink: 0;
cursor: pointer;
user-select: none;
/* label 默认 inline,内部 checkbox 是 inline-block 会受 line-height 撑出
基线间距导致 checkbox 在 label 内偏上。用 flex 居中并清零行高,
让 checkbox 严格在 label 盒子中心。 */
display: flex;
align-items: center;
line-height: 0;
}
.tiptap-editor ul[data-type='taskList'] li > label input[type='checkbox'] {
width: 16px;
height: 16px;
/* 主强调色 Latte green */
accent-color: #40a02b;
cursor: pointer;
}
.tiptap-editor ul[data-type='taskList'] li > div {
flex: 1;
}
/* task list 项内的段落去掉上下 margin.tiptap-editor p 默认 .6em 0
否则 margin 会撑高 div使 checkbox 与文本垂直中线错位、项间距过大。 */
.tiptap-editor ul[data-type='taskList'] li > div > p {
margin: 0;
}
.tiptap-editor ul[data-type='taskList'] li[data-checked='true'] > div {
text-decoration: line-through;
opacity: 0.6;
}
/* ========== Image ========== */
.tiptap-editor img {
max-width: 100%;
height: auto;
border-radius: 6px;
margin: 1em 0;
display: block;
}
.tiptap-editor img.ProseMirror-selectednode {
/* 主强调色 Latte green */
outline: 2px solid #40a02b;
border-radius: 6px;
}
/* ========== Upload Image Placeholder NodeView ========== */
.upload-image-container {
position: relative;
display: inline-block;
max-width: 100%;
}
.upload-image-container img {
max-width: 100%;
height: auto;
border-radius: 6px;
margin: 1em 0;
display: block;
/* blur/grayscale 出现与消失的过渡(上传中→成功/失败的 filter 切换) */
transition: filter 0.2s ease;
}
.upload-image-container.is-error img {
opacity: 0.5;
filter: grayscale(0.5);
}
/* 上传中图片高斯模糊遮罩文字spinner/"上传中…")在独立 overlay 层不受影响 */
.upload-image-container.is-uploading {
overflow: hidden;
}
.upload-image-container.is-uploading img {
filter: blur(8px);
}
.upload-image-overlay {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
border-radius: 6px;
pointer-events: none;
}
.upload-image-container.is-error .upload-image-overlay {
background: rgba(0, 0, 0, 0.35);
pointer-events: auto;
}
.upload-spinner {
width: 28px;
height: 28px;
border: 3px solid rgba(255, 255, 255, 0.4);
border-top-color: #fff;
border-radius: 50%;
animation: upload-spin 0.8s linear infinite;
}
@keyframes upload-spin {
to {
transform: rotate(360deg);
}
}
.upload-overlay-text {
color: #fff;
font-size: 13px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.upload-error-icon {
color: #fff;
font-size: 22px;
line-height: 1;
}
.upload-error-msg {
color: #fff;
font-size: 13px;
text-align: center;
padding: 0 12px;
max-width: 90%;
word-break: break-word;
}
.upload-error-actions {
display: flex;
gap: 8px;
}
.upload-btn {
padding: 3px 12px;
font-size: 12px;
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.6);
background: rgba(255, 255, 255, 0.15);
color: #fff;
cursor: pointer;
}
.upload-btn:hover {
background: rgba(255, 255, 255, 0.3);
}
.upload-btn-remove {
border-color: rgba(254, 202, 202, 0.7);
color: #fecaca;
}
.upload-btn-remove:hover {
background: rgba(239, 68, 68, 0.3);
}
/* ========== Dark Theme: Table (Mocha) ========== */
.dark .tiptap-editor table {
/* Mocha surface1 */
border-color: #45475a;
}
.dark .tiptap-editor table td,
.dark .tiptap-editor table th {
border-color: #45475a;
color: #cdd6f4;
}
.dark .tiptap-editor table th {
/* Mocha surface0 */
background: #313244;
}
.dark .tiptap-editor table .selectedCell {
background: rgba(30, 102, 245, 0.25);
}
/* ========== Dark Theme: Slash Command (Mocha) ========== */
.dark .slash-command {
/* Mocha mantle */
background: #181825;
border-color: #45475a;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
}
.dark .slash-command-item:hover,
.dark .slash-command-item.is-selected {
background: #313244;
}
.dark .slash-command-item-icon {
background: #313244;
color: #cdd6f4;
}
.dark .slash-command-item-title {
color: #cdd6f4;
}
.dark .slash-command-item-desc {
/* Mocha overlay0 */
color: #6c7086;
}
/* ========== Dark Theme: Image (Mocha) ========== */
.dark .tiptap-editor img.ProseMirror-selectednode {
/* 主强调色 Mocha green */
outline-color: #a6e3a1;
}
.dark .upload-image-container.is-error img {
opacity: 0.45;
}
.dark .upload-btn {
border-color: rgba(200, 200, 200, 0.4);
}
.dark .upload-btn-remove {
border-color: rgba(239, 68, 68, 0.5);
}
/* ========== Dark Theme: Task List (Mocha) ========== */
.dark .tiptap-editor ul[data-type='taskList'] li > label input[type='checkbox'] {
/* 主强调色 Mocha green */
accent-color: #a6e3a1;
}
/* ========== Source Toggle Button ========== */
.tiptap-toggle-btn {
position: absolute;
top: 12px;
right: 12px;
z-index: 10;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border: none;
border-radius: 6px;
/* Latte text 半透明底 */
background: rgba(76, 79, 105, 0.05);
color: #6c6f85;
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;
font-size: 13px;
font-weight: 600;
line-height: 1;
cursor: pointer;
opacity: 0.55;
transition:
opacity 0.15s,
background 0.15s;
}
.tiptap-toggle-btn:hover {
opacity: 1;
background: rgba(76, 79, 105, 0.1);
}
.dark .tiptap-toggle-btn {
/* Mocha text 半透明底 */
background: rgba(205, 214, 244, 0.06);
color: #6c7086;
}
.dark .tiptap-toggle-btn:hover {
background: rgba(205, 214, 244, 0.12);
}
/* ========== Source Textarea ========== */
.tiptap-source-textarea {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
padding: 24px 32px;
box-sizing: border-box;
border: none;
background: transparent;
/* Latte text */
color: #4c4f69;
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;
font-size: 14px;
line-height: 1.7;
resize: none;
outline: none;
overflow-y: auto;
z-index: 5;
}
.tiptap-source-textarea::placeholder {
/* Latte overlay0 */
color: #9ca0b0;
}
.dark .tiptap-source-textarea {
/* Mocha text */
color: #cdd6f4;
}
.dark .tiptap-source-textarea::placeholder {
/* Mocha overlay0 */
color: #6c7086;
}