橋本 Hashimoto   Baku

橋本 Hashimoto   Baku

Morion

  • baku89.iconが開発しているヘッドレスCMS、ノート、Wiki、ナレッジベースシステム
  • 投稿タイプやタグ、カテゴリ、フォルダ分けという概念のない、ただのテキストファイルで相互リンクするページの集まりとしてサイトを構築できる
  • コアにあるのはAPI
  • https://featuredprojects.jp のために開発し、このサイトで投入しながら安定化を図っている
  • 使えそうなMarkdown拡張

要するに

  • Source of truth はローカルにおかれたObsidian互換のMarkdownファイル

Morionの原則

階層性と線形性を信じない

普通のCMS開発のように、workeventmember のような投稿タイプや、 /2025/some-post といったディレクトリに分類したりはしない。
そうした設計はその瞬間の見立てにすぎず、「workでもありeventでもある」ような中間的なコンテンツの登場とともに破綻する運命にある。
コンテンツ同士の連関は「AがBに属する」のような含有関係ではなく、「AがBを指し示す」というリンクによってのみ決まる。
また、Morionでは「ページ」と「タグ」の区別をしない。(Cosense同様)

ローカル・ファースト

ページのは常にローカルに置かれたテキストファイルと同期する。File System Access APIによって) ローカルは単なるバックアップではなく、オリジナルそのもの。書き手の「第二の脳」をクラウド上に人質に取らない。

プレーンテキストは普遍的なインターフェース

ページのソースはプレーンテキストによって表現される。CMSの管理画面だけではなく、メモ帳からも編集出来るし、ほかのCMSにインポートすることも、コマンドで一括置換することも、AIに喰わせることもできる。
このサイトはbaku89.iconの好みでMarkdownを採用したが、HTMLに変換できさえすれば、Cosenseのブラケット記法でもなんでも好きな構文をユーザーは採用することができる(予定)。

ちょうど今日、The VergeにObsidian CEOのSteph Angoへのインタビューを読んでいて、そこに
「ひとつ面白い展開として、ぼくはステフにこう尋ねた。多くの競合が生産性ソフトに次々とAI機能を詰め込もうとしているのに、なぜ Obsidian はそれに倣おうと急いでいないように見えるのか、と。」
という下りがありました。これは的外れも良いところで、プレーンテキストこそがその答えだろうと思っています。だってObsidianの実態は手元のPCに保存されたただのテキストファイルなんだから、Claude Codeにでもなんでも食べさせることができる。
エディタとの統合という点では Cursor を使ったっていい。むしろNotionはNotion AIとしかインテグレーションされていないですし……。
Obsidianには、多くのプラットフォーマーが口にする「エコシステム」なんて意識はなくて、プレーンテキストを介した、人類がこれまでに築き上げてきたコンピューティング文化という、より大きなものの一部であるっていう自覚と謙虚さがあるんだと思います。

ダダ漏れをアフォードする

コンテンツの階層構造同様に、その書き手の気分もまた、公か私か、寄稿かつぶやきかといった境界で割り切れるものではない、連続的なもの。
Morionでは、そうした曖昧さを切り捨てず、そのままに受け止められる「構造のなさ」を大事にしている。
ブログとニュース、リサーチとブックマークは区別なく保管でき、両方のタグをもたせればその中間的な性質をもたせることができる。
ページの公開設定やクロール設定も多段階でセットできるし、気まぐれで変えることもでる。
だからこそ、書き手は自分の思考や制作の過程を“ダダ漏れ”のまま残しておけるし、読み手はその連続性を含めてアクセスできる。

設計

Principles for Digital Gardening

各ページは本質的には以下の情報から成り立つ

  • ユニークなpath (e.g., /hello, /2025/some-posts)
  • DBにシリアライズ可能な何らかのデータ(JSONなど)

畢竟、本文すら必要ない。英単語のデーターベースサイトとかだと実際そうなると思う。Nuxt ContentはYML, CSVファイルをページデータとして使用することができる。

ただし、一般的にはページからなるサイトの構築にも使うものなので、プレーンテキストからHTMLの構文木にパース可能な言語を選定しておく。
以下、Lightweight markup language (LM言語) と呼ぶ。

  • HTMLそのもの
  • Markdown
  • Cosenseブラケット記法
  • Textile
  • 以下のVDOMそのものを表したJSONテキスト(何らかのリッチテキストエディタで編集される前提で)

構文木は例えばこういう感じ。(Nuxt Contentにおける内部表現の例。rehypeでもremarkでもどの表現方法でもOK)

{
  type: 'root',
  children: [
    {
      type: 'element',
      tag: 'p', 
      children: [
        {type: 'text', text: 'Hello, World!'}
      ]
    },
    {
      type: 'element',
      tag: 'img',
      attributes: {
        src: 'https://example.com/some-image.jpg'
      }
    }
  ]
}

