---
created: 2026-02-24T22:56:29.401Z
visibility: public
permanent: true
content_id: 01KJ8XWHPT4TCW71K9BTW81W6X
modified: 2026-04-01T01:43:45.769Z
---

# 情報設計

## コンテンツパイプライン

ページの「本体」は Markdown のソースコードではなく、HTML の AST（抽象構文木）。

```mermaid
graph LR
  source[source:string] -- parser --> AST[body:AST<br>HTML の構文木]
  AST -- renderer --> DOM
```

現在は Nuxt MDC の `MDCRoot` を AST フォーマットとして使っているけれど、ここは差し替え可能な設計になっている。[unified エコシステム](https://unifiedjs.com/)を使えば、Markdown に限らず HTML にパース可能な任意の軽量マークアップ言語をサポートできる。

サーバーが保持するのは `{source: string, body: AST, ...metadata}` の組。AST の生成はクライアントサイドで行う。

この設計がもたらすもの:

- **Copy as Markdown**: AST → Markdown の逆変換
- **PDF エクスポート**: permanent ページの永続的な文書化
- **カスタムパーサー**: ユーザーが独自の記法を持ち込める

## 識別子の3層構造

ページには3種類の識別子がある。それぞれ異なる文脈で使う。

::wide

| 識別子    | 用途                               | 例                                |
| ------ | -------------------------------- | -------------------------------- |
| `path` | ローカルのファイルパス。空白や記号をそのまま含む         | `Articles/100% User Supported`   |
| `uri`  | URL として使える形式。空白→`_`、制御文字→エスケープ   | `Articles/100%25_User_Supported` |
| `slug` | DB 検索用の正規化キー。`uri.toLowerCase()` | `articles/100%25_user_supported` |

::

変換は `path → uri → slug` の一方向。逆変換は `uri → path`（`_` → 半角スペース）のみ可能。

`slug` が重複するページは作成できない。ローカルで衝突が起きた場合、後から検知された方はリネームされるまでリモートに同期されない。

**関連コード:**

- `utils/normalizeIdentifiers.ts` — 識別子の変換
- `sanitizeTitleToPath` — パスのサニタイズ
- `normalizeToSlug` — slug の正規化

## リンクとバックリンク

ページ同士の関連性はリンクで表現する。種類は2つ。

### Tag link と Body link

- **Tag link**（`A is B`）: frontmatter の `tags` で指定。所属・包摂・共通性
- **Body link**（`A refers to B`）: 本文中のハイパーリンク。言及・参照

Tag link のほうが強い関連性を示す。

### バックリンク

A → B のリンクがあるとき、B のページに「関連ページ」として A を自動表示する仕組み。有向グラフとしてのハイパーリンクを双方向にしている。

これで従来 CMS の「カテゴリ一覧」を代替できる。「チーム」というページに、メンバーの各ページから tag link を貼って、レイアウトを `grid` にすれば、チームメンバー一覧ページになる。`grid` / `table` レイアウトでは body link のバックリンクが除外されるので、「チームについてはこちら！」みたいな無関係なページが混ざらない。

**関連コード:**

- `utils/markdown/collectOutlinksFromPage.ts` — アウトリンク収集
- `types/page.ts` の `Link` — `{type: 'tag' | 'link', slug: string}`

## 公開範囲（Visibility）

4段階ある。

- `public`: 誰でも見える。リストに表示される
- `unlisted`: URL直打ちか直リンクでのみ到達可能。リスト表示やバックリンクには現れない。「文脈を追わないとたどり着けない」状態を作る
- `protected`: 認証（パスワード、OAuth 等）が必要。メンバーシップ制の限定コンテンツや有料配信（note/Substack 的な）を想定している。決済機能は未実装
- `private`: 書き手だけが見える

**関連コード:** `types/page.ts` の `visibilitySchema`

## Permanent フラグ

公開範囲とは別軸で、すべてのページが持つフラグ。永続化したいものとエフェメラルなものを分ける。

- **Permanent page**（`permanent: true`）: 不変の URL で公開したいもの
- **Note page**（`permanent: false`）: 個人的なメモや下書き。`noindex, nofollow` で検索エンジンにもインデックスされず、いつでも消せる

`note page` はあくまで概念上の呼び名で、frontmatter に `note: true` のような専用フィールドがあるわけではない。コード上でも基本的には `permanent === false` として扱われる。

`permanent` は明示的に `true` と書かなくても、`layout` が `page` 以外だったり、特定のタグ（`works`, `video` 等）が付いていれば自動的に `true` に推定される（`FrontmatterSchema` の transform 参照）。