---
title: HackMD - Markdown 協作筆記 @ 2016 MOPCON
lang: zh-tw
disqus: hackmd
slideOptions:
width: 1200
---
<style>
.reveal, .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { font-family: -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Helvetica, Arial, PingFangTC-Light, "Microsoft JhengHei", "微軟正黑", sans-serif, "Apple Color Emoji" }
</style>
# <i class="fa fa-file-text"></i> HackMD
## Markdown 協作筆記
**jackycute @ 2016 MOPCON**
---
<img src="https://i.imgur.com/PTbB46S.jpg" style="width:300px;height:300px;border-radius:50%"/>
# 吳承翰 Max Wu
#### 北科大 資訊工程所
### 我喜歡好的產品,也開發好的產品;<br>我寫的不是程式,是理想。
---
# <i class="fa fa-file-text"></i> HackMD
## Realtime collaborative markdown notes on all platforms
----
## 工程師看到的時候想:
- Realtime;那就是很快就可以看到結果的意思
- Collaborative;就是可以跟別人一起同時工作
- Markdown;就是 GitHub 還有 堆疊溢出 在用的語法嘛
- Notes;喔 Evernote 還是 Quip 都用過啦
- Platforms;阿不就跨平台好棒棒
----
## 一般人看到的時候想:
- Realtime;即時?阿是有多即時?FB 那樣嗎?
- Collaborative;協作?Google Doc 我聽過
- Markdown;你給我解釋解釋,解釋解釋
- Notes;我們公司都用 Word 作筆記傳 FTP
- Platforms;不懂你的意思,可以說清楚一點嗎?
----
# Hack + MD
----
# Hack
## 用聰明的方法來完成事情<!-- .element: class="fragment" data-fragment-index="1" -->
----
# Markdown
# 輕量的視覺化標記語言<!-- .element: class="fragment" data-fragment-index="1" -->
# 「蛤?」<!-- .element: class="fragment" data-fragment-index="2" -->
----
# 文字就可以排版的語法
感謝 @clkao 貢獻此麻瓜咒語
---
# <i class="fa fa-file-text"></i> HackMD
- 靈感來自 Hackpad
- 更注重速度與靈活度
- 跨平台的筆記服務
----
# <i class="fa fa-file-text"></i> 用途
- 製作筆記
- 撰寫文件
- 快速簡報
----
# <i class="fa fa-file-text"></i> 功能
- 自動上傳圖片
- 支援嵌入影片
- 支援數學公式
- 支援各式圖表
- 自動修訂紀錄
- 自動作者標記
----
# <i class="fa fa-file-text"></i> 採用
- PyCon TW 2016
- g0v
- CTF write-ups
- Debater
- 成大 進階電腦系統理論與實作 (Fall 2016)
----
# <i class="fa fa-file-text"></i> 現況
- 2015 年 3 月開始上線服務
- 累計超過 5.2 萬份筆記
- 每月超過 3.4 萬的活躍使用者
- 會員超過 7,500 名
- 使用者來自 台灣、日本、美國、歐洲、南美洲
- 支援超過 15 種介面語言
----
# <i class="fa fa-github"></i> 開源
- 被星星超過 1,000 次
- 擁有超過 24 名貢獻者
- 被 Fork 超過 130 次
----
# <i class="fa fa-file-text"></i> 架構
- 前端使用 jQuery
- 後端使用 Node.js
- 資料庫使用 PostgreSQL
---
## 一個人怎麼實踐理想?
# 「組合肉」<!-- .element: class="fragment" data-fragment-index="1" -->
----
# 起源
## 資訊安全 期末專案
<!-- .element: class="fragment" data-fragment-index="1" -->
### 好奇即時協作的原理與其中的安全問題<!-- .element: class="fragment" data-fragment-index="2" -->
### 因為我很懶所以愛上優雅的 Markdown<!-- .element: class="fragment" data-fragment-index="3" -->
----
# 做法:
1. 上網餵狗搜尋 Collaborate Editor
2. 找最容易看得懂的範例開始改起
3. 找可以用的 Open Source 套件加料
4. 研究原始碼填補接縫
5. 測試功能正常,美化外皮
----
## 結論:兩天完成的夜市牛排
- 可以多使用者同時協作文字
- 實作對稱式加解密 (AES)
- 其實就是實踐 MVP (最小可行產品) 的過程
----
# 劇終
- 這個專案拿到 99 分
- 做完就去放寒假了 XD
- 沒有人愛你 :heart:
---
## Re: 從零開始的協作筆記
- 既然用了 Markdown 就要渲染成 HTML 啊!
- 既然都是網頁了不如支援一下 RWD (響應式網頁設計)
- 先來場大亂鬥看看會發生甚麼事情?!
---
## 選擇 Markdown renderer
- chjj / marked <i class="fa fa-star"></i> 10766 <i class="fa fa-exclamation-circle"></i> 298
- jonschlinkert / remarkable <i class="fa fa-star"></i> 2881 <i class="fa fa-exclamation-circle"></i> 53
- markdown-it / markdown-it <i class="fa fa-star"></i> 2656 <i class="fa fa-exclamation-circle"></i> 2
- wooorm / remark <i class="fa fa-star"></i> 715 <i class="fa fa-exclamation-circle"></i> 5
----
# chjj / marked
- 輕量、效能最好
- 沒有遵循任何標準
- 自訂語法較困難
----
## markdown-it / markdown-it
- 效能佳
- 遵循 CommonMark
- 容易自訂語法
- 外掛多
~~jonschlinkert / remarkable~~
基本上已棄用,markdown-it 的前身
<!-- .element: class="fragment" data-fragment-index="1" -->
----
# wooorm / remark
- 效能好
- 遵循 CommonMark
- 有制定一套 AST (虛擬結構樹)
- 外掛多
- 明日之星 :star:
----
# CommonMark
因為 Markdown 是開放格式
所以各自制定的語法有非常多種
目前 CommonMark 為大多數人遵循的標準
---
# 跨平台介面設計
- 使用 Bootstrap,內建 RWD
- 無論整合什麼都要看起來像 Bootstrap
- 盡量讓所有功能可用,不能用的直接藏起來
----
## 跨平台也能保持良好的使用者體驗
### 了解瀏覽器的設計,盡量保留原生功能<!-- .element: class="fragment" data-fragment-index="1" -->
- body scrolling 的時候工具列會縮小
- 輸入的時候虛擬鍵盤會彈出來
- #hash 是拿來定位 id anchor 的 :anger:
<!-- .element: class="fragment" data-fragment-index="2" -->
----
## 跨平台介面開發與除錯
- 善用響應式設計模式預覽 (Safari)
- 善用 Remote 除錯 (Chrome, Safari)
- 實機測試最準確
- 為不同螢幕裝置做更詳細的最佳化
----
## 使用者總能挑戰裝置的極限!
- 這麼小的螢幕上還是要用 IDE!
- 超小的元素還是被點到了!
- 行動網路很慢,但是硬要用! :anguished:
---
# 社群推廣大亂鬥
- 分享到 FB 的幾個較大的資訊相關社團
- 自己分享到 Hacker News
- 造成空前的流量,很多人一起測試輸入
----
# 大爆炸 :bomb:
- 有人嘗試輸入 40 萬字,造成 CPU 負載過重
- 有人嘗試輸入 不安全的語法
- 多人一起輸入會搶字
----
# 適當且無感的限制
- 限制最大輸入字數,超過會提醒
- 自動過濾所有渲染出來的 HTML
---
# 如何協作同步?
- 輸入拼字也需要同步
- 網路延遲造成同步錯亂
----
# 拼字問題
- 輸入需要拼字的還有 日文、韓文 等
- 拼字的時候瀏覽器有事件 (compositionStart 等)
- 但是會因為裝置、瀏覽器而實作有所不同
- 總之不要忽略拼字,ㄅㄆㄇㄈ也需要同步
----
### Operational Transformation 操作轉換
### 無法避免因為網路延遲所導致的資料不同步<!-- .element: class="fragment" data-fragment-index="1" -->
多人協作的時候需要將輸入的操作結構化
然後按照順序轉換成相對應的操作並同步
<!-- .element: class="fragment" data-fragment-index="2" -->
----
# 共有三種操作
- Retain 多少字元不更動
- Insert 插入字元
- Delete 刪除字元
----
# 如何轉換?
## 以「PPAP」為例<!-- .element: class="fragment" data-fragment-index="1" -->
----
# PP→PPAP
```mermaid
sequenceDiagram
Client A->>Server: O1: Insert[1, 'P', 1]
Client B->>Server: O2: Insert[1, 'A', 1]
Server-->>Client B: O1': Insert[1, 'P', 2]
Server-->>Client A: O2': Insert[2, 'A', 1]
```
----
# PAAP→PPAP
```mermaid
sequenceDiagram
Client A->>Server: O1: Delete[1, 'A', 2]
Client B->>Server: O2: Insert[1, 'P', 3]
Server-->>Client B: O1': Delete[2, 'A', 2]
Server-->>Client A: O2': Insert[1, 'P', 2]
```
---
# 資料儲存架構
- 將具有即時性的資料暫存在記憶體
- 變更資料的同時標記此筆資料變更
- 使用非同步 Workers 掃描變更資料並存回資料庫
----
### Worker 的工作流程
<div style="width: 80vmin; display: inline-block;">
```flow
st=>start: 資料存取
e=>end: 刪除暫存
op1=>operation: 暫存資料至記憶體
cond1=>condition: 資料是否被變更?
cond2=>condition: 資料是否逾時未變更?
op2=>operation: 儲存變更資料回資料庫
st->op1->cond1
cond1(yes)->op2->e
cond1(no)->cond2->op1
cond2(yes)->e
cond2(no)->cond1->sub1
```
</div>
---
# 如何記錄修訂版本?
- google-diff-match-patch
- 只記錄 patch 減少重複內容
----
## google-diff-match-patch
- 基於 Myer's diff algorithm
- 加上預處理 (加速)
- 加上後處理 (減少多餘的結構)
----
# diff 範例
- old: 假的業障重, new: 假的暫時的
- diff 得到:假的~~業障重~~==暫時的==
- 轉成 GNU unified format patch
```=
@@ -1,5 +1,5 @@
假的
-業障重
+暫時的
```
----
## 只儲存 patch 減少資料量
- 只有最新與最舊的版本會完整儲存
- 當拿取中間版本時,由最靠近的完整版本 patch 回來
- 從舊版本計算出較新版本 → patch
- 從新版本計算到較舊版本 → inverse patch
---
# 如何整合套件
- 要整合的無縫
- 要能發揮它的力量
- 了解它、修正它、Fork 它
- 不吝給予回饋 (開 Issue、發 PR)
----
# 目前已整合的重要套件
- CodeMirror
- socket.io
- OT
- textcomplete
- bootstrap
- markdown-it
- reveal.js
- google-diff-match-patch
----
# 為何不重造輪子
- 因為沒那麼多資源
- 因為沒有那麼專業
----
# 為何重造輪子
- 因為可以完全自訂
- 因為可以知道細節
---
# 設計與核心思想
# 「懶」<!-- .element: class="fragment" data-fragment-index="1" -->
## 懶得優雅<!-- .element: class="fragment" data-fragment-index="2" -->
## 懶得簡潔<!-- .element: class="fragment" data-fragment-index="3" -->
----
# 實踐方法
- 只出現必要的元件
- 注重微小的細節
- 好的體驗是由無數個細節構成
----
# 自己吃吃看狗糧
- 最好的電影是連導演都感動
- 最棒的服務是連自己都驚嘆
---
# 社群是你最好的導師
- 自己的痛點可能也是別人的痛點
- 用 open source 實踐 MVP
----
## 別想單打獨鬥解決所有困難
- 勇者,不要跟他拼拳,嘗試切他中路! :facepunch:
- 嘗試創造不存在那些痛點的世界 (但是有 tradeoffs)
- 想法 → 課程專案 → Issue DD → Community DD
----
# 樂於與社群互動
- 關心使用者,提供管道並傾聽他們的聲音 :ear:
- 與敬佩的對手聯誼,市場夠大不要小氣
- 研究 → 設計 → 實作 → 驗證 → 回饋
----
## 最好是免錢又大碗又好吃
- 這是不可能的 :cry:
- 必須在設計、開發、營運之間嘗試平衡
- 將所有功能都集合的不一定是好產品
---
# 感謝貢獻與推廣
@clkao @neil @BlueT @ETblue @EvanWu @yukai @PeterDaveHello @ttcat @BobbyTung @xnum @au @g0v and more...
---
# 感謝贊助
## 已收到超過 250 美元的捐贈!
---
## <i class="fa fa-facebook-square"></i> 搜尋並加入
# Markdown 台灣
---
# Q&A
---
# <i class="fa fa-github"></i> ⨯ <i class="fa fa-file-text"></i>
## HackMD<br>已列入<br>GitHub Integrations List
---
# 謝謝大家!
這份簡報使用 HackMD 製作
https://hackmd.io/p/Bk9X2eJT