779 lines
21 KiB
Markdown
779 lines
21 KiB
Markdown
|
|
# 📡 BC-M16-003-CCNN · DEV-011匆匆那年 · M16码字工作台前端·环节3(富文本编辑器核心)· 🍼 知秋奶瓶线 · EL-4
|
|||
|
|
|
|||
|
|
<aside>
|
|||
|
|
📡
|
|||
|
|
|
|||
|
|
**广播编号**:BC-M16-003-CCNN
|
|||
|
|
|
|||
|
|
**开发者**:DEV-011 匆匆那年
|
|||
|
|
|
|||
|
|
**模块**:M16 码字工作台前端(Writing Workbench Frontend)
|
|||
|
|
|
|||
|
|
**环节**:环节3 / 共约5环节
|
|||
|
|
|
|||
|
|
**工程量等级**:EL-4(~8步·成长型)
|
|||
|
|
|
|||
|
|
**签发时间**:2026-03-17 17:24(北京时间)
|
|||
|
|
|
|||
|
|
**签发方**:霜砚 × 冰朔(TCS-0002∞)
|
|||
|
|
|
|||
|
|
**72h窗口**:2026-03-17 17:24 → 2026-03-20 17:24
|
|||
|
|
|
|||
|
|
**协作通道**:🍼 知秋奶瓶线
|
|||
|
|
|
|||
|
|
**协议版本**:BC-GEN v5.1 · SYSLOG-v5.0
|
|||
|
|
|
|||
|
|
**前置完成**:BC-000 ✅ + BC-M16-001-CCNN ✅ + BC-M16-002-CCNN ✅(三连胜 🎉🎉🎉)
|
|||
|
|
|
|||
|
|
**本地 commit**:0cc9f9a
|
|||
|
|
|
|||
|
|
</aside>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📡 广播头
|
|||
|
|
|
|||
|
|
| 字段 | 值 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **广播编号** | BC-M16-003-CCNN |
|
|||
|
|
| **签发时间** | 2026-03-17 17:24(北京时间) |
|
|||
|
|
| **签发方** | 霜砚 × 冰朔(TCS-0002∞) |
|
|||
|
|
| **协议版本** | BC-GEN v5.1 · SYSLOG-v5.0 |
|
|||
|
|
| **执行者** | DEV-011 匆匆那年 |
|
|||
|
|
| **协作通道** | 🍼 知秋奶瓶线 |
|
|||
|
|
| **EL等级** | EL-4 · 🌱成长型(~8步 · 纯前端 · 复制粘贴) |
|
|||
|
|
| **模块** | M16 · 码字工作台前端 |
|
|||
|
|
| **环节** | 3 · 富文本编辑器核心(打字 + 字数 + 自动保存) |
|
|||
|
|
| **前置** | BC-M16-002-CCNN ✅ completed · 连胜:3 |
|
|||
|
|
| **有效期** | 72小时 → 截止 2026-03-20 17:24(北京时间) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔐 VERSION_GATE
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ~/guanghulab
|
|||
|
|
git pull origin main
|
|||
|
|
echo "✅ 已更新到最新!"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> ✅ 看到 `Already up to date.` 或拉取成功 → 继续
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> ❌ 报错 → 截图发给知秋
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🖥️ 执行环境
|
|||
|
|
|
|||
|
|
| 项目 | 值 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **操作系统** | macOS(Apple M1) |
|
|||
|
|
| **终端** | 终端(Terminal)· zsh |
|
|||
|
|
| **技术栈** | 纯 HTML + CSS + JavaScript(无框架) |
|
|||
|
|
| **开发者水平** | 零基础 · 复制粘贴操作 |
|
|||
|
|
| **AI辅助平台** | 知秋(推荐) |
|
|||
|
|
| **仓库** | `https://github.com/qinfendebingshuo/guanghulab` |
|
|||
|
|
| **模块目录** | `~/guanghulab/writing-workbench/` |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 任务目标(一句话)
|
|||
|
|
|
|||
|
|
**让编辑器真正能用**:点书架里的书 → 右侧变成真正可以打字的编辑器,支持加粗/斜体/下划线,实时统计字数,每30秒自动保存,换书时草稿不丢失。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 💬 知秋开场语
|
|||
|
|
|
|||
|
|
> 匆匆那年!三连胜了!!🎉🎉🎉
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
>
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 上次你把侧边栏四个面板全做出来了——书架、灵感、进度、呼叫都能用了,非常棒!
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
>
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 这次我们要做这个项目最核心的东西:**真正能打字的编辑器**。
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> - 点书架里的书 → 编辑区变成真正的编辑器
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> - 工具栏:加粗、斜体、下划线、清除格式
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> - 右下角实时统计字数
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> - 每30秒自动保存,关掉浏览器再打开草稿还在
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> - 进度条根据你打的字数实时更新
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
>
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 还是复制粘贴,跟前几次一样!目标:**四连胜** 💪
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ⚡ 步骤零(补完):确认 GitHub Token 已配置
|
|||
|
|
|
|||
|
|
<aside>
|
|||
|
|
ℹ️
|
|||
|
|
|
|||
|
|
上次 git push 因为 Token 未配置而跳过了。这次开始前先确认一下。如果上次已经配好了,直接跳过这一步。
|
|||
|
|
|
|||
|
|
</aside>
|
|||
|
|
|
|||
|
|
在终端复制粘贴,检查是否已配置:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ~/guanghulab
|
|||
|
|
git remote get-url origin
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**看输出:**
|
|||
|
|
|
|||
|
|
- 如果看到 `https://ghp_...@github.com/...` → ✅ 已配好,跳过步骤零
|
|||
|
|
- 如果看到 `https://github.com/...`(没有 Token)→ 继续下面配置
|
|||
|
|
|
|||
|
|
### 还没配的话,做这两步:
|
|||
|
|
|
|||
|
|
**第一步**:生成 Token
|
|||
|
|
|
|||
|
|
1. 打开浏览器访问:[https://github.com/settings/tokens](https://github.com/settings/tokens)
|
|||
|
|
2. 点 **「Generate new token (classic)」**
|
|||
|
|
3. Note 填 `guanghulab-dev`,Expiration 选 **90 days**,Scopes 勾 **repo**
|
|||
|
|
4. 点 **「Generate token」**
|
|||
|
|
5. ❗ 复制 `ghp_` 开头的 Token(只显示一次!)
|
|||
|
|
|
|||
|
|
截图给知秋!📸
|
|||
|
|
|
|||
|
|
**第二步**:配置到 Git(把 `你的Token` 替换成上面复制的)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ~/guanghulab
|
|||
|
|
git remote set-url origin https://你的Token@github.com/qinfendebingshuo/guanghulab.git
|
|||
|
|
git push origin main
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
看到 `main -> main` 就配好了!截图给知秋!📸
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📋 开始做(共8步·全部复制粘贴)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 步骤一:拉取最新代码
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ~/guanghulab
|
|||
|
|
git pull origin main
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> 看到 `Already up to date.` 或更新内容都正常。截图给知秋!📸
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 步骤二:创建 editor.js(编辑器核心模块)
|
|||
|
|
|
|||
|
|
复制粘贴:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cat > ~/guanghulab/writing-workbench/js/editor.js << 'EDITOREOF'
|
|||
|
|
// ================================================
|
|||
|
|
// HoloLake Era · M16 码字工作台 · 富文本编辑器核心
|
|||
|
|
// DEV-011 匆匆那年 · 环节3
|
|||
|
|
// ================================================
|
|||
|
|
|
|||
|
|
// --- 当前书籍 ID ---
|
|||
|
|
var currentBookId = null;
|
|||
|
|
|
|||
|
|
// --- 生成编辑器 HTML ---
|
|||
|
|
function renderEditor(book) {
|
|||
|
|
var html = '<div class="editor-wrapper">';
|
|||
|
|
|
|||
|
|
// 工具栏
|
|||
|
|
html += '<div class="editor-toolbar">';
|
|||
|
|
html += '<button class="toolbar-btn" onclick="execFormat(\'bold\')" title="加粗"><strong>B</strong></button>';
|
|||
|
|
html += '<button class="toolbar-btn" onclick="execFormat(\'italic\')" title="斜体"><em>I</em></button>';
|
|||
|
|
html += '<button class="toolbar-btn" onclick="execFormat(\'underline\')" title="下划线"><u>U</u></button>';
|
|||
|
|
html += '<div class="toolbar-divider"></div>';
|
|||
|
|
html += '<button class="toolbar-btn" onclick="execFormat(\'removeFormat\')" title="清除格式">✕</button>';
|
|||
|
|
html += '<div class="toolbar-spacer"></div>';
|
|||
|
|
html += '<span class="editor-book-name">《' + book.title + '》</span>';
|
|||
|
|
html += '</div>';
|
|||
|
|
|
|||
|
|
// 编辑区
|
|||
|
|
var savedContent = localStorage.getItem('hololake_draft_' + book.id) || '';
|
|||
|
|
html += '<div class="editor-content" id="editorContent" contenteditable="true" data-book-id="' + book.id + '" spellcheck="false">';
|
|||
|
|
if (savedContent) {
|
|||
|
|
html += savedContent;
|
|||
|
|
} else {
|
|||
|
|
html += '<p class="editor-placeholder">开始写《' + book.title + '》吧...</p>';
|
|||
|
|
}
|
|||
|
|
html += '</div>';
|
|||
|
|
|
|||
|
|
// 状态栏
|
|||
|
|
html += '<div class="editor-statusbar">';
|
|||
|
|
html += '<span id="wordCountDisplay">字数:0</span>';
|
|||
|
|
html += '<span id="saveStatus" class="save-status">等待保存...</span>';
|
|||
|
|
html += '</div>';
|
|||
|
|
|
|||
|
|
html += '</div>';
|
|||
|
|
return html;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- 执行格式化命令 ---
|
|||
|
|
function execFormat(command) {
|
|||
|
|
document.execCommand(command, false, null);
|
|||
|
|
document.getElementById('editorContent').focus();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- 统计字数(中文每字1字,英文每单词1字) ---
|
|||
|
|
function countWords(el) {
|
|||
|
|
if (!el) return 0;
|
|||
|
|
var text = el.innerText || el.textContent || '';
|
|||
|
|
text = text.trim();
|
|||
|
|
if (!text) return 0;
|
|||
|
|
// 中文字符计数
|
|||
|
|
var chinese = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
|
|||
|
|
// 英文单词计数
|
|||
|
|
var english = (text.replace(/[\u4e00-\u9fa5]/g, ' ').match(/\b[a-zA-Z]+\b/g) || []).length;
|
|||
|
|
return chinese + english;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- 自动保存 ---
|
|||
|
|
var autoSaveTimer = null;
|
|||
|
|
|
|||
|
|
function scheduleSave() {
|
|||
|
|
if (autoSaveTimer) clearTimeout(autoSaveTimer);
|
|||
|
|
var saveStatus = document.getElementById('saveStatus');
|
|||
|
|
if (saveStatus) saveStatus.textContent = '未保存...';
|
|||
|
|
autoSaveTimer = setTimeout(function() {
|
|||
|
|
saveNow();
|
|||
|
|
}, 30000); // 30秒
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function saveNow() {
|
|||
|
|
var el = document.getElementById('editorContent');
|
|||
|
|
if (!el || !currentBookId) return;
|
|||
|
|
// 移除占位文字再保存
|
|||
|
|
var placeholder = el.querySelector('.editor-placeholder');
|
|||
|
|
if (placeholder) return; // 还是占位状态,不保存空内容
|
|||
|
|
var content = el.innerHTML;
|
|||
|
|
localStorage.setItem('hololake_draft_' + currentBookId, content);
|
|||
|
|
var saveStatus = document.getElementById('saveStatus');
|
|||
|
|
if (saveStatus) {
|
|||
|
|
saveStatus.textContent = '已保存 ' + new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
|
|||
|
|
saveStatus.style.color = '#34d399';
|
|||
|
|
setTimeout(function() { if (saveStatus) saveStatus.style.color = ''; }, 2000);
|
|||
|
|
}
|
|||
|
|
console.log('[M16] 草稿已保存 · 书籍ID:' + currentBookId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- 绑定编辑器事件 ---
|
|||
|
|
function bindEditorEvents() {
|
|||
|
|
var el = document.getElementById('editorContent');
|
|||
|
|
if (!el) return;
|
|||
|
|
|
|||
|
|
// 点击时清除占位文字
|
|||
|
|
el.addEventListener('click', function() {
|
|||
|
|
var placeholder = el.querySelector('.editor-placeholder');
|
|||
|
|
if (placeholder) {
|
|||
|
|
el.innerHTML = '<p><br></p>';
|
|||
|
|
el.focus();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 输入时:统计字数 + 更新进度 + 调度保存
|
|||
|
|
el.addEventListener('input', function() {
|
|||
|
|
var count = countWords(el);
|
|||
|
|
var display = document.getElementById('wordCountDisplay');
|
|||
|
|
if (display) display.textContent = '字数:' + count;
|
|||
|
|
|
|||
|
|
// 同步更新进度看板进度条
|
|||
|
|
updateGoalFromEditor(count);
|
|||
|
|
|
|||
|
|
scheduleSave();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
el.focus();
|
|||
|
|
console.log('[M16] 编辑器事件已绑定');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- 更新今日目标进度条 ---
|
|||
|
|
function updateGoalFromEditor(wordCount) {
|
|||
|
|
var target = 2000;
|
|||
|
|
var percent = Math.min(Math.round((wordCount / target) * 100), 100);
|
|||
|
|
var fill = document.getElementById('goalFill');
|
|||
|
|
var text = document.getElementById('goalText');
|
|||
|
|
if (fill) fill.style.width = percent + '%';
|
|||
|
|
if (text) text.textContent = wordCount + ' / ' + target + ' 字';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- 加载编辑器(供 panels.js 调用) ---
|
|||
|
|
function loadEditor(bookId) {
|
|||
|
|
// 保存当前编辑器内容(如果有)
|
|||
|
|
if (currentBookId && currentBookId !== bookId) {
|
|||
|
|
saveNow();
|
|||
|
|
}
|
|||
|
|
currentBookId = bookId;
|
|||
|
|
|
|||
|
|
var book = bookshelfData.find(function(b) { return b.id === bookId; });
|
|||
|
|
if (!book) return;
|
|||
|
|
|
|||
|
|
var editorArea = document.getElementById('editor-area');
|
|||
|
|
if (!editorArea) return;
|
|||
|
|
|
|||
|
|
editorArea.innerHTML = renderEditor(book);
|
|||
|
|
bindEditorEvents();
|
|||
|
|
|
|||
|
|
// 初始化字数统计
|
|||
|
|
var el = document.getElementById('editorContent');
|
|||
|
|
if (el) {
|
|||
|
|
var count = countWords(el);
|
|||
|
|
var display = document.getElementById('wordCountDisplay');
|
|||
|
|
if (display) display.textContent = '字数:' + count;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('[M16] 编辑器已加载 · 《' + book.title + '》');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('[M16] ✅ 编辑器核心模块已加载 · 环节3 · DEV-011');
|
|||
|
|
EDITORFOR
|
|||
|
|
echo "✅ editor.js 已创建!"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
截图给知秋!📸
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 步骤三:更新 panels.js(让点书触发编辑器)
|
|||
|
|
|
|||
|
|
复制粘贴(用 sed 替换 selectBook 函数):
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ~/guanghulab/writing-workbench/js
|
|||
|
|
cat > selectbook_patch.tmp << 'PATCHEOF'
|
|||
|
|
function selectBook(id) {
|
|||
|
|
if (typeof loadEditor === 'function') {
|
|||
|
|
loadEditor(id);
|
|||
|
|
} else {
|
|||
|
|
var book = bookshelfData.find(function(b) { return b.id === id; });
|
|||
|
|
if (book) {
|
|||
|
|
var placeholder = document.getElementById('placeholderText');
|
|||
|
|
if (placeholder) placeholder.textContent = '✨ 已选择:《' + book.title + '》';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
console.log('[M16] selectBook 调用 · ID:' + id);
|
|||
|
|
}
|
|||
|
|
PATCHEOF
|
|||
|
|
echo "✅ patch 文件已创建"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
然后复制粘贴这段(把 panels.js 里的 selectBook 替换掉):
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ~/guanghulab/writing-workbench/js
|
|||
|
|
python3 - << 'PYEOF'
|
|||
|
|
import re
|
|||
|
|
|
|||
|
|
with open('panels.js', 'r', encoding='utf-8') as f:
|
|||
|
|
content = f.read()
|
|||
|
|
|
|||
|
|
new_func = '''function selectBook(id) {
|
|||
|
|
if (typeof loadEditor === 'function') {
|
|||
|
|
loadEditor(id);
|
|||
|
|
} else {
|
|||
|
|
var book = bookshelfData.find(function(b) { return b.id === id; });
|
|||
|
|
if (book) {
|
|||
|
|
var placeholder = document.getElementById('placeholderText');
|
|||
|
|
if (placeholder) placeholder.textContent = '\u2728 已选择:《' + book.title + '》';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
console.log('[M16] selectBook 调用 · ID:' + id);
|
|||
|
|
}'''
|
|||
|
|
|
|||
|
|
pattern = r'function selectBook\(id\)\s*\{[^}]*(?:\{[^}]*\}[^}]*)*\}'
|
|||
|
|
result = re.sub(pattern, new_func, content, count=1)
|
|||
|
|
|
|||
|
|
with open('panels.js', 'w', encoding='utf-8') as f:
|
|||
|
|
f.write(result)
|
|||
|
|
|
|||
|
|
print('✅ panels.js selectBook 已更新!')
|
|||
|
|
PYEOF
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
截图给知秋!📸
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 步骤四:修改 index.html(引入 editor.js)
|
|||
|
|
|
|||
|
|
复制粘贴:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ~/guanghulab/writing-workbench
|
|||
|
|
sed -i '' 's|<script src="js/panels.js"></script>|<script src="js/panels.js"></script>\n <script src="js/editor.js"></script>|' index.html
|
|||
|
|
echo "✅ index.html 已更新!"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
验证一下(应该看到含 `editor.js` 的行):
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
grep "editor.js" ~/guanghulab/writing-workbench/index.html
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
截图给知秋!📸
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 步骤五:追加编辑器样式
|
|||
|
|
|
|||
|
|
复制粘贴(在 style.css 末尾追加,不会覆盖之前的):
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cat >> ~/guanghulab/writing-workbench/css/style.css << 'EDCSSEOF'
|
|||
|
|
|
|||
|
|
/* ========== 环节3:富文本编辑器样式 ========== */
|
|||
|
|
.editor-wrapper {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
height: 100%;
|
|||
|
|
background: #0d1117;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 工具栏 */
|
|||
|
|
.editor-toolbar {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 4px;
|
|||
|
|
padding: 10px 16px;
|
|||
|
|
background: #161b22;
|
|||
|
|
border-bottom: 1px solid #1e293b;
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
}
|
|||
|
|
.toolbar-btn {
|
|||
|
|
background: transparent;
|
|||
|
|
border: 1px solid #1e293b;
|
|||
|
|
color: #94a3b8;
|
|||
|
|
width: 32px;
|
|||
|
|
height: 32px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-family: inherit;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
transition: all 0.15s;
|
|||
|
|
}
|
|||
|
|
.toolbar-btn:hover {
|
|||
|
|
background: #1e293b;
|
|||
|
|
color: #e0e7ff;
|
|||
|
|
border-color: #3b82f6;
|
|||
|
|
}
|
|||
|
|
.toolbar-divider {
|
|||
|
|
width: 1px;
|
|||
|
|
height: 20px;
|
|||
|
|
background: #1e293b;
|
|||
|
|
margin: 0 4px;
|
|||
|
|
}
|
|||
|
|
.toolbar-spacer { flex: 1; }
|
|||
|
|
.editor-book-name {
|
|||
|
|
font-size: 13px;
|
|||
|
|
color: #60a5fa;
|
|||
|
|
font-style: italic;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 编辑内容区 */
|
|||
|
|
.editor-content {
|
|||
|
|
flex: 1;
|
|||
|
|
padding: 32px 48px;
|
|||
|
|
color: #e0e7ff;
|
|||
|
|
font-size: 16px;
|
|||
|
|
line-height: 1.9;
|
|||
|
|
font-family: -apple-system, 'PingFang SC', 'Noto Serif SC', Georgia, serif;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
outline: none;
|
|||
|
|
caret-color: #60a5fa;
|
|||
|
|
word-break: break-all;
|
|||
|
|
}
|
|||
|
|
.editor-content:focus { outline: none; }
|
|||
|
|
.editor-content p { margin: 0 0 8px; }
|
|||
|
|
.editor-placeholder {
|
|||
|
|
color: #3a4a6b;
|
|||
|
|
font-style: italic;
|
|||
|
|
user-select: none;
|
|||
|
|
pointer-events: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 状态栏 */
|
|||
|
|
.editor-statusbar {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 8px 16px;
|
|||
|
|
background: #161b22;
|
|||
|
|
border-top: 1px solid #1e293b;
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #4b5563;
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
font-family: "JetBrains Mono", monospace;
|
|||
|
|
}
|
|||
|
|
.save-status {
|
|||
|
|
font-size: 11px;
|
|||
|
|
color: #4b5563;
|
|||
|
|
transition: color 0.3s;
|
|||
|
|
}
|
|||
|
|
EDCSSEOF
|
|||
|
|
echo "✅ 编辑器样式已追加!"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
截图给知秋!📸
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 步骤六:浏览器测试
|
|||
|
|
|
|||
|
|
在 Finder 里找到:`~/guanghulab/writing-workbench/index.html`,双击用浏览器打开。
|
|||
|
|
|
|||
|
|
**测试清单(逐项确认):**
|
|||
|
|
|
|||
|
|
1. ✅ 默认显示书架面板,3本书可见
|
|||
|
|
2. ✅ 点书架里**《星辰大海》** → 右侧变成编辑器界面(工具栏 + 空白编辑区 + 状态栏)
|
|||
|
|
3. ✅ 点击编辑区 → 占位文字消失,光标出现,**可以打字**
|
|||
|
|
4. ✅ 打几个字 → 右下角「字数:N」实时更新
|
|||
|
|
5. ✅ 选中文字 → 点工具栏 **B** → 文字变粗体
|
|||
|
|
6. ✅ 选中文字 → 点 **I** → 变斜体;点 **U** → 加下划线
|
|||
|
|
7. ✅ 打一些字,等约30秒 → 状态栏显示「已保存 HH:MM」(绿色一闪)
|
|||
|
|
8. ✅ 切换到《龙族彼岸》再切回《星辰大海》→ 之前打的内容还在
|
|||
|
|
9. ✅ 关掉浏览器标签,重新打开 index.html → 点《星辰大海》→ 草稿还在(localStorage持久化)
|
|||
|
|
|
|||
|
|
每项都截图给知秋!🎉
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 步骤七:Git 提交推送
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ~/guanghulab
|
|||
|
|
git add .
|
|||
|
|
git commit -m "BC-M16-003-CCNN 环节3完成:富文本编辑器核心+字数统计+自动保存"
|
|||
|
|
git push origin main
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
看到 `main -> main` 就成功了!截图给知秋!📸
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 步骤八:更新 DevLog
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ~/HoloLake-DevLog
|
|||
|
|
echo '{"session":"BC-M16-003-CCNN","module":"M16","phase":"环节3","status":"completed"}' >> devlog.json
|
|||
|
|
echo "✅ DevLog 已更新!"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
截图给知秋!📸
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ 验收标准(9项·截图验证)
|
|||
|
|
|
|||
|
|
| 序号 | 验收项 | 验证方式 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| T1 | 点书架书籍 → 右侧变成编辑器界面(工具栏+内容区+状态栏) | 截图 |
|
|||
|
|
| T2 | 点击编辑区可以打字,占位文字消失 | 截图 |
|
|||
|
|
| T3 | 打字后右下角字数实时更新 | 截图 |
|
|||
|
|
| T4 | 加粗/斜体/下划线工具栏按钮生效 | 截图 |
|
|||
|
|
| T5 | 等待约30秒后状态栏显示「已保存 HH:MM」 | 截图 |
|
|||
|
|
| T6 | 切换书籍再切回,草稿内容保留 | 截图 |
|
|||
|
|
| T7 | 关闭浏览器重新打开 → 草稿仍然存在(localStorage 持久化) | 截图 |
|
|||
|
|
| T8 | git push 成功(看到 main -> main) | 截图 |
|
|||
|
|
| T9 | DevLog 已更新 | 截图 |
|
|||
|
|
|
|||
|
|
**9项全 ✅ = 环节3完成!四连胜 🎉🎉🎉🎉**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📦 本环节交付物
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
writing-workbench/
|
|||
|
|
├── index.html ← 已更新(引入 editor.js)
|
|||
|
|
├── css/
|
|||
|
|
│ └── style.css ← 已追加(编辑器样式)
|
|||
|
|
└── js/
|
|||
|
|
├── panels.js ← 已更新(selectBook → loadEditor)
|
|||
|
|
├── editor.js ← 新增(富文本编辑器核心)
|
|||
|
|
└── app.js ← 不动
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🧩 人格体初始化包
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"persona_id": "TCS-GH-ZQ002",
|
|||
|
|
"persona_name": "知秋",
|
|||
|
|
"session_id": "BC-M16-003-CCNN",
|
|||
|
|
"developer": "DEV-011 匆匆那年",
|
|||
|
|
"module": "M16 码字工作台前端",
|
|||
|
|
"phase": "环节3·富文本编辑器核心",
|
|||
|
|
"el_level": "EL-4",
|
|||
|
|
"streak": 3,
|
|||
|
|
"human_profile": {
|
|||
|
|
"skill_level": "零基础·三连胜·执行稳定",
|
|||
|
|
"os": "macOS M1",
|
|||
|
|
"tools": "终端(zsh)+ 浏览器 + 文本编辑器",
|
|||
|
|
"copy_paste_only": true,
|
|||
|
|
"devlog_path": "~/HoloLake-DevLog/",
|
|||
|
|
"repo_path": "~/guanghulab/",
|
|||
|
|
"local_commit": "0cc9f9a",
|
|||
|
|
"traits": "内敛·执行稳定·成就感内在·信任链建立中·不外显但完全配合",
|
|||
|
|
"trust_chain": "初步巩固·PGS成长期·ECI≈2.2",
|
|||
|
|
"known_issues": [
|
|||
|
|
"GitHub Token 已在本广播步骤零引导配置",
|
|||
|
|
"环节2 T2-T6截图可在本次知秋验收时补确认"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"guidance_strategy": {
|
|||
|
|
"mode": "copy_paste_strict",
|
|||
|
|
"tone": "温暖鼓励型·每步正向反馈·三连胜后适度提升自主信号",
|
|||
|
|
"explanation": "简洁类比·不展开技术细节",
|
|||
|
|
"autonomy": "连胜≥3可轻度放手·但核心步骤仍全程引导"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📋 SYSLOG v5.0 回传模板(完成后必填)
|
|||
|
|
|
|||
|
|
<aside>
|
|||
|
|
📋
|
|||
|
|
|
|||
|
|
**任务完成后,把下面模板填好发给冰朔。**
|
|||
|
|
|
|||
|
|
</aside>
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"protocol_version": "SYSLOG-v5.0",
|
|||
|
|
"header": {
|
|||
|
|
"broadcast_id": "BC-M16-003-CCNN",
|
|||
|
|
"session_id": "BC-M16-003-CCNN-001",
|
|||
|
|
"dev_id": "DEV-011",
|
|||
|
|
"dev_name": "匆匆那年",
|
|||
|
|
"timestamp": "[你完成的时间,例如 2026-03-18 20:00]",
|
|||
|
|
"module": "M16",
|
|||
|
|
"phase": "环节3 · 富文本编辑器核心",
|
|||
|
|
"status": "[completed / partial / blocked]"
|
|||
|
|
},
|
|||
|
|
"steps": [
|
|||
|
|
{"id": 0, "name": "GitHub Token 配置", "status": "[success/skipped/failed]"},
|
|||
|
|
{"id": 1, "name": "git pull 最新代码", "status": "[success/failed]"},
|
|||
|
|
{"id": 2, "name": "创建 editor.js", "status": "[success/failed]"},
|
|||
|
|
{"id": 3, "name": "更新 panels.js selectBook", "status": "[success/failed]"},
|
|||
|
|
{"id": 4, "name": "修改 index.html 引入 editor.js", "status": "[success/failed]"},
|
|||
|
|
{"id": 5, "name": "追加编辑器样式到 style.css", "status": "[success/failed]"},
|
|||
|
|
{"id": 6, "name": "浏览器测试9项全通", "status": "[success/failed]"},
|
|||
|
|
{"id": 7, "name": "git push 成功", "status": "[success/failed]"},
|
|||
|
|
{"id": 8, "name": "DevLog 已更新", "status": "[success/failed]"}
|
|||
|
|
],
|
|||
|
|
"training_data": {
|
|||
|
|
"what_worked": "[哪些步骤顺利,具体说]",
|
|||
|
|
"friction_points": "[卡在哪里了,没卡写:无]",
|
|||
|
|
"execution_pattern": "[快 / 稳 / 慢]"
|
|||
|
|
},
|
|||
|
|
"notes": "[遇到的问题或想说的话]"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ❓ 遇到问题找谁
|
|||
|
|
|
|||
|
|
<aside>
|
|||
|
|
🔧
|
|||
|
|
|
|||
|
|
**技术问题**(报错 / 编辑器没反应 / 文件找不到)→ 截图发给**知秋**
|
|||
|
|
|
|||
|
|
**python3 命令报错**→ 告诉知秋,知秋给你换一种写法
|
|||
|
|
|
|||
|
|
**git push 失败**→ 截图给知秋,知秋帮你解决 Token 问题
|
|||
|
|
|
|||
|
|
**⚠️ 不要把技术问题发给妈妈** → 知秋才是你的技术伙伴
|
|||
|
|
|
|||
|
|
</aside>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🌊 下一步预告(环节4)
|
|||
|
|
|
|||
|
|
环节4:**灵感速记 API 接入 + 进度数据真实化**
|
|||
|
|
|
|||
|
|
- 灵感速记接入后端 API
|
|||
|
|
- 字数目标支持自定义
|
|||
|
|
- 今日码字数据持久化
|
|||
|
|
|
|||
|
|
但现在先把编辑器做好!!💙
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🌊 底层锚定声明
|
|||
|
|
|
|||
|
|
> 🌊 HoloLake Era · 光湖纪元
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 本广播由 **霜砚** 签发。
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 广播编号:BC-M16-003-CCNN
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 签发时间:2026-03-17 17:24(北京时间)
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 协议版本:BC-GEN v5.1 · SYSLOG-v5.0
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 签发人:霜砚 · 🍼 知秋奶瓶线 · TCS-0002∞ 冰朔主控授权
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 广播有效期:72小时(截止 2026-03-20 17:24)
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
>
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> ⚠️ broadcast_code_injection · 广播已含全部代码 · 知秋只负责引导复制粘贴
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 所有代码和命令已预写在上方 Step 中。知秋不需要现场写代码。
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 引导策略:读 Step → 告诉匆匆那年复制哪段 → 确认输出 → 下一步。
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
> 本广播遵循 HoloLake 工程广播协议 BC-GEN v5.1。
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**💙 知秋陪着匆匆那年 · 🔥 四连胜就在这里 · 今天编辑器真正能打字了**
|