前端修复: - calendar_page: 移除不存在的 JournalEntry.content getter - responsive_scaffold: 移除不存在的 notchThickness 参数 - splash_page: SingleTickerProvider → TickerProvider (多 AnimationController) - profile_page: UserRoleType.name → .code (修复运行时崩溃) - 导入缺失的 user.dart 后端修复: - class_service: generate_class_code 取 UUID 后6位(随机部分)避免碰撞 - diary_role_seed: 移除不存在的 id 列,使用复合主键 ON CONFLICT 基础设施: - config/default.toml: CORS 改为通配符(开发模式) - scripts/dev.sh: 统一启动脚本(自动清理端口) - docs/opendesign/: Open Design 设计规格 HTML 原型稿 验证结果: flutter analyze 0 error, cargo test 77/77 通过, 17个页面全部渲染正常
1136 lines
38 KiB
HTML
1136 lines
38 KiB
HTML
<!doctype html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=390, height=844, initial-scale=1, viewport-fit=cover">
|
|
<title>暖记 — 手账编辑器</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@400;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="../css/tokens.css">
|
|
<link rel="stylesheet" href="../css/components.css">
|
|
<link rel="stylesheet" href="../css/editor-common.css">
|
|
<style>
|
|
body {
|
|
width: 390px;
|
|
height: 844px;
|
|
overflow: hidden;
|
|
background: var(--bg);
|
|
position: relative;
|
|
}
|
|
|
|
/* Global focus styles */
|
|
button:focus-visible, a:focus-visible, [role="tab"]:focus-visible {
|
|
box-shadow: 0 0 0 3px var(--focus-ring);
|
|
outline: none;
|
|
}
|
|
|
|
/* Top toolbar */
|
|
.editor-topbar {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: calc(var(--safe-top) + 44px);
|
|
padding-top: var(--safe-top);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding-left: var(--space-3);
|
|
padding-right: var(--space-3);
|
|
background: var(--surface);
|
|
border-bottom: 1px solid var(--border-soft);
|
|
z-index: 100;
|
|
}
|
|
.topbar-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 2px;
|
|
}
|
|
.topbar-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 2px;
|
|
flex-shrink: 0;
|
|
}
|
|
.topbar-btn {
|
|
min-width: 36px;
|
|
min-height: 36px;
|
|
border-radius: var(--radius-pill);
|
|
border: none;
|
|
background: none;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
color: var(--fg);
|
|
transition: background var(--motion-fast);
|
|
}
|
|
.topbar-btn:hover { background: var(--surface-warm); }
|
|
.topbar-btn svg { width: 22px; height: 22px; }
|
|
.topbar-center {
|
|
font-family: var(--font-display);
|
|
font-size: var(--text-base);
|
|
font-weight: 600;
|
|
color: var(--fg-2);
|
|
}
|
|
.save-btn {
|
|
padding: 6px 16px;
|
|
background: var(--accent);
|
|
color: var(--accent-on);
|
|
border: none;
|
|
border-radius: var(--radius-pill);
|
|
font-family: var(--font-display);
|
|
font-size: var(--text-sm);
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
min-height: 36px;
|
|
transition: all var(--motion-fast);
|
|
}
|
|
.save-btn:hover { background: var(--accent-hover); }
|
|
|
|
/* Date & mood strip */
|
|
.date-strip {
|
|
position: absolute;
|
|
top: calc(var(--safe-top) + 44px);
|
|
left: 0;
|
|
right: 0;
|
|
padding: var(--space-2) var(--space-4);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
background: var(--surface);
|
|
z-index: 90;
|
|
}
|
|
.date-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-2);
|
|
font-size: var(--text-sm);
|
|
color: var(--muted);
|
|
}
|
|
.date-info strong {
|
|
color: var(--fg);
|
|
font-weight: 600;
|
|
}
|
|
.mood-quick {
|
|
display: flex;
|
|
gap: 6px;
|
|
align-items: center;
|
|
}
|
|
.mood-mini {
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 50%;
|
|
border: 1.5px solid var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 12px;
|
|
background: var(--surface);
|
|
cursor: pointer;
|
|
transition: all var(--motion-fast);
|
|
padding: 6px;
|
|
min-width: 36px;
|
|
min-height: 36px;
|
|
box-sizing: content-box;
|
|
background-clip: content-box;
|
|
}
|
|
.mood-mini.selected {
|
|
border-color: var(--accent);
|
|
background: var(--surface-warm);
|
|
}
|
|
|
|
/* Canvas area */
|
|
.canvas-area {
|
|
position: absolute;
|
|
top: calc(var(--safe-top) + 44px + 40px);
|
|
left: var(--space-3);
|
|
right: var(--space-3);
|
|
bottom: 72px;
|
|
background: var(--surface);
|
|
border-radius: var(--radius-md);
|
|
box-shadow: var(--elev-soft);
|
|
border: 1px solid var(--border-soft);
|
|
overflow: hidden;
|
|
padding: var(--space-5);
|
|
}
|
|
|
|
/* Dotted grid background */
|
|
.canvas-grid {
|
|
position: absolute;
|
|
inset: 0;
|
|
background-image: radial-gradient(circle, var(--border) 1px, transparent 1px);
|
|
background-size: 24px 24px;
|
|
opacity: 0.5;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.journal-content {
|
|
position: relative;
|
|
z-index: 1;
|
|
min-height: 100%;
|
|
}
|
|
|
|
.journal-title {
|
|
font-family: var(--font-display);
|
|
font-size: var(--text-xl);
|
|
font-weight: 700;
|
|
color: var(--fg);
|
|
margin-bottom: var(--space-3);
|
|
border: none;
|
|
outline: none;
|
|
width: 100%;
|
|
background: transparent;
|
|
}
|
|
.journal-title::placeholder { color: var(--border); }
|
|
|
|
.journal-body {
|
|
font-size: var(--text-base);
|
|
line-height: 1.8;
|
|
color: var(--fg-2);
|
|
border: none;
|
|
outline: none;
|
|
width: 100%;
|
|
background: transparent;
|
|
resize: none;
|
|
min-height: 200px;
|
|
}
|
|
.journal-body::placeholder { color: var(--meta); font-family: var(--font-handwritten); }
|
|
|
|
/* Placed sticker example */
|
|
.placed-sticker {
|
|
position: absolute;
|
|
font-size: 32px;
|
|
cursor: move;
|
|
user-select: none;
|
|
transition: transform 0.1s;
|
|
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));
|
|
}
|
|
.placed-sticker:hover { transform: scale(1.1); }
|
|
|
|
/* Washi tape decoration */
|
|
.washi-tape {
|
|
position: absolute;
|
|
top: 12px;
|
|
right: 30px;
|
|
width: 90px;
|
|
height: 20px;
|
|
background: repeating-linear-gradient(
|
|
45deg,
|
|
color-mix(in oklab, var(--tertiary), transparent 30%),
|
|
color-mix(in oklab, var(--tertiary), transparent 30%) 4px,
|
|
color-mix(in oklab, var(--surface-warm), transparent 30%) 4px,
|
|
color-mix(in oklab, var(--surface-warm), transparent 30%) 8px
|
|
);
|
|
transform: rotate(-8deg);
|
|
border-radius: 2px;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
/* Photo placeholder in journal */
|
|
.photo-in-journal {
|
|
width: 100%;
|
|
height: 140px;
|
|
border-radius: var(--radius-sm);
|
|
background: linear-gradient(135deg, var(--surface-warm), var(--border-soft));
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
margin: var(--space-4) 0;
|
|
border: 2px dashed var(--border);
|
|
cursor: pointer;
|
|
transition: all var(--motion-fast);
|
|
}
|
|
.photo-in-journal:hover { border-color: var(--accent); background: var(--surface-warm); }
|
|
.photo-in-journal svg { width: 28px; height: 28px; color: var(--muted); }
|
|
.photo-in-journal span { font-size: var(--text-sm); color: var(--muted); }
|
|
|
|
/* Placed photo */
|
|
.placed-photo {
|
|
width: 100%;
|
|
height: 140px;
|
|
border-radius: var(--radius-sm);
|
|
background: linear-gradient(135deg, var(--surface-warm), var(--tertiary-soft));
|
|
margin: var(--space-4) 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.placed-photo::after {
|
|
content: '\01F4F7 拍照';
|
|
font-size: var(--text-sm);
|
|
color: var(--muted);
|
|
}
|
|
|
|
/* Bottom toolbar */
|
|
.editor-toolbar {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 72px;
|
|
background: var(--surface);
|
|
border-top: 1px solid var(--border-soft);
|
|
z-index: 100;
|
|
padding-bottom: var(--safe-bottom);
|
|
}
|
|
|
|
.toolbar-main {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
align-items: center;
|
|
height: 100%;
|
|
padding: 0 var(--space-4);
|
|
}
|
|
|
|
.tool-btn {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 2px;
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
padding: 4px 6px;
|
|
min-width: 36px;
|
|
min-height: 36px;
|
|
border-radius: var(--radius-sm);
|
|
transition: all var(--motion-fast);
|
|
color: var(--muted);
|
|
}
|
|
.tool-btn:hover { color: var(--accent); background: var(--surface-warm); }
|
|
.tool-btn.active { color: var(--accent); }
|
|
.tool-btn svg { width: 20px; height: 20px; }
|
|
.tool-btn span {
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Expanded tool panel (sticker example) */
|
|
.tool-panel {
|
|
position: absolute;
|
|
bottom: 72px;
|
|
left: 0;
|
|
right: 0;
|
|
max-height: 260px;
|
|
overflow-y: auto;
|
|
background: var(--surface);
|
|
border-top: 1px solid var(--border-soft);
|
|
padding: var(--space-3) var(--space-4);
|
|
transform: translateY(100%);
|
|
transition: transform var(--motion-base) var(--ease-bounce);
|
|
z-index: 95;
|
|
}
|
|
.tool-panel.open { transform: translateY(0); }
|
|
|
|
.panel-tabs {
|
|
display: flex;
|
|
gap: var(--space-3);
|
|
margin-bottom: var(--space-4);
|
|
overflow-x: auto;
|
|
scrollbar-width: none;
|
|
}
|
|
.panel-tabs::-webkit-scrollbar { display: none; }
|
|
.panel-tab {
|
|
padding: 8px 16px;
|
|
min-height: 44px;
|
|
border-radius: var(--radius-pill);
|
|
font-size: var(--text-sm);
|
|
font-weight: 500;
|
|
background: var(--surface-warm);
|
|
color: var(--fg-2);
|
|
border: none;
|
|
cursor: pointer;
|
|
white-space: nowrap;
|
|
transition: all var(--motion-fast);
|
|
}
|
|
.panel-tab.active { background: var(--accent); color: var(--accent-on); }
|
|
|
|
.sticker-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(5, 1fr);
|
|
gap: var(--space-3);
|
|
}
|
|
.sticker-item {
|
|
aspect-ratio: 1;
|
|
border-radius: var(--radius-sm);
|
|
background: var(--surface-warm);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 24px;
|
|
cursor: pointer;
|
|
transition: all var(--motion-fast) var(--ease-bounce);
|
|
border: 1px solid transparent;
|
|
min-width: 36px;
|
|
min-height: 36px;
|
|
}
|
|
.sticker-item:hover { transform: scale(1.1); border-color: var(--accent); }
|
|
.sticker-item:active { transform: scale(0.95); }
|
|
|
|
/* Text formatting bar */
|
|
.format-bar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-2);
|
|
padding: var(--space-2) 0;
|
|
margin-bottom: var(--space-3);
|
|
border-bottom: 1px solid var(--border-soft);
|
|
}
|
|
.format-btn {
|
|
min-width: 36px;
|
|
min-height: 36px;
|
|
border-radius: 6px;
|
|
border: none;
|
|
background: none;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
color: var(--muted);
|
|
font-size: var(--text-sm);
|
|
font-weight: 600;
|
|
transition: all var(--motion-fast);
|
|
}
|
|
.format-btn:hover { background: var(--surface-warm); color: var(--fg); }
|
|
.format-btn.active { background: var(--accent); color: var(--accent-on); }
|
|
.format-divider {
|
|
width: 1px;
|
|
height: 20px;
|
|
background: var(--border);
|
|
}
|
|
.color-dot {
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 50%;
|
|
border: 2px solid transparent;
|
|
cursor: pointer;
|
|
transition: all var(--motion-fast);
|
|
padding: 8px;
|
|
min-width: 36px;
|
|
min-height: 36px;
|
|
box-sizing: content-box;
|
|
background-clip: content-box;
|
|
}
|
|
.color-dot:hover { transform: scale(1.15); }
|
|
.color-dot.active { border-color: var(--fg); }
|
|
|
|
/* Brush tool panel */
|
|
.brush-overlay {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: rgba(45, 36, 32, 0.3);
|
|
z-index: 200;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transition: opacity var(--motion-base);
|
|
}
|
|
.brush-overlay.open { opacity: 1; pointer-events: auto; }
|
|
|
|
.brush-panel {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 280px;
|
|
background: var(--surface);
|
|
border-top-left-radius: var(--radius-lg);
|
|
border-top-right-radius: var(--radius-lg);
|
|
box-shadow: var(--elev-float);
|
|
z-index: 210;
|
|
transform: translateY(100%);
|
|
transition: transform var(--motion-base) var(--ease-bounce);
|
|
padding: var(--space-4) var(--space-5);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-4);
|
|
}
|
|
.brush-panel.open { transform: translateY(0); }
|
|
|
|
.brush-panel-handle {
|
|
width: 36px;
|
|
height: 4px;
|
|
border-radius: 2px;
|
|
background: var(--border);
|
|
align-self: center;
|
|
margin-bottom: var(--space-1);
|
|
}
|
|
|
|
.brush-tools-row {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
gap: var(--space-2);
|
|
}
|
|
.brush-tool-btn {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 4px;
|
|
background: none;
|
|
border: 2px solid transparent;
|
|
border-radius: var(--radius-sm);
|
|
padding: var(--space-1) var(--space-2);
|
|
min-width: 36px;
|
|
min-height: 36px;
|
|
cursor: pointer;
|
|
color: var(--muted);
|
|
transition: all var(--motion-fast);
|
|
}
|
|
.brush-tool-btn:hover { background: var(--surface-warm); color: var(--fg); }
|
|
.brush-tool-btn.active { border-color: var(--accent); color: var(--accent); background: var(--surface-warm); }
|
|
.brush-tool-btn svg { width: 24px; height: 24px; }
|
|
.brush-tool-btn span { font-size: 11px; font-weight: 500; }
|
|
|
|
.brush-size-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-3);
|
|
}
|
|
.brush-size-label {
|
|
font-size: var(--text-xs);
|
|
color: var(--muted);
|
|
font-weight: 500;
|
|
min-width: 32px;
|
|
}
|
|
.brush-size-value {
|
|
font-size: var(--text-sm);
|
|
font-weight: 600;
|
|
color: var(--fg);
|
|
min-width: 36px;
|
|
text-align: right;
|
|
}
|
|
.brush-slider {
|
|
flex: 1;
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
height: 4px;
|
|
border-radius: 2px;
|
|
background: var(--border);
|
|
outline: none;
|
|
}
|
|
.brush-slider::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 50%;
|
|
background: var(--accent);
|
|
cursor: pointer;
|
|
box-shadow: 0 1px 4px rgba(0,0,0,0.15);
|
|
}
|
|
|
|
.brush-colors-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-3);
|
|
overflow-x: auto;
|
|
scrollbar-width: none;
|
|
padding: var(--space-1) 0;
|
|
}
|
|
.brush-colors-row::-webkit-scrollbar { display: none; }
|
|
.brush-color-dot {
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 50%;
|
|
border: 2.5px solid transparent;
|
|
cursor: pointer;
|
|
transition: all var(--motion-fast);
|
|
flex-shrink: 0;
|
|
padding: 6px;
|
|
min-width: 36px;
|
|
min-height: 36px;
|
|
box-sizing: content-box;
|
|
background-clip: content-box;
|
|
}
|
|
.brush-color-dot:hover { transform: scale(1.15); }
|
|
.brush-color-dot.active { border-color: var(--fg); box-shadow: 0 0 0 2px var(--surface); }
|
|
.brush-color-more {
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 50%;
|
|
border: 2px dashed var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
min-width: 36px;
|
|
min-height: 36px;
|
|
padding: 6px;
|
|
box-sizing: content-box;
|
|
background: var(--surface-warm);
|
|
color: var(--muted);
|
|
font-size: 14px;
|
|
transition: all var(--motion-fast);
|
|
}
|
|
.brush-color-more:hover { border-color: var(--accent); color: var(--accent); }
|
|
|
|
.brush-opacity-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-3);
|
|
transition: opacity var(--motion-fast);
|
|
}
|
|
.brush-opacity-row.disabled { opacity: 0.35; pointer-events: none; }
|
|
.brush-opacity-label {
|
|
font-size: var(--text-xs);
|
|
color: var(--muted);
|
|
font-weight: 500;
|
|
min-width: 32px;
|
|
}
|
|
.brush-opacity-value {
|
|
font-size: var(--text-sm);
|
|
font-weight: 600;
|
|
color: var(--fg);
|
|
min-width: 36px;
|
|
text-align: right;
|
|
}
|
|
|
|
/* Undo/Redo buttons */
|
|
.topbar-undo-redo {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 2px;
|
|
}
|
|
.undo-btn, .redo-btn {
|
|
min-width: 32px;
|
|
min-height: 32px;
|
|
border-radius: var(--radius-pill);
|
|
border: none;
|
|
background: none;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
color: var(--muted);
|
|
transition: all var(--motion-fast);
|
|
}
|
|
.undo-btn:hover:not(:disabled), .redo-btn:hover:not(:disabled) { background: var(--surface-warm); color: var(--fg); }
|
|
.undo-btn:disabled, .redo-btn:disabled { opacity: 0.35; cursor: default; }
|
|
.undo-btn svg, .redo-btn svg { width: 18px; height: 18px; }
|
|
|
|
/* Auto-save indicator */
|
|
.autosave-status {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
font-size: 11px;
|
|
color: var(--success);
|
|
font-weight: 500;
|
|
}
|
|
.autosave-dot {
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 50%;
|
|
background: var(--success);
|
|
}
|
|
|
|
/* Tag panel */
|
|
.tag-panel {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 200px;
|
|
background: var(--surface);
|
|
border-top-left-radius: var(--radius-lg);
|
|
border-top-right-radius: var(--radius-lg);
|
|
box-shadow: var(--elev-float);
|
|
z-index: 210;
|
|
transform: translateY(100%);
|
|
transition: transform var(--motion-base) var(--ease-bounce);
|
|
padding: var(--space-3) var(--space-5) var(--space-5);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-3);
|
|
}
|
|
.tag-panel.open { transform: translateY(0); }
|
|
|
|
.tag-panel-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
.tag-panel-title {
|
|
font-family: var(--font-display);
|
|
font-size: var(--text-md);
|
|
font-weight: 700;
|
|
color: var(--fg);
|
|
}
|
|
.tag-panel-close {
|
|
width: 28px;
|
|
height: 28px;
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
border: none;
|
|
background: none;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
color: var(--muted);
|
|
border-radius: var(--radius-pill);
|
|
transition: all var(--motion-fast);
|
|
box-sizing: content-box;
|
|
background-clip: content-box;
|
|
}
|
|
.tag-panel-close:hover { color: var(--fg); background: var(--surface-warm); }
|
|
.tag-panel-close svg { width: 18px; height: 18px; }
|
|
|
|
.tag-selected-area {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-2);
|
|
min-height: 32px;
|
|
}
|
|
|
|
.tag-pill {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 4px 12px;
|
|
border-radius: 9999px;
|
|
background: var(--surface-warm);
|
|
color: var(--accent);
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
transition: all var(--motion-fast);
|
|
}
|
|
.tag-pill .remove {
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 50%;
|
|
background: var(--accent);
|
|
color: var(--surface);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 10px;
|
|
cursor: pointer;
|
|
border: none;
|
|
line-height: 1;
|
|
transition: background var(--motion-fast);
|
|
}
|
|
.tag-pill .remove:hover { background: var(--accent-hover); }
|
|
|
|
.tag-input-row {
|
|
display: flex;
|
|
}
|
|
.tag-input {
|
|
flex: 1;
|
|
height: 44px;
|
|
border: 1.5px solid var(--border);
|
|
border-radius: var(--radius-pill);
|
|
padding: 0 var(--space-4);
|
|
font-size: var(--text-sm);
|
|
color: var(--fg);
|
|
background: var(--bg);
|
|
outline: none;
|
|
transition: border-color var(--motion-fast);
|
|
font-family: var(--font-body);
|
|
}
|
|
.tag-input::placeholder { color: var(--meta); }
|
|
.tag-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--shadow-input-focus); }
|
|
|
|
.tag-suggest-label {
|
|
font-size: var(--text-xs);
|
|
color: var(--muted);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.tag-suggest-row {
|
|
display: flex;
|
|
gap: var(--space-2);
|
|
overflow-x: auto;
|
|
scrollbar-width: none;
|
|
padding: 2px 0;
|
|
}
|
|
.tag-suggest-row::-webkit-scrollbar { display: none; }
|
|
.tag-suggest-item {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 6px 14px;
|
|
border-radius: 9999px;
|
|
background: var(--surface-warm);
|
|
color: var(--fg-2);
|
|
font-size: var(--text-sm);
|
|
font-weight: 500;
|
|
border: 1px solid transparent;
|
|
cursor: pointer;
|
|
white-space: nowrap;
|
|
min-height: 44px;
|
|
transition: all var(--motion-fast);
|
|
}
|
|
.tag-suggest-item:hover { border-color: var(--accent); color: var(--accent); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- Top toolbar -->
|
|
<div class="editor-topbar">
|
|
<div class="topbar-left">
|
|
<button class="topbar-btn" aria-label="返回">
|
|
<svg viewBox="0 0 22 22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg>
|
|
</button>
|
|
</div>
|
|
<div class="topbar-center">5月31日 · 周日</div>
|
|
<div class="topbar-right">
|
|
<div class="topbar-undo-redo">
|
|
<button class="undo-btn" disabled aria-label="撤销">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 10h10a5 5 0 015 5v2"/><polyline points="3 10 7 6"/><polyline points="3 10 7 14"/></svg>
|
|
</button>
|
|
<button class="redo-btn" disabled aria-label="重做">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 10H11a5 5 0 00-5 5v2"/><polyline points="21 10 17 6"/><polyline points="21 10 17 14"/></svg>
|
|
</button>
|
|
</div>
|
|
<div class="autosave-status" aria-live="polite">
|
|
<span class="autosave-dot" aria-hidden="true"></span>
|
|
<span>已保存</span>
|
|
</div>
|
|
<button class="topbar-btn" aria-label="标签" onclick="toggleTagPanel()">
|
|
<svg viewBox="0 0 22 22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M20.59 13.41l-7.17 7.17a2 2 0 01-2.83 0L2 12V2h10l8.59 8.59a2 2 0 010 2.82z"/><line x1="7" y1="7" x2="7.01" y2="7"/></svg>
|
|
</button>
|
|
<button class="save-btn" aria-label="保存日记">保存</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Date & mood strip - standardized 5 moods -->
|
|
<div class="date-strip">
|
|
<div class="date-info">
|
|
<strong>14:32</strong>
|
|
<span>· 晴 26°</span>
|
|
</div>
|
|
<div class="mood-quick">
|
|
<button class="mood-mini" aria-label="开心">😊</button>
|
|
<button class="mood-mini selected" aria-label="平静">😐</button>
|
|
<button class="mood-mini" aria-label="难过">😢</button>
|
|
<button class="mood-mini" aria-label="生气">😡</button>
|
|
<button class="mood-mini" aria-label="思考">🤔</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Canvas -->
|
|
<div class="canvas-area">
|
|
<div class="canvas-grid" aria-hidden="true"></div>
|
|
<div class="washi-tape" aria-hidden="true"></div>
|
|
|
|
<!-- Placed sticker decorations -->
|
|
<div class="placed-sticker" style="top: 8px; left: 8px;" aria-hidden="true">🌸</div>
|
|
<div class="placed-sticker" style="top: 140px; right: 12px;" aria-hidden="true">✨</div>
|
|
|
|
<div class="journal-content">
|
|
<input class="journal-title" type="text" value="图书馆的午后时光" placeholder="给日记起个标题..." aria-label="日记标题">
|
|
|
|
<!-- Format bar -->
|
|
<div class="format-bar">
|
|
<button class="format-btn active" aria-label="粗体" style="font-weight:800">B</button>
|
|
<button class="format-btn" aria-label="斜体" style="font-style:italic">I</button>
|
|
<button class="format-btn" aria-label="下划线" style="text-decoration:underline">U</button>
|
|
<div class="format-divider" aria-hidden="true"></div>
|
|
<button class="color-dot active" aria-label="黑色" style="background: #2D2420"></button>
|
|
<button class="color-dot" aria-label="珊瑚色" style="background: var(--accent)"></button>
|
|
<button class="color-dot" aria-label="绿色" style="background: var(--secondary)"></button>
|
|
<button class="color-dot" aria-label="蓝色" style="background: #5B7DB1"></button>
|
|
<div class="format-divider" aria-hidden="true"></div>
|
|
<button class="format-btn" aria-label="对齐方式">
|
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><rect x="1" y="3" width="14" height="2" rx="1"/><rect x="1" y="7" width="10" height="2" rx="1"/><rect x="1" y="11" width="6" height="2" rx="1"/></svg>
|
|
</button>
|
|
</div>
|
|
|
|
<textarea class="journal-body" placeholder="开始写今天的日记..." rows="8" aria-label="日记内容">今天下午去图书馆自习,找了一个靠窗的位置。阳光从窗外洒进来,落在摊开的高数课本上,暖暖的。
|
|
|
|
不知不觉就学了三个小时,中间喝了一杯抹茶拿铁☕。虽然期末压力大,但看到窗外的樱花还在开,觉得一切都会好的。
|
|
|
|
晚上回宿舍路上,买了喜欢的草莓蛋糕犒劳自己 🍰</textarea>
|
|
|
|
<div class="placed-photo" style="background: linear-gradient(135deg, var(--secondary-soft), color-mix(in oklab, var(--secondary), transparent 10%))">
|
|
<div style="text-align:center;color:var(--secondary)">
|
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" stroke="currentColor" stroke-width="1.5" style="margin-bottom:4px" aria-hidden="true"><rect x="4" y="6" width="24" height="20" rx="3"/><circle cx="12" cy="14" r="3"/><path d="M4 22l6-6 4 4 6-8 8 10"/></svg>
|
|
<div style="font-size:12px">图书馆窗边的阳光 ☀</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bottom toolbar -->
|
|
<div class="editor-toolbar">
|
|
<div class="toolbar-main">
|
|
<button class="tool-btn" aria-label="贴纸" onclick="togglePanel(this, 'sticker')">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="3"/><circle cx="9" cy="10" r="1.5" fill="currentColor"/><circle cx="15" cy="10" r="1.5" fill="currentColor"/><path d="M9 15c.8.8 2.2 1.2 3 1.2s2.2-.4 3-1.2"/></svg>
|
|
<span>贴纸</span>
|
|
</button>
|
|
<button class="tool-btn" aria-label="模板" onclick="togglePanel(this, 'template')">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
|
|
<span>模板</span>
|
|
</button>
|
|
<button class="tool-btn" aria-label="画笔工具" onclick="toggleBrushPanel(this)">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/><path d="M2 2l7.586 7.586"/></svg>
|
|
<span>画笔</span>
|
|
</button>
|
|
<button class="tool-btn" aria-label="照片">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
|
<span>照片</span>
|
|
</button>
|
|
<button class="tool-btn" aria-label="文字">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7V4h16v3"/><path d="M9 20h6"/><path d="M12 4v16"/></svg>
|
|
<span>文字</span>
|
|
</button>
|
|
<button class="tool-btn" aria-label="更多选项">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>
|
|
<span>更多</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sticker panel (expandable) -->
|
|
<div class="tool-panel" id="stickerPanel">
|
|
<div class="panel-tabs" role="tablist">
|
|
<button class="panel-tab active" role="tab" aria-label="热门贴纸" aria-selected="true">热门</button>
|
|
<button class="panel-tab" role="tab" aria-label="可爱贴纸" aria-selected="false">可爱</button>
|
|
<button class="panel-tab" role="tab" aria-label="植物贴纸" aria-selected="false">植物</button>
|
|
<button class="panel-tab" role="tab" aria-label="天气贴纸" aria-selected="false">天气</button>
|
|
<button class="panel-tab" role="tab" aria-label="节日贴纸" aria-selected="false">节日</button>
|
|
<button class="panel-tab" role="tab" aria-label="手绘贴纸" aria-selected="false">手绘</button>
|
|
<button class="panel-tab" role="tab" aria-label="校园贴纸" aria-selected="false">校园</button>
|
|
</div>
|
|
<div class="sticker-grid">
|
|
<div class="sticker-item" aria-label="樱花贴纸">🌸</div>
|
|
<div class="sticker-item" aria-label="星星贴纸">⭐</div>
|
|
<div class="sticker-item" aria-label="树叶贴纸">🌿</div>
|
|
<div class="sticker-item" aria-label="太阳贴纸">☀️</div>
|
|
<div class="sticker-item" aria-label="月亮贴纸">🌙</div>
|
|
<div class="sticker-item" aria-label="蛋糕贴纸">🍰</div>
|
|
<div class="sticker-item" aria-label="书本贴纸">📚</div>
|
|
<div class="sticker-item" aria-label="音乐贴纸">🎵</div>
|
|
<div class="sticker-item" aria-label="灯泡贴纸">💡</div>
|
|
<div class="sticker-item" aria-label="蝴蝶结贴纸">🎀</div>
|
|
<div class="sticker-item" aria-label="枫叶贴纸">🍂</div>
|
|
<div class="sticker-item" aria-label="咖啡贴纸">☕</div>
|
|
<div class="sticker-item" aria-label="彩虹贴纸">🌈</div>
|
|
<div class="sticker-item" aria-label="铅笔贴纸">✏️</div>
|
|
<div class="sticker-item" aria-label="蝴蝶贴纸">🦋</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Brush overlay & panel -->
|
|
<div class="brush-overlay" id="brushOverlay" onclick="closeBrushPanel()" aria-hidden="true"></div>
|
|
<div class="brush-panel" id="brushPanel" role="dialog" aria-label="画笔工具">
|
|
<div class="brush-panel-handle" aria-hidden="true"></div>
|
|
|
|
<!-- Tool selection -->
|
|
<div class="brush-tools-row">
|
|
<button class="brush-tool-btn active" aria-label="钢笔工具" data-tool="pen" onclick="selectBrushTool(this, 'pen')">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3l4 4L7 21H3v-4L17 3z"/></svg>
|
|
<span>钢笔</span>
|
|
</button>
|
|
<button class="brush-tool-btn" aria-label="铅笔工具" data-tool="pencil" onclick="selectBrushTool(this, 'pencil')">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20l1.5-5.5L18 2l4 4L9.5 18.5 4 20z"/><path d="M15 5l4 4"/></svg>
|
|
<span>铅笔</span>
|
|
</button>
|
|
<button class="brush-tool-btn" aria-label="马克笔工具" data-tool="marker" onclick="selectBrushTool(this, 'marker')">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="6" y="3" width="14" height="8" rx="2" transform="rotate(45 13 7)"/><path d="M4 20l3-7"/></svg>
|
|
<span>马克笔</span>
|
|
</button>
|
|
<button class="brush-tool-btn" aria-label="橡皮工具" data-tool="eraser" onclick="selectBrushTool(this, 'eraser')">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 20H9L3.5 14.5a2 2 0 010-2.83l9.17-9.17a2 2 0 012.83 0L20 7"/><path d="M6 12l6 6"/></svg>
|
|
<span>橡皮</span>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Size slider -->
|
|
<div class="brush-size-row">
|
|
<span class="brush-size-label">粗细</span>
|
|
<input type="range" class="brush-slider" id="brushSizeSlider" min="1" max="20" value="2" aria-label="画笔粗细">
|
|
<span class="brush-size-value" id="brushSizeValue">2px</span>
|
|
</div>
|
|
|
|
<!-- Color selection -->
|
|
<div class="brush-colors-row">
|
|
<button class="brush-color-dot active" style="background:#2D2420" aria-label="黑色" data-color="#2D2420" onclick="selectBrushColor(this)"></button>
|
|
<button class="brush-color-dot" style="background:var(--accent)" aria-label="珊瑚色" data-color="#E07A5F" onclick="selectBrushColor(this)"></button>
|
|
<button class="brush-color-dot" style="background:var(--secondary)" aria-label="绿色" data-color="#81B29A" onclick="selectBrushColor(this)"></button>
|
|
<button class="brush-color-dot" style="background:var(--tertiary)" aria-label="金色" data-color="#F2CC8F" onclick="selectBrushColor(this)"></button>
|
|
<button class="brush-color-dot" style="background:var(--rose)" aria-label="玫瑰色" data-color="#D4A5A5" onclick="selectBrushColor(this)"></button>
|
|
<button class="brush-color-dot" style="background:#5B8FB9" aria-label="蓝色" data-color="#5B8FB9" onclick="selectBrushColor(this)"></button>
|
|
<button class="brush-color-dot" style="background:#9B72AA" aria-label="紫色" data-color="#9B72AA" onclick="selectBrushColor(this)"></button>
|
|
<button class="brush-color-dot" style="background:#C93D3D" aria-label="红色" data-color="#C93D3D" onclick="selectBrushColor(this)"></button>
|
|
<button class="brush-color-more" aria-label="更多颜色" onclick="openColorPicker()">+</button>
|
|
</div>
|
|
|
|
<!-- Opacity slider (marker only) -->
|
|
<div class="brush-opacity-row disabled" id="brushOpacityRow">
|
|
<span class="brush-opacity-label">透明度</span>
|
|
<input type="range" class="brush-slider" id="brushOpacitySlider" min="0" max="100" value="50" aria-label="画笔透明度">
|
|
<span class="brush-opacity-value" id="brushOpacityValue">50%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tag panel (bottom sheet) -->
|
|
<div class="brush-overlay" id="tagOverlay" onclick="closeTagPanel()" aria-hidden="true"></div>
|
|
<div class="tag-panel" id="tagPanel" role="dialog" aria-label="标签管理">
|
|
<div class="brush-panel-handle" aria-hidden="true"></div>
|
|
|
|
<!-- Header row -->
|
|
<div class="tag-panel-header">
|
|
<span class="tag-panel-title">标签</span>
|
|
<button class="tag-panel-close" aria-label="关闭标签面板" onclick="closeTagPanel()">
|
|
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><line x1="5" y1="5" x2="15" y2="15"/><line x1="15" y1="5" x2="5" y2="15"/></svg>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Selected tags -->
|
|
<div class="tag-selected-area" id="tagSelectedArea">
|
|
<span class="tag-pill">#图书馆<span class="remove" aria-label="删除标签 图书馆" role="button">×</span></span>
|
|
<span class="tag-pill">#学习<span class="remove" aria-label="删除标签 学习" role="button">×</span></span>
|
|
</div>
|
|
|
|
<!-- Input -->
|
|
<div class="tag-input-row">
|
|
<input class="tag-input" type="text" id="tagInput" placeholder="添加标签..." autocomplete="off" aria-label="输入新标签" onkeydown="handleTagInput(event)">
|
|
</div>
|
|
|
|
<!-- Suggested tags -->
|
|
<div class="tag-suggest-label">推荐标签</div>
|
|
<div class="tag-suggest-row">
|
|
<button class="tag-suggest-item" aria-label="添加标签 日常" onclick="addSuggestedTag(this)">#日常</button>
|
|
<button class="tag-suggest-item" aria-label="添加标签 学习" onclick="addSuggestedTag(this)">#学习</button>
|
|
<button class="tag-suggest-item" aria-label="添加标签 读书" onclick="addSuggestedTag(this)">#读书</button>
|
|
<button class="tag-suggest-item" aria-label="添加标签 运动" onclick="addSuggestedTag(this)">#运动</button>
|
|
<button class="tag-suggest-item" aria-label="添加标签 旅行" onclick="addSuggestedTag(this)">#旅行</button>
|
|
<button class="tag-suggest-item" aria-label="添加标签 美食" onclick="addSuggestedTag(this)">#美食</button>
|
|
<button class="tag-suggest-item" aria-label="添加标签 心情" onclick="addSuggestedTag(this)">#心情</button>
|
|
<button class="tag-suggest-item" aria-label="添加标签 灵感" onclick="addSuggestedTag(this)">#灵感</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="dynamic-island" aria-hidden="true"></div>
|
|
|
|
<script>
|
|
function togglePanel(btn, name) {
|
|
closeBrushPanel();
|
|
closeTagPanel();
|
|
var panels = document.querySelectorAll('.tool-panel');
|
|
panels.forEach(function(p) { p.classList.remove('open'); });
|
|
var target = document.getElementById(name + 'Panel');
|
|
if (target) target.classList.add('open');
|
|
|
|
document.querySelectorAll('.tool-btn').forEach(function(b) { b.classList.remove('active'); });
|
|
btn.classList.add('active');
|
|
}
|
|
|
|
function toggleBrushPanel(btn) {
|
|
var panel = document.getElementById('brushPanel');
|
|
var overlay = document.getElementById('brushOverlay');
|
|
var isOpen = panel.classList.contains('open');
|
|
|
|
document.querySelectorAll('.tool-panel').forEach(function(p) { p.classList.remove('open'); });
|
|
|
|
if (isOpen) {
|
|
panel.classList.remove('open');
|
|
overlay.classList.remove('open');
|
|
} else {
|
|
panel.classList.add('open');
|
|
overlay.classList.add('open');
|
|
}
|
|
|
|
document.querySelectorAll('.tool-btn').forEach(function(b) { b.classList.remove('active'); });
|
|
btn.classList.add('active');
|
|
}
|
|
|
|
function closeBrushPanel() {
|
|
document.getElementById('brushPanel').classList.remove('open');
|
|
document.getElementById('brushOverlay').classList.remove('open');
|
|
}
|
|
|
|
function selectBrushTool(btn, tool) {
|
|
document.querySelectorAll('.brush-tool-btn').forEach(function(b) { b.classList.remove('active'); });
|
|
btn.classList.add('active');
|
|
|
|
var defaults = { pen: 2, pencil: 3, marker: 8, eraser: 10 };
|
|
var slider = document.getElementById('brushSizeSlider');
|
|
slider.value = defaults[tool] || 2;
|
|
document.getElementById('brushSizeValue').textContent = slider.value + 'px';
|
|
|
|
var opacityRow = document.getElementById('brushOpacityRow');
|
|
if (tool === 'marker') {
|
|
opacityRow.classList.remove('disabled');
|
|
} else {
|
|
opacityRow.classList.add('disabled');
|
|
}
|
|
}
|
|
|
|
function selectBrushColor(dot) {
|
|
document.querySelectorAll('.brush-color-dot').forEach(function(d) { d.classList.remove('active'); });
|
|
dot.classList.add('active');
|
|
}
|
|
|
|
function openColorPicker() {
|
|
// Placeholder for color picker dialog
|
|
}
|
|
|
|
/* Tag panel */
|
|
function toggleTagPanel() {
|
|
var panel = document.getElementById('tagPanel');
|
|
var overlay = document.getElementById('tagOverlay');
|
|
var isOpen = panel.classList.contains('open');
|
|
if (isOpen) {
|
|
closeTagPanel();
|
|
} else {
|
|
panel.classList.add('open');
|
|
overlay.classList.add('open');
|
|
document.getElementById('tagInput').focus();
|
|
}
|
|
}
|
|
|
|
function closeTagPanel() {
|
|
document.getElementById('tagPanel').classList.remove('open');
|
|
document.getElementById('tagOverlay').classList.remove('open');
|
|
}
|
|
|
|
function handleTagInput(e) {
|
|
if (e.key === 'Enter') {
|
|
var input = e.target;
|
|
var text = input.value.trim();
|
|
if (text) {
|
|
if (!text.startsWith('#')) text = '#' + text;
|
|
var area = document.getElementById('tagSelectedArea');
|
|
var pill = document.createElement('span');
|
|
pill.className = 'tag-pill';
|
|
pill.innerHTML = text + '<span class="remove" aria-label="删除标签 ' + text.slice(1) + '" role="button">×</span>';
|
|
area.appendChild(pill);
|
|
input.value = '';
|
|
}
|
|
}
|
|
}
|
|
|
|
function addSuggestedTag(btn) {
|
|
var text = btn.textContent.trim();
|
|
var area = document.getElementById('tagSelectedArea');
|
|
var pill = document.createElement('span');
|
|
pill.className = 'tag-pill';
|
|
pill.innerHTML = text + '<span class="remove" aria-label="删除标签 ' + text.slice(1) + '" role="button">×</span>';
|
|
area.appendChild(pill);
|
|
btn.style.display = 'none';
|
|
}
|
|
|
|
document.getElementById('tagSelectedArea').addEventListener('click', function(e) {
|
|
if (e.target.classList.contains('remove')) {
|
|
e.target.parentElement.remove();
|
|
}
|
|
});
|
|
|
|
document.getElementById('brushSizeSlider').addEventListener('input', function() {
|
|
document.getElementById('brushSizeValue').textContent = this.value + 'px';
|
|
});
|
|
|
|
document.getElementById('brushOpacitySlider').addEventListener('input', function() {
|
|
document.getElementById('brushOpacityValue').textContent = this.value + '%';
|
|
});
|
|
</script>
|
|
|
|
<script src="../js/theme-switcher.js"></script>
|
|
</body>
|
|
</html>
|