Luồng hoạt động hệ thống (System Flows)

Mô tả luồng tương tác tuần tự, luồng dịch chuyển dữ liệu qua các khối thành phần khi chạy ứng dụng.

1. Bản đồ di chuyển giữa các Gói (Package Transition Map)

Bản đồ thể hiện cách dữ liệu đi từ tệp vật lý trên đĩa cứng, được xử lý thông qua các thư viện trung gian ở backend (Node.js), chuyển tiếp qua cầu nối IPC (postMessage) và hiển thị trực quan trên Webview DOM (trang ngang từ trái qua phải):

flowchart LR
    %% Files trên đĩa cứng
    subgraph Disk["Hệ thống tệp (Disk Storage)"]
        docx_file["file.docx (Binary ZIP)"]
        save_docx_file["file.docx (Updated Binary)"]
    end

    %% Thư viện lõi Node.js
    subgraph HostPackages["Gói xử lý Extension Host (Node.js)"]
        vscode_api["vscode API (Workspace & FS)"]
        mammoth_pkg["mammoth.js (Parser)"]
        docx_pkg["docx (Serializer)"]
        node_fs["fs / child_process (Export PDF)"]
    end

    %% Giao tiếp IPC
    subgraph IPCBridge["Cầu nối Giao tiếp IPC (VS Code Webview Bridge)"]
        post_msg["postMessage({ type: 'load', payload })"]
        receive_msg["onDidReceiveMessage({ type: 'update' | 'save' })"]
    end

    %% Môi trường Trình duyệt Webview
    subgraph WebviewPackages["Gói Webview Frontend (React 19 & Tiptap)"]
        tiptap_core["@tiptap/core & @tiptap/react (Editor State)"]
        prosemirror_pm["@tiptap/pm (ProseMirror Engine)"]
        webview_dom["React Virtual DOM / HTML Rendering"]
    end

    %% Mũi tên chỉ luồng hoạt động
    docx_file -->|1. Đọc Buffer| vscode_api
    vscode_api -->|2. Phân tách XML| mammoth_pkg
    mammoth_pkg -->|3. Sinh HTML + Metadata| post_msg
    post_msg -->|4. Nhận Payload| tiptap_core
    tiptap_core -->|5. Tạo ProseMirror State| prosemirror_pm
    prosemirror_pm -->|6. Render Giao diện Chỉnh sửa| webview_dom

    %% Luồng Lưu (Save Flow)
    webview_dom -->|A. Phát tín hiệu Thay đổi / Lưu| tiptap_core
    tiptap_core -->|B. Gửi HTML cập nhật| receive_msg
    receive_msg -->|C. Gọi Serializer| docx_pkg
    docx_pkg -->|D. Sinh Buffer nhị phân mới| vscode_api
    vscode_api -->|E. Ghi đè tệp tin| save_docx_file
        
🔑 Chú thích luồng dịch chuyển dữ liệu:

🔵 Luồng Đọc / Hiển thị (Bước số 1 ➔ 6)

  • 1. Đọc Buffer: VS Code API đọc luồng nhị phân ZIP của tệp .docx từ đĩa.
  • 2. Phân tách XML: Gọi mammoth.js tách và chuyển mã XML sang HTML thô.
  • 3. Sinh HTML: Đóng gói HTML, PageSettings, Comments gửi qua postMessage.
  • 4. Nhận Payload: React Webview giải mã tin nhắn nhận được.
  • 5. Tạo State: Tiptap phân tích cấu trúc HTML để dựng ProseMirror EditorState.
  • 6. Render DOM: Dựng giao diện chỉnh sửa WYSIWYG trực quan lên trình duyệt Webview.

🟢 Luồng Ghi / Lưu (Bước chữ A ➔ E)

  • A. Phát tín hiệu: Người dùng thao tác chỉnh sửa hoặc bấm Ctrl+S trên editor.
  • B. Gửi HTML: Giao diện React gửi gói HTML và lề trang hiện tại qua IPC về Host.
  • C. Gọi Serializer: Backend nhận sự kiện và kích hoạt DocxSerializer.ts.
  • D. Sinh Buffer: Thư viện docx biên dịch DOM HTML thành định dạng XML Word.
  • E. Ghi đè: Hệ thống tệp VS Code ghi đè mảng byte nhị phân mới xuống đĩa cứng.

