diff --git a/src/app/globals.css b/src/app/globals.css index e5962e8..8173c6a 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -341,3 +341,137 @@ page-break-inside: avoid; } } + +/* ============================================================ + * V4 备课编辑器:纸区工具条 + 锚点 + inline-node + 对话体样式 + * ============================================================ */ + +/* V4 备课编辑器:纸区工具条 */ +.lp-paper-toolbar { + position: sticky; + top: 0; + margin: -64px -72px 24px; + padding: 10px 72px; + background: rgba(255,255,255,0.92); + backdrop-filter: blur(8px); + border-bottom: 1px solid var(--border); + display: flex; + gap: 2px; + z-index: 5; +} +.lp-tb-btn { + width: 28px; height: 28px; + border: none; background: transparent; + border-radius: 4px; cursor: pointer; + font-family: 'Inter', sans-serif; + font-size: 13px; color: var(--foreground); + display: inline-flex; align-items: center; justify-content: center; + transition: background .12s; +} +.lp-tb-btn:hover { background: var(--muted); } +.lp-tb-btn.is-active { background: var(--muted); color: var(--foreground); } +.lp-tb-btn.b { font-weight: 700; } +.lp-tb-btn.i { font-style: italic; font-family: 'Fraunces', serif; } +.lp-tb-btn.h1 { font-size: 11px; font-weight: 600; } +.lp-tb-btn.h2 { font-size: 10px; font-weight: 600; } +.lp-tb-btn.ul { font-size: 14px; } +.lp-tb-btn.quote { font-family: 'Fraunces', serif; font-style: italic; font-size: 14px; } +.lp-tb-divider { + width: 1px; + background: var(--border); + margin: 4px 4px; +} + +/* V4 锚点样式 */ +.lp-anchor-range { + background: var(--lp-anchor-range); + border-radius: 2px; + padding: 1px 2px; + margin: 0 -2px; + cursor: pointer; + border-bottom: 1.5px solid var(--lp-anchor-point); + transition: background .15s; +} +.lp-anchor-range:hover, .lp-anchor-range.is-active { + background: var(--lp-anchor-range-active); +} +.lp-anchor-point { + display: inline-block; + width: 16px; height: 16px; + line-height: 16px; + text-align: center; + border-radius: 50%; + background: var(--lp-anchor-point); + color: #fff; + font-family: 'Inter', sans-serif; + font-size: 9px; + font-weight: 600; + margin: 0 2px; + vertical-align: middle; + cursor: pointer; +} + +/* V4 inline-node 样式 */ +.lp-inline-node { + font-family: 'Inter', sans-serif; + margin: 20px 0 20px; + padding: 14px 18px 14px 20px; + border-left: 2px solid var(--lp-inline-node-border); + color: var(--lp-inline-node-text); + font-size: 13.5px; + line-height: 1.65; + transition: border-color .2s; +} +.lp-inline-node:hover { border-left-color: var(--foreground); } +.lp-inline-node-head { + display: flex; align-items: center; gap: 8px; + margin-bottom: 8px; + font-size: 10px; font-weight: 600; + text-transform: uppercase; letter-spacing: 0.08em; + color: var(--lp-inline-node-meta); +} +.lp-inline-node-title { + font-family: 'Inter', sans-serif; + font-weight: 600; + font-size: 14px; + color: #1a1a1a; + margin: 0 0 6px; + line-height: 1.35; +} +.lp-inline-node-body { + font-size: 13px; + line-height: 1.6; + color: #404040; +} + +/* V4 师生交互对话体 */ +.lp-qa-dialog { margin: 8px 0 4px; } +.lp-qa-turn { + display: grid; + grid-template-columns: 28px 1fr; + gap: 10px; + padding: 8px 0; + border-bottom: 1px dashed var(--border); + font-size: 12.5px; + line-height: 1.55; +} +.lp-qa-turn:last-child { border-bottom: none; } +.lp-qa-role { + font-family: 'JetBrains Mono', monospace; + font-size: 9px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + padding-top: 2px; + text-align: right; +} +.lp-qa-role.teacher { color: #1c1917; } +.lp-qa-role.student { color: #6b7280; } +.lp-qa-content { color: #404040; } +.lp-qa-prompt { + font-style: italic; + color: #525252; + font-size: 11px; + display: block; + margin-top: 2px; +} diff --git a/src/modules/lesson-preparation/components/paper-editor/paper-toolbar.tsx b/src/modules/lesson-preparation/components/paper-editor/paper-toolbar.tsx new file mode 100644 index 0000000..3da5932 --- /dev/null +++ b/src/modules/lesson-preparation/components/paper-editor/paper-toolbar.tsx @@ -0,0 +1,47 @@ +"use client"; + +import type { Editor } from "@tiptap/react"; + +interface Props { + editor: Editor; +} + +/** + * V4 浮动工具条:粘在纸顶部,毛玻璃半透明。 + * 仅在编辑器聚焦时显示(由父组件控制)。 + */ +export function PaperToolbar({ editor }: Props) { + const btn = ( + label: string, + action: () => void, + isActive: boolean, + className: string, + title: string, + ) => ( + + ); + + return ( +
+ {btn("B", () => editor.chain().focus().toggleBold().run(), editor.isActive("bold"), "b", "Bold")} + {btn("I", () => editor.chain().focus().toggleItalic().run(), editor.isActive("italic"), "i", "Italic")} + + {btn("H1", () => editor.chain().focus().toggleHeading({ level: 1 }).run(), editor.isActive("heading", { level: 1 }), "h1", "Heading 1")} + {btn("H2", () => editor.chain().focus().toggleHeading({ level: 2 }).run(), editor.isActive("heading", { level: 2 }), "h2", "Heading 2")} + + {btn("•", () => editor.chain().focus().toggleBulletList().run(), editor.isActive("bulletList"), "ul", "Bullet list")} + {btn("1.", () => editor.chain().focus().toggleOrderedList().run(), editor.isActive("orderedList"), "ol", "Ordered list")} + + {btn('"', () => editor.chain().focus().toggleBlockquote().run(), editor.isActive("blockquote"), "quote", "Quote")} + {btn("—", () => editor.chain().focus().setHorizontalRule().run(), false, "hr", "Divider")} +
+ ); +}