Open8

docxとzipとxmlの関係をmarkdownに持ってきたい

podhmopodhmo
podhmopodhmo

何も考えずに履歴保持するオブジェクトストレージ(e.g. s3)にtextpackを保存してくという富豪的なアプローチもありはする

podhmopodhmo
target_reader: Future me
objective: Markdownを核とした、バージョン管理機能内蔵の再編集可能なドキュメント形式の仕様策定プロセスを記録する。

再編集可能なMarkdownバンドル形式の設計思想

🤔 問題定義 - docxの代替としてのMarkdown

手順書の作成ツールとして、Wordは高機能だが環境設定が煩雑で、Markdownは手軽だが画像の同梱や非技術者への配布に課題がある。特に、編集不可なPDFとして共有すると修正依頼のサイクルが非効率になる。

この問題の解決策として、Markdownの書きやすさとdocxの自己完結性を両立させる新しいファイル形式を構想する。

初期案として、画像をBase64エンコードしてMarkdownファイルに直接埋め込む方法を検討した。

![alt text](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...)

しかし、このアプローチはテキストエディタでの可読性と編集性を著しく損なうため、即座に棄却した。docxのように、ファイル全体を展開せずとも内部のアセット一覧などのメタデータに高速にアクセスできることが重要な要件となる。

💡 基本設計 - TextBundle v2の採用

ゼロから仕様を策定するのではなく、既存の仕様であるTextBundle v2[1]を基盤とすることにした。これにより、基本的なファイル構造についての議論を省略できる。

TextBundleの基本構造は以下の通り。

MyDocument.textbundle/
├── info.json
├── text.md
└── assets/
    └── image.png
  • info.json: バンドルのメタデータを記述する。
  • text.md: 文書の本体。
  • assets/: 画像などの添付ファイルを格納する。

このディレクトリ構造をZIPでアーカイブしたものが.textpackファイルとなる。

💡 機能拡張 - 内部バージョン管理

TextBundleの仕様にはバージョン管理の概念がない。そこで、バンドル自体に変更履歴を内包させる機能を考案する。具体的にはinfo.jsonを拡張し、historyオブジェクトを追加する。

{
  "version": 2,
  "type": "net.daringfireball.markdown",
  "history": {
    "log": "history/commits.jsonl",
    "diff": true,
    "hash": false
  }
}

変更履歴はhistory/commits.jsonlにJSON Lines[2]形式で追記していく。diff: trueの場合、変更差分をgit apply可能なpatchファイルとしてhistory/patches/以下に保存する。

{"author": "userA", "timestamp": "...", "message": "Initial commit"}
{"author": "userB", "timestamp": "...", "message": "Add image", "patchFile": "history/patches/1.patch"}

❌ 課題 - バイナリアセットの差分管理

テキストファイルの差分はpatchファイルで表現できるが、画像のようなバイナリファイルの変更を同様に扱うことは困難である。この問題を解決する必要がある。

✅ 解決策 - エイリアスによる間接参照

バイナリファイルの差分管理問題を回避するため、アセットを直接ファイル名で参照するのではなく、エイリアス(ユニークID)を介して間接的に参照する方式を導入する。

Markdown内での記述:

![alt text](assets/id-001)

info.jsonに対応表を追加:

{
  "version": 2,
  "type": "net.daringfireball.markdown",
  "aliases": {
    "id-001": "image.png"
  },
  "history": { ... }
}

これにより、画像の差し替えはinfo.json内のエイリアス定義を変更するだけで済む。info.jsonはテキストファイルであるため、この変更はpatchファイルとして記録可能となる。アセットの変更履歴を、テキストの変更履歴として間接的に管理できるようになった。

🤔 パフォーマンス - メタデータへの高速アクセス

.textpackはZIPアーカイブであるため、内部のinfo.jsonを読むためにアーカイブ全体を展開する必要があり、パフォーマンス上の懸念がある。docxの内部構造を参考に、この問題を解決する。

docxは、パッケージ内の関係性を定義したXMLファイル(_rels/.relsなど)を読み込むことで、全体を展開せずとも構造を把握している。