2. Biểu đồ tương tác tuần tự (Sequence Diagrams)

2.1. Quy trình mở và khởi chạy tệp Word (.docx)

Khi người dùng mở một file Word, quy trình phân tách nhị phân ở backend và đồng bộ dữ liệu sang Tiptap diễn ra tuần tự:

sequenceDiagram
    autonumber
    actor User as Người dùng
    participant VSCode as VS Code Host
    participant Provider as LeandixEditorProvider
    participant DocMgr as DocumentManager
    participant Parser as DocxParser
    participant Webview as Webview Panel (HTML)
    participant ReactApp as React App (App.tsx)
    participant Tiptap as TiptapEditor

    User->>VSCode: Click mở file.docx
    VSCode->>Provider: Kích hoạt openCustomDocument() & resolveCustomEditor()
    Provider->>Webview: Nạp khung HTML tĩnh & script/style (main.js, main.css)
    Webview->>ReactApp: Khởi chạy Component App
    ReactApp->>Provider: Gửi tin nhắn IPC: { type: 'ready' }
    
    note over Provider, DocMgr: Bắt đầu đọc dữ liệu
    Provider->>DocMgr: Gọi readFile(uri)
    DocMgr-->>Provider: Trả về Buffer tệp nhị phân
    Provider->>Parser: Gọi parseDocx(buffer)
    Parser-->>Provider: Trả về { bodyHtml, headerHtml, footerHtml, comments, pageSettings }
    
    Provider->>Webview: postMessage({ type: 'load', payload })
    Webview->>ReactApp: Nhận sự kiện 'load' qua hook useVsCodeBridge
    ReactApp->>Tiptap: Đặt nội dung editor qua editor.commands.setContent()
    ReactApp->>ReactApp: Cập nhật lề trang (pageSettings) & bình luận (comments) vào React state
    Tiptap-->>User: Hiển thị giao diện soạn thảo WYSIWYG
        
🔑 Giải thích quy trình mở tệp (Các bước 1 ➔ 12):

Luồng chạy bắt đầu khi người dùng click vào tệp (1-2), hệ thống nạp các tài nguyên giao diện tĩnh trước (3-4), sau đó khi Webview React đã sẵn sàng thì phát tin hiệu ready (5) để kích hoạt tiến trình đọc nhị phân (6-7), giải mã XML sang HTML (8-9), và cuối cùng truyền gói dữ liệu đầy đủ qua IPC (10-11) để Tiptap dựng nội dung trực quan (12).

2.2. Quy trình lưu và ghi đè tệp Word

Quá trình gửi trả dữ liệu HTML biên tập về backend để biên dịch sang Word nhị phân khi lưu tệp:

sequenceDiagram
    autonumber
    actor User as Người dùng
    participant Tiptap as TiptapEditor
    participant ReactApp as React App (App.tsx)
    participant Webview as Webview Panel
    participant Provider as LeandixEditorProvider
    participant Serializer as DocxSerializer
    participant DocMgr as DocumentManager
    participant VSCode as VS Code Host / Disk

    User->>Tiptap: Chỉnh sửa văn bản / ấn Ctrl+S
    Tiptap->>ReactApp: Kích hoạt hàm onUpdate / handleEditorUpdate()
    ReactApp->>Webview: Gửi tin nhắn IPC: { type: 'update' | 'save', payload: { html, comments, pageSettings } }
    Webview->>Provider: Nhận tin nhắn trong handleWebviewMessage()
    Provider->>DocMgr: setDirty(uri, html, extras) ➔ Đánh dấu tệp đã chỉnh sửa (Dấu chấm tròn trên Tab)
    
    rect rgb(240, 240, 240)
        note over Provider, VSCode: Lưu tệp nhị phân
        VSCode->>Provider: Gọi saveCustomDocument() khi lưu tệp được xác nhận
        Provider->>Serializer: Gọi serializeToDocx(html, options)
        Serializer->>Serializer: Tải các ảnh URL nội bộ & nhúng dạng Base64
        Serializer->>Serializer: Chuyển đổi cây HTML DOM thành cấu trúc Word XML
        Serializer-->>Provider: Trả về Uint8Array (mảng byte file Word mới)
        Provider->>VSCode: Ghi tệp qua vscode.workspace.fs.writeFile()
        Provider->>DocMgr: clearDirty(uri) (Xóa trạng thái dirty)
    end
    
    Provider->>Webview: postMessage({ type: 'saved' })
    Webview->>ReactApp: Xóa trạng thái isDirty trên thanh StatusBar
    ReactApp-->>User: Hiển thị trạng thái "Đã lưu thành công"
        
