--- 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