これをVueでもReactでも、VDOMを実際のDOMに変換可能なフロントエンドライブラリに流し込み、Webを表示させる(あるいはCGIでもいい)

投稿タイプやメタデータのスキームは、流動的に変化させられるようにする。NoSQLで構築したいところ。Markdownだと、Frontmatterにこういう感じのものを書き込める。
ここはObsidianとできるだけ互換性をもたせたい。

created: 2025-08-20
title: Hello World
title_locale:
   ja: こんにちは、世界!
layout: grid
sort_by: modified
visibility: public
tags:
  - video
  - diary

ページ同士は、本文のリンク、あるいはtagsを介して関連性をもたせる。たとえば、「メンバーA」のページを「チーム」に関連付けるには、
単ページの中で「チーム」のpathにリンクを貼ればいい。

CMSにおけるタグ分類と違うのは、「チーム」と「メンバーA」のページに本質的な違いは無いこと。オブジェクト(括られるもの、対象)と
タクソノミー(分類子)を区別しない。カテゴリやフォルダによる木構造の分類に対して、タグによる分類はいわば二部グラフではあるが、
MorionについてはCosense同様有向グラフの構造をもたせる。

データ構造の階層関係

そして、「リンクされたもの」から「リンクしたもの」には自動的にバックリンクが貼られる。この機能をうまく使うことで、従来のCMSにおける
カテゴリや投稿リストいったものを表現する。

Backlink

Morionの現状の実装では、「タグによるリンク」と「本文中からのリンク」を便宜的に区別している。
タグリンクのほうが本文リンクよりもより高い関連性を示唆する。

  • タグリンク: 所属、包摂、共通性
  • 本文リンク: 言及

例えば、「チーム」というページをカテゴリのように運用したいときには、チームメンバーからタグリンクを貼ることとする。
かつ、ページのレイアウトをデフォルトのpageからそれ以外のもの(FPとこのサイトではgrid, tableを用意)に加えることで、
本文後に追記されるbacklinksから本文リンクを除外することができる。

layout: grid
sort_by: title
sort_order: asc

こうすることで、「チームメンバーはこちらを御覧ください!」という関係のないページがチーム一覧に表示されるのを防ぐ。

公開設定

  • public: 誰からもみることができ、リストにも表示される
  • unlsited: 一切のページリストには表示されず、URLの直接入力や直リンクのみで表示できる
  • proteced: 何らかの認証(パスワード、OAuth、WebAuthn等)越しに見ることができる
    • 数人への共有、メンバーシップ制、あるいは同じCMSを使うメンバー間でのみ閲覧可能
    • github:baku89, mail:b@baku89.comとか。
  • private: 書き手だけが見ることができる

archivalフラグ

全てのページは、公開設定とは別にarhivalというフラグを持つ。
これによって、永続化したいものと、エフェメラルなものを区別する。

  • Archival: 公開ページ(不変のURLで公開され続け、引用可能なもの)
    • permalink
  • Scratchpad: メモ書きページ(一応見ることのできる個人的なメモ書き)
    • no-index, no-follow

詳細はそれぞれのページを参照。

そして、archivalページは、バージョン管理が出来るようにする。SemVerで。

${major}.${minor}.${patch}

  • patch: 誤字修正。編集のたびに勝手にインクリメントされる
  • minor: 普通の改稿
  • major: 文章全体の構造が一変するくらいの大きなアップデート。

一度 1.0.0 を付与したテキストは、できる限り取り下げられないようにする。

多言語対応

LMLは多言語を扱えるように拡張してもいい。FPとbaku89.comではこうした、WordpressのqTranslatorに則ってこういう記法を採用している。

[\:ja]  
**なんらかの強調文字**
[\:en]
**Some emphasized texts**
[\:]

\」は無視して! プリプロセッサ的に正規表現置換しているから、このタグを普通に入力するとそれ自体が多言語ブロックとして解釈されちゃう

けどなんでもいいかも。今ならぼくはこうするかな。

<!--ja-->日本語<!--:en-->English<!--:-->

あるいはMDC記法にのっとって

::en
some texts
::

::ja
何かの文章
::

とか、

::locale
#en
some texts
#ja
::

だっていい。


API

  • [`/api/pages?tags=tools&limit
  • =5`](/api/pages?tags=tools&limit=5)
    • まちがえた、取得件数に上限を設けるのを忘れてて、そのままアクセスすると(公開されている)全ページがdumpされてしまう
  • /api/page?path=&2Fmorion
    • 常にpathにleading slashをつけるか迷う。やめたほうがいいと思う
  • /api/search-index
  • /api/pages-count