🔑 Giải thích quy trình lưu tệp (Các bước 1 ➔ 14):

Khi có thay đổi, Frontend liên tục đồng bộ HTML về backend qua bản tin update (2-4) để DocumentManager đánh dấu trạng thái dirty (5) giúp VS Code biết tài liệu có chỉnh sửa. Khi có sự kiện Save thực tế (như Ctrl+S), VS Code kích hoạt luồng serialize nhị phân (6-11), ghi đè file lên ổ đĩa (12), xóa trạng thái dirty (13) và gửi tín hiệu phản hồi saved (14) về lại cho người dùng.

3. Quy trình Xuất tệp PDF (PDF Headless Export)

Luồng thực hiện chuyển mã cấu trúc biên tập sang PDF bằng phương thức headless printing (vẽ theo chiều ngang):

flowchart LR
    %% Các bước xuất PDF
    step1["1. Người dùng kích hoạt Export PDF"]
    step2["2. Extension gom HTML hiện tại + CSS từ webview stylesheet"]
    step3["3. Tạo tệp HTML tạm thời (.leandix-temp/document.html) nằm trong Workspace"]
    step4["4. Tìm kiếm đường dẫn cài đặt của Chrome hoặc Edge trên máy tính"]
    step5["5. Gọi lệnh CLI headless in trang:
chrome --headless --print-to-pdf=output.pdf document.html"] step6["6. Xóa thư mục tạm thời .leandix-temp và thông báo hoàn thành"] %% Luồng thực hiện step1 --> step2 step2 --> step3 step3 --> step4 step4 --> step5 step5 --> step6
🔑 Chú thích luồng Xuất PDF:

Do mở rộng không chạy trong môi trường in ấn, tính năng in PDF được thực hiện bằng cách xuất cấu trúc DOM HTML thành tệp tạm thời trong thư mục .leandix-temp/ (2-3), sau đó dùng API CLI gọi trình duyệt Edge hoặc Chrome có sẵn trên máy khách dưới cờ --headless (không mở giao diện) để in tệp HTML tạm thời này ra file PDF đích (4-5) trước khi tự động dọn dẹp bộ nhớ đệm (6).

4. Luồng xử lý đồng bộ thước kẻ & căn lề (Ruler Synchronization)

  1. mm-to-pixel: Quy đổi kích thước trang và lề trang (PageSettings - mm) sang Pixel để dựng CSS chính xác trên trình duyệt bằng công thức: $\text{Pixels} = \text{mm} \times (96 / 25.4)$.
  2. Tự động vẽ thước (Ruler redraw): Khi thay đổi kích thước lề trái/phải, component Ruler.tsx vẽ lại các vạch centimet động dựa trên vùng khả dụng (Content Width).
  3. Gửi cập nhật: Mọi tương tác thay đổi lề trang trên thước kẻ đều được React cập nhật vào state pageSettings và tự động gửi thông báo IPC update về backend.

5. Sơ đồ phân vùng thành phần và Luồng hoạt động chi tiết (Tiptap vs Custom Extensions Flow)

Biểu đồ dưới đây làm rõ ranh giới giữa phần mềm Tiptap tiêu chuẩn, các plugin/extension tùy chỉnh (Custom Extensions) tự phát triển và luồng truyền dẫn tương tác từ giao diện người dùng đến hệ thống lưu trữ ở backend:

graph LR
    %% Action khởi đầu
    user_action["Tương tác Người dùng
(Kéo ảnh/Gõ / /Gõ Tab/Bôi đen)"] %% Nhóm 1: Tiptap Tiêu chuẩn subgraph TiptapStd["Tiptap Core & Standard Extensions (Tiêu chuẩn)"] tiptap_core["Tiptap Core Engine
(EditorState, Transactions)"] std_kit["StarterKit
(Bold, Italic, History)"] std_table["TableKit
(Base Table State)"] std_link["Link / Highlight / Align
(Inline Formatting)"] end %% Nhóm 2: Custom Extensions subgraph CustomExts["Custom Extensions & NodeViews (Tùy biến tự viết)"] cust_img["CustomImage Extension
(data-wrap-mode, Base64 Pasteur)"] cust_list["CustomBullet/OrderedList
(Depth bullet styling)"] cust_table["CustomTable & RowResize
(Drag column/row width)"] cust_indent["Indent / HangingIndent
(Snapping to Ruler Margin)"] cust_comment["CommentMark Extension
(Map Text Range to Comment ID)"] cust_shortcuts["KeyboardShortcuts
(Ctrl+Shift+X, Align keys, Tab)"] slash_cmd["SlashCommands Extension
(/ popup list menu)"] end %% Nhóm 3: React UI Components subgraph ReactUI["React UI Components (Giao diện hiển thị)"] editor_comp["TiptapEditor.tsx
(Mount editor instance)"] toolbar_comp["Toolbar.tsx & TableToolbar.tsx
(Trigger Commands)"] ruler_comp["Ruler.tsx & Markers
(Show & Edit margins/indents)"] comment_panel["CommentsPanel.tsx
(Threaded comment list)"] end %% Nhóm 4: Backend subgraph Backend["Extension Host Backend (Lưu trữ)"] bridge["useVsCodeBridge.ts
(IPC Bridge)"] host_provider["LeandixEditorProvider.ts
(Lifecycle & Save)"] docx_ser["DocxSerializer.ts
(HTML -> XML DOCX)"] end %% Mối quan hệ luồng hoạt động user_action -->|1. Phát sự kiện DOM| editor_comp editor_comp -->|2. Phân phối Transaction| tiptap_core %% Phân luồng xử lý tiptap_core -->|Định dạng cơ bản| std_kit tiptap_core -->|Kéo dãn/Bọc chữ ảnh| cust_img tiptap_core -->|Tác vụ nâng cao| CustomExts %% Phản hồi giao diện cust_img -->|3a. Cập nhật CSS/DOM NodeView| editor_comp cust_indent -->|3b. Gửi tọa độ indents| ruler_comp cust_comment -->|3c. Gửi tọa độ Text Range| comment_panel toolbar_comp -->|Command Trigger| tiptap_core %% Đồng bộ lưu trữ editor_comp -->|4. Trigger onChange| bridge bridge -->|5. Gửi IPC: update| host_provider host_provider -->|6. Khi lưu| docx_ser
🔑 Chú thích ranh giới và luồng đi (1 ➔ 6):

💡 Phân bổ chức năng xử lý

  • Tiptap Tiêu chuẩn: Xử lý toàn bộ thao tác soạn thảo chữ cơ bản, in đậm, in nghiêng, chèn link và bảng mặc định.
  • Custom Extensions (Tự viết): Chứa các logic nghiệp vụ nâng cao đòi hỏi tính đồng bộ chặt chẽ với Word (.docx) như wrapMode float của ảnh, kéo thả co dãn cột bảng, thụt dòng thước kẻ, gõ phím tắt nhanh và xử lý bình luận.

🔄 Quy trình tương tác đến lưu trữ

Người dùng tác động vào giao diện ➔ Tiptap Core nhận diện ➔ định tuyến sự kiện sang Custom Extensions nếu là tác vụ nâng cao ➔ thay đổi trạng thái NodeView (hiển thị giao diện) ➔ phát tín hiệu đồng bộ qua useVsCodeBridge về backend ➔ LeandixEditorProvider thực thi lưu trữ file Word.