✅ 解決策 - Index-first ZIP

docxのアプローチを参考に、.textpackを作成する際のルールを定める。

  • info.jsonを、非圧縮(DEFLATE Level 0)で、ZIPアーカイブの先頭に配置する。

これにより、ZIPファイルをシーケンシャルに読み進め、info.jsonの部分だけを高速に取得し、パースすることが可能になる。ファイル全体をメモリに読み込んだり、ディスクに展開したりする必要がなくなる。

💡 機能拡張 - スタイリングとレンダリング

ドキュメントの見た目を制御するため、スタイリングの仕様を追加する。

info.jsonstyleキーとrendererキーを追加する。

{
  ...
  "style": "styles/github-theme.css",
  "renderer": "gfm"
}
  • style: スタイルシートのURIを指定する。相対パスの場合はバンドル内のassets/または専用のstyles/ディレクトリを参照する。http/httpsから始まるURLも許容する。
  • renderer: HTMLを生成する際のルールを指定する。gfm(GitHub Flavored Markdown)のように、デファクトスタンダードとなっている仕様を参照することで、CSSが適用されるHTMLのクラス名や構造を安定させる。

🤔 概念整理 - Wikiとの比較と本質的価値

このファイル形式と、Markdownで記述するWikiとの本質的な違いを明確にする。

  • Wiki: 中央集権的なWebサービス。オンライン環境が前提。データはサーバーに属する。
  • 新形式: 分散的でポータブルなファイル。オフラインでの編集が可能。データはユーザーに属する。

この形式の核心的な価値は、「自己完結したスナップショットとして扱える、オフライン編集可能な生きたドキュメント」であること。これにより、クライアントサイドでの自由な書き換えが可能になる。

❌ 未解決課題 - 衝突の解決

オフラインでの並行編集を許容するアーキテクチャであるため、変更の衝突(コンフリクト)は避けられない。あるユーザーが作成したファイルを、別のユーザーがコピーして同時に編集した場合、それらの変更をどうマージするかが大きな課題となる。

この問題を解決するには、SVNやGitが持つような衝突検知と、ユーザーに変更の解決を促すためのマージツールのような機構が必要になるだろう。

脚注
  1. TextBundle: プレーンテキストファイルとそれに付随するアセット(画像など)を一つのパッケージとして扱うためのファイル形式仕様。 ↩︎

  2. JSON Lines: 1行に1つの有効なJSONオブジェクトが記述されるテキスト形式。ストリーム処理に適している。 ↩︎

podhmopodhmo

markdownからhtmlやpdfを出力するのと何が異なるの?

非常に本質的な問いですね。その違いを明確にすることが、このファイル形式の価値を定義することに繋がります。

一言で言うと、PDFやHTMLへの変換が「一方通行の書き出し」であるのに対し、この形式は「編集可能なソースと成果物が一体化した、再編集可能なパッケージ」である点が本質的な違いです。

具体的には、以下の点で異なります。

  • 1. 可逆性と編集可能性:

    • PDF/HTML: 一度変換すると、元のシンプルなMarkdownに戻して編集するのは困難です。これらは「完成品」です。
    • 新形式: いつでも誰でも、元のMarkdownソース(text.md)を直接編集できます。「プロジェクトファイル」そのものを共有するイメージです。
  • 2. 人間が読める差分管理:

    • PDF/HTML: バージョン間の差分を比較するのは非常に困難です。
    • 新形式: 内部に持つ履歴機能により、「どの文章がどう変わったか」をMarkdownの差分として人間が分かりやすく追跡できます。
  • 3. 自己完結性:

    • HTML: 画像やCSSが別ファイルになりがちで、共有時にファイル一式を渡す必要があります。
    • 新形式: 必要なものが全て一つの .textpack ファイルにまとまっているため、取り扱いが非常にシンプルです。

つまり、単に「読む」ためだけに共有するならPDF/HTMLでも十分ですが、**共有した後も「書き続ける」「更新し続ける」ことを前提とした、「生きたドキュメント」**である点が最大の違いと言えます。