Color mode

Configuration

Riebeckite の設定は riebeckite.config.ts に記述します。

基本的な Site では、defineConfig() を使って次のように設定します。

ts
import { defineConfig } from "@riebeckite/core";
 
export default defineConfig({
  site: {
    title: "My site",
    baseUrl: "https://example.com",
  },
 
  content: {
    directory: "content",
    exclude: ["drafts/**"],
    filters: {
      publishStrategy: "explicit",
    },
  },
 
  theme: {
    colorMode: "system",
    articleLayout: "article",
  },
 
  plugins: [],
});

必須なのは site です。

そのほかは必要に応じて設定します。

Field 用途
site Site の基本情報
content コンテンツの場所と公開条件
theme Theme の設定
plugins 使用する Plugin
cache build cache の設定

Config は Content や Plugin の処理が始まる前に Integration によって解決されます。

Navigation はトップレベルの Config 項目ではなく、@riebeckite/plugin-navigation Plugin が提供します。Plugin は { primary, secondary } という意味的なモデルを返し、SiteNav を通じて描画の仕組みを持ちます。Site がそれぞれのリストをどこに配置するかを決めます。primary と secondary は目立たせ方の違いを表すもので、配置そのものではありません。Plugin API に header / footer というキーはありません。

ts
import { defineConfig } from "@riebeckite/core";
import { navigation } from "@riebeckite/plugin-navigation";
 
export default defineConfig({
  site: { title: "My site", baseUrl: "https://example.com" },
  plugins: [navigation()],
});

描画

@riebeckite/plugin-navigation の SiteNav は、解決済みのツリーを標準の rb-nav 構造で描画し、現在パスの判定、言語を考慮した正規化、aria-current を提供します。ツリーをどこに置くかは Site が決めます。

tsx
import { SiteNav } from "@riebeckite/plugin-navigation";
 
<SiteNav
  items={model.primary}
  path={c.req.path}
  language={c.get("htmlLanguage")}
/>;

localizeHref を渡すと href を書き換えられます(たとえば docs リンクのローカライズ)。label でランドマークのラベルを、class / className で <nav> のクラスを拡張できます。

引数なしの導出

引数なしで呼び出すと、Vault の discoverable entries(manifest.discoverableEntries。public かつ discoverable で、draft や非 routable な Content を除く)から primary を導出します。専用の Vault ファイルを要求せず、既存の Riebeckite の情報を再利用します。

  • folder 構造(folder はセクションになり、ネストした folder は children になります)
  • README / index の解決(index または README のノートがその folder を表し、folder の href になります)
  • README / index を持たない folder は、リンクを持たない label になります
  • ルート直下の README / index は Navigation には現れません
  • ページの title(無い場合は slug のセグメントを整形)
  • permalink

Riebeckite 専用の Vault ファイル(navigation.md など)も、必須の frontmatter も必要ありません。

導出は表示中の言語に追従します。l10n Plugin が付与する言語 metadata をもとに同じ翻訳の entry をひとつの項目へ集約し、現在の言語の href だけを使います。/ja/guide/ を表示しているときに /en/... が混ざることはありません。l10n を使っていない Vault では、これまでどおり全 entry が対象になります。

手動リンクと補助リンク

items を渡すと、導出された primary を置き換えます。secondary には、Site がより控えめに表示する補助リンクを渡します。

ts
navigation({
  items: [
    { label: "Guide", href: "/guide" },
    {
      label: "Notes",
      href: "/notes/planning",
      children: [
        { label: "Planning", href: "/notes/planning" },
        { label: "Writing", href: "/notes/writing" },
      ],
    },
  ],
  secondary: [
    { label: "GitHub", href: "https://github.com/example/site", external: true },
  ],
});

各リンクは NavigationItem として設定します。

Field 型 説明
label string リンクに表示する名前
href string 遷移先の path または URL
children NavigationItem[] 子項目。サブメニューとして表示されます
external boolean true の場合は別タブで開きます

手動指定の item では label と href が必須です。index ノートを持たない導出 folder は label のみで描画されるため、導出モデルでは href は省略可能です。

配置

配置は Plugin ではなく Site の shell が決めます。Reference Site では primary を header、secondary を footer に表示します。Site タイトルがすでに Home へのリンクになっている shell では、href: "/" の item を表示しないことがあります。

サブメニューを作る

children を使うと、Navigation を入れ子にできます。

ts
{
  label: "Notes",
  href: "/notes/planning",
  children: [
    { label: "Planning", href: "/notes/planning" },
    { label: "Writing", href: "/notes/writing" },
  ],
}

children はサブメニューとして表示されます。

外部サイトへリンクする

外部サイトへのリンクには external: true を指定できます。

ts
{
  label: "GitHub",
  href: "https://github.com/example/site",
  external: true,
}

この場合は別タブで開き、リンクに rel="noreferrer" が付きます。

現在のページを示す

現在表示しているページに対応するリンクが自動的に active になります。

たとえば、

ts
{ label: "Guide", href: "/guide" }

という項目がある場合、次のようなページで active になります。

text
/guide
/guide/getting-started
/en/guide
/en/guide/getting-started

末尾の / や先頭の locale は判定時に調整されるため、/guide/ と /en/guide のような違いを意識する必要はありません。

active なリンクには aria-current="page" が付きます。

外部 URL など / から始まらない href と、external: true の項目は active 判定の対象になりません。

モバイルでの表示

画面が狭い場合、Site の Navigation は Menu から開閉できる表示になります。

Navigation の内容や HTML 構造が別のものになるわけではなく、画面幅に応じて CSS で表示方法が変わります。

設定の検証

navigation の Option は Plugin の読み込み時に検証されます。

主な条件は次のとおりです。

  • label は空でない文字列
  • href は空でない文字列
  • external を指定する場合は boolean
  • children に祖先の項目を含めることはできない

不正な設定は Plugin の読み込み時にエラーになります。

Plugin のページは自動追加されない

Plugin は Vault の discoverable entries からリンクを導出します。Plugin が生成するページを自動で surface することはありません。

たとえば、次のようなものは自動的には追加されません。

  • Search
  • Tag / Folder 一覧
  • Taxonomy のページ
  • Plugin の Page Type
  • Breadcrumbs
  • Backlinks
  • Related Posts
  • その他の Content graph 機能

Plugin が作るページを表示したい場合は、そのページへのリンクを navigation({ items }) に追加してください。

Navigation と Plugin の役割の違いについては、サイトのカスタマイズ を参照してください。

エクスポートされる helper と型

@riebeckite/plugin-navigation は navigation、buildNavigation、resolveSiteNavigation、NAVIGATION_PLUGIN_NAME、描画 primitive の SiteNav と、型 NavigationItem、NavigationOptions、SiteNavigation、SiteNavProps をエクスポートします。

Content の設定

通常は content.directory で Markdown などを読み込むディレクトリを指定します。

ts
content: {
  directory: "content",
}

標準的な Site なら、

text
my-site/
├─ app/
├─ content/
├─ public/
├─ package.json
├─ vite.config.ts
└─ riebeckite.config.ts

のような構成になります。

除外するファイル

exclude を使うと、Content System に読み込ませないファイルを指定できます。

ts
content: {
  directory: "content",
  exclude: [
    "drafts/**",
    "Templates/**",
  ],
}

exclude に一致したファイルは、コンテンツとして処理される前に除外されます。

内部では isExcluded がこの判定を行います。

公開条件

どのコンテンツを公開するかは、既定では publishStrategy で設定します。

ts
content: {
  filters: {
    publishStrategy: "explicit",
  },
}

公開判定には frontmatter も利用されます。

yaml
---
publish: true
---

現在の公開状態は Core で一度だけ解決され、Plugin には次の manifest view として渡されます。

View 含まれるもの 用途
manifest.publicEntries ルーティングできるページ。public と unlisted ページ表示、SSG の path 列挙
manifest.discoverableEntries 発見可能な public ページだけ docs navigation、search、feed、sitemap、taxonomy、graph、backlinks、related/recent

frontmatter で明示的な公開状態を指定できます。

Frontmatter 結果
visibility: public URL で表示でき、一覧や検索にも出る
visibility: unlisted URL を知っていれば表示できるが、一覧や検索には出ない
visibility: draft URL でも表示されず、一覧や検索にも出ない
publishAt: 2026-01-01T00:00:00.000Z build 時刻がその日時より前なら非公開、以後の build で public になる
visibility / publishAt なし publishStrategy に従う。explicit は publish: true が必要。selective は private: true と draft: true を除外する

visibility や publishAt が不正な場合、推測せず build を失敗させます。scheduled publishing は build 時刻だけで判定します。Riebeckite は runtime timer を起動しません。

exclude と publishStrategy は似ていますが、役割が異なります。

Diagram source
text
flowchart LR
    Files["Files"]
    Exclude{"exclude ?"}
    Content["Content"]
    Publish{"Published ?"}
    Public["Public Content"]
    Private["Not Published"]
 
    Files --> Exclude
    Exclude -->|Yes| Skip["読み込まない"]
    Exclude -->|No| Content
    Content --> Publish
    Publish -->|Yes| Public
    Publish -->|No| Private

exclude は Content System に入れるか、publishStrategy や visibility は Site でどう扱うかを決めます。exclude されたファイルは link resolution や graph、diagnostics にも現れません。一方、draft、unlisted、公開前の publishAt は raw manifest には残りますが、Core が route 用 view と discovery 用 view から適切に外します。

ContentSource

通常は content.directory を使用しますが、独自の読み込み元を使用する場合は content.source を指定できます。

text
content.directory
    ↓
標準 filesystem ContentSource

または、

text
content.source
    ↓
独自 ContentSource

のどちらかです。

同じコンテンツに対して2つの reader を動かすための設定ではありません。

content.source を指定する場合は、標準 filesystem reader を置き換えるものとして扱います。

3つの Root

外部 Vault や monorepo 構成を扱う場合に重要なのが、

  • appRoot
  • configRoot
  • contentRoot

の違いです。

Diagram source
text
flowchart TD
    App["appRoot<br/>Site Application"]
    Config["configRoot<br/>Config の場所"]
    Content["contentRoot<br/>Content / Vault の場所"]
 
    App -->|"既定"| Config
    App -->|"content.directory を解決"| Content
名前 何を表す? 既定値 / 基準
appRoot HonoX / Vite Site の root Vite root
configRoot riebeckite.config.* を探す場所 appRoot
contentRoot 実際にコンテンツを読む場所 path.resolve(appRoot, content.directory)

この3つは別の役割を持ちます。

appRoot

appRoot は Site Application の基準となるディレクトリです。

たとえば、

text
site/
├─ app/
├─ public/
├─ package.json
├─ vite.config.ts
└─ riebeckite.config.ts

なら通常、

text
appRoot = site/

です。

appRoot は、

  • app/
  • public/
  • route
  • generated styles
  • Build 設定

など Site Application の基準になります。

configRoot

configRoot は、

text
riebeckite.config.ts
riebeckite.config.js
riebeckite.config.mjs

を探す基準です。

通常は appRoot と同じです。

text
appRoot
   └─ riebeckite.config.ts

特殊な repository 構成で config を別の場所へ置く場合のみ変更します。

configRoot を変更しても content.directory の基準は変わらない点に注意してください。

contentRoot

contentRoot は、実際に Markdown や asset を読み込む場所です。

相対 content.directory は常に appRoot を基準に解決されます。

ts
content: {
  directory: "../vault",
}

なら、

text
contentRoot
  = path.resolve(appRoot, "../vault")

となります。

Diagram source
text
flowchart LR
    App["appRoot<br/>workspace/site"]
    Directory["content.directory<br/>../vault"]
    Root["contentRoot<br/>workspace/vault"]
 
    App --> Directory
    Directory --> Root

process.cwd() を基準にするわけではありません。

そのため CLI を別の directory から実行しても、同じ Site Application を解決できれば同じ Vault を参照できます。

外部 Vault を使う

Obsidian Vault を Site と独立して管理したい場合は、Site の外へ置く構成を推奨します。

たとえば、

text
workspace/
├─ site/
│  ├─ package.json
│  ├─ vite.config.ts
│  ├─ riebeckite.config.ts
│  ├─ app/
│  └─ public/
│
└─ vault/
   ├─ index.md
   ├─ notes/
   ├─ attachments/
   └─ media/

という構成です。

Diagram source
text
flowchart LR
    Site["site/<br/>HonoX / Vite Application"]
    Config["riebeckite.config.ts"]
    Vault["vault/<br/>Obsidian Content"]
 
    Site --> Config
    Config -->|"content.directory = ../vault"| Vault

Site と Vault の役割が明確に分離されます。

text
site/
  → Application
 
vault/
  → Source Content

Vault を Vite application root にする必要はありません。

外部 Vault の設定例

ts
// site/riebeckite.config.ts
 
import { defineConfig } from "@riebeckite/core";
import { attachment } from "@riebeckite/plugin-attachment";
import { media } from "@riebeckite/plugin-media";
import { obsidianMarkdown } from "@riebeckite/plugin-obsidian-markdown";
 
export default defineConfig({
  site: {
    title: "My notes",
  },
 
  content: {
    directory: "../vault",
    exclude: [
      ".obsidian/**",
      "Templates/**",
    ],
  },
 
  plugins: [
    obsidianMarkdown(),
    media(),
    attachment(),
  ],
});

この場合、

text
appRoot
  = workspace/site
 
content.directory
  = ../vault
 
contentRoot
  = workspace/vault

となります。

絶対パスを指定することもできます。

ts
content: {
  directory: "C:/Users/example/Documents/vault",
}

ただし絶対パスは開発 PC や CI で場所が変わると使えなくなるため、通常は Site からの相対パスを推奨します。

process.cwd() に依存しない

Content directory を次のように組み立てることは避けてください。

ts
directory: path.resolve(
  process.cwd(),
  "../vault",
)

CLI をどこから実行したかによって結果が変化するためです。

また、

text
appRoot = Vault

とする必要もありません。

Vault は source data、appRoot は Site Application です。

Diagram source
text
flowchart LR
    Vault["Vault<br/>Source Data"]
    Site["Site<br/>Application"]
    Build["Riebeckite"]
 
    Vault --> Build
    Site --> Build
 
    Build --> Output["Generated Site"]

この境界を維持してください。

Application から ContentManager を使う

通常、HonoX Integration が contentRoot を自動的に解決します。解決済みの値は Framework 所有の module として公開されるため、Site が riebeckite.config.ts を読み直す必要はありません。

ts
import { config } from "virtual:riebeckite/config";
import { content } from "virtual:riebeckite/content";

config.content.directory は絶対パスで、content はそれに結びついた ContentManager です。そのため、別の基準からもう一度 path.resolve() しないでください。この module は riebeckiteVite() が解決します。Vite の外で動く script(tsx で起動する Node script など)は @riebeckite/honox/runtime の resolveHonoxConfig で同じ値を解決できます。

ts
import { fileURLToPath } from "node:url";
import { resolveConfigModule } from "@riebeckite/core";
import { resolveHonoxConfig } from "@riebeckite/honox/runtime";
import * as rawConfigModule from "../riebeckite.config";
 
const appRoot = fileURLToPath(new URL("../", import.meta.url));
export const config = resolveHonoxConfig(
  resolveConfigModule(rawConfigModule),
  appRoot,
);
Diagram source
text
flowchart LR
    Relative["../vault"]
    Resolve["appRoot から一度だけ resolve"]
    Absolute["C:/.../vault"]
    Manager["ContentManager"]
 
    Relative --> Resolve
    Resolve --> Absolute
    Absolute --> Manager

Attachment と Media

Obsidian の attachment や media も contentRoot を基準に扱います。

ここで attachment / media とは Markdown でも画像でもないファイルです。Vault 内の画像(png、jpg、svg など)は content image として別の扱いになるため、混同しないよう次の節で分けて説明します。

たとえば Vault に、

text
vault/
├─ notes/
│  └─ report.md
├─ attachments/
│  └─ report.pdf
└─ media/
   └─ interview.mp3

があるとします。

Markdown では、

md
![[attachments/report.pdf]]
 
![[media/interview.mp3]]

のように参照できます。

obsidianMarkdown() はこれらを Vault からの相対 logical path として扱います。

attachment() と media() が対応する embed を描画します。

公開 URL は安定した形式になります。

text
/assets/attachments/<Vault からの相対 logical path>

たとえば、

text
attachments/report.pdf
 
↓
 
/assets/attachments/attachments/report.pdf

のように logical path を維持します。

Asset URL と実ファイルは別

ここは特に重要です。

URL を生成することと、そのファイルが Site へ公開されることは別のことです。

Asset は次の3種類に分かれ、公開を担当する場所も異なります。

種類 対象 公開 URL 公開を担当する場所
Content image Vault 内の画像 /<Vault からの相対 logical path> Riebeckite の build
Attachment / Media Markdown でも画像でもないファイル /assets/attachments/<Vault からの相対 logical path> Site Application
Static asset Site Application 自身が管理するファイル / 配下 Vite の public/
Diagram source
text
flowchart LR
    Vault["Vault"]
    Image["Content image"]
    Attach["Attachment / Media"]
 
    Vault --> Image
    Vault --> Attach
 
    Image -->|"build が書き出す"| Output["Build Output"]
    Attach -->|"URL だけ生成"| Public["public/"]
    Public --> Output

Content image は build で公開される

obsidianMarkdown() は、公開ページから参照されている image を build の出力として書き出します。

Site Application が何もしなくても、その image は build output に含まれ、生成された URL から取得できます。

たとえば、

text
assets/logo.png
 
↓
 
/assets/logo.png

という論理 path をそのまま公開します。

開発サーバーでも、同じ論理 path のまま Content から直接配信されます。

参照されていない image、非公開ページからの image は書き出されません。どの image を書き出すかは公開ページと参照関係から決まります。

Attachment と Media の公開は Site Application の担当

Attachment と Media については、URL を生成しただけでは実ファイルは公開されません。

Plugin が、

text
/assets/attachments/attachments/report.pdf

という URL を生成したからといって、report.pdf が自動的に Vite の public/ へコピーされるわけではありません。

Site Application は、公開する必要がある asset だけを、

text
public/assets/attachments/

へコピーしてください。

その際も Vault からの相対 logical path を維持します。

参照 Application の build_images.ts は、実際に参照されている attachment だけを差分コピーする実装例です。

public/ を使う Static asset

public/ は Site Application 自身が管理する asset 用の directory です。

public/ 以下のファイルは、Vite の build でそのまま build output へコピーされます。

Content から取り込んだ画像や attachment をここへまとめて置くのではなく、上記のように公開する対象を絞って配置します。

Vault 全体を公開しない

次のような実装は避けてください。

text
vault/**
   ↓
public/**

Vault 全体をそのまま public/ へコピーすると、

  • 非公開の記事
  • 未公開ページからしか参照されていない画像
  • 未参照の attachment
  • .obsidian/ の metadata
  • 公開するつもりのないファイル

まで公開される可能性があります。

Diagram source
text
flowchart TD
    Vault["Vault"]
 
    Vault --> Published["公開対象Content"]
    Vault --> UsedAssets["参照されているAssets"]
    Vault --> Private["非公開Content"]
    Vault --> Metadata[".obsidian / Metadata"]
 
    Published --> Public["Public Site"]
    UsedAssets --> Public
 
    Private -. "公開しない" .-> Public
    Metadata -. "公開しない" .-> Public

必要な asset だけを公開することが重要です。

Content image は build が公開対象を判断します。Attachment と Media をどの範囲で公開するかは Site Application 側の責務です。

Build cache

cache は build 時に使う永続 cache の設定です。省略可能で、既定では Integration が適切な directory を決めます。

Field 既定値 説明
cache.enabled true false にすると永続 cache を無効化し、毎回すべてを再生成します。
cache.directory <buildDirectory>/cache cache の保存先を上書きします。cache を別の場所へ移したり共有したい場合に指定します。未指定なら Integration の既定値を使います。

cache には build 間で再利用する処理済み Content や Plugin の結果が入ります。無効化や保存先の変更は build の速度にだけ影響し、出力は変わりません。cold build でも同じ結果になります。

Plugin の設定

Plugin は plugins に指定します。

ts
plugins: [
  obsidianMarkdown(),
  media(),
  attachment(),
]

条件によって Plugin を切り替えることもできます。

PluginInput では、

text
false
null
undefined

を無効な Plugin input として扱えます。

たとえば、

ts
plugins: [
  enableAnalytics && analytics(),
]

のような conditional configuration が可能です。

Config resolve 時に無効な input は除外され、有効な Plugin は安定した順序で整理されます。

その後 capability の整合性が検証されます。

Theme の設定

Theme は theme で設定します。

ts
theme: {
  colorMode: "system",
  articleLayout: "article",
}

raw configuration または宣言済み Theme を利用できます。

Theme は presentation の設定です。

HonoX、Vite、Cloudflare など Integration 固有の設定を Core configuration へ入れないでください。

Diagram source
text
flowchart TD
    Config["Riebeckite Config"]
 
    Config --> Site["Site"]
    Config --> Content["Content"]
    Config --> Markdown["Markdown"]
    Config --> Theme["Theme"]
    Config --> Plugins["Plugins"]
 
    Framework["HonoX / Vite / Platform"]
    Framework --> Integration["Integration Config"]
 
    Integration -. "Core Configへ混ぜない" .-> Config

Config の検証

Configuration に問題がある場合は ConfigValidationError として報告されます。

不正な Configuration を無視してそのまま起動するのではなく、早い段階で失敗させます。

Config を変更した後は、

sh
npm exec riebeckite check

を実行してください。

外部 Vault のトラブルシュート

外部 Vault や複雑な directory 構成を使っている場合は、次の順番で確認すると原因を切り分けやすくなります。

Diagram source
text
flowchart LR
    Check["1. check"]
    Doctor["2. doctor"]
    Config["3. inspect config"]
    Content["4. inspect content --list"]
    Build["5. build"]
 
    Check --> Doctor
    Doctor --> Config
    Config --> Content
    Content --> Build

1. Config を検証する

sh
npm exec riebeckite check

Config と Plugin contract が正しいか確認します。

2. Content Source を診断する

sh
npm exec riebeckite doctor

filesystem content source が存在しない、読み込めないなどの問題を確認します。

3. 解決された Directory を確認する

sh
npm exec riebeckite inspect config

content.directory が期待する絶対 path に解決されているか確認します。

4. Content を確認する

sh
npm exec -- riebeckite inspect content --list

WikiLink や embed を調査する前に、期待する logical path と content が認識されていることを確認します。

5. 実際に Build する

sh
npm exec riebeckite build

最後に Integration、SSG、route rendering まで含めて確認します。

Config を Site の外へ置く

通常、

text
appRoot
  = configRoot

ですが、意図的に riebeckite.config.ts を別の directory に置くこともできます。

その場合は riebeckiteVite() に configRoot を指定します。

ただし、

text
appRoot
  → Site Application
 
configRoot
  → Config
 
contentRoot
  → Content / Vault

という役割は変わりません。

特に appRoot を Vault 側へ変更しないでください。

相対 content.directory は、configRoot ではなく引き続き appRoot を基準に指定します。

まとめ

Configuration では、Site と Content の場所を混同しないことが重要です。

Diagram source
text
flowchart LR
    App["appRoot<br/>Siteはどこ?"]
    Config["configRoot<br/>Configはどこ?"]
    Content["contentRoot<br/>Contentはどこ?"]
 
    Config --> Resolve["Configuration Resolution"]
    App --> Resolve
    Resolve --> Content
 
    Content --> Manager["Content System"]
    Manager --> Build["Build"]

基本的には、

text
appRoot
  = Site Application の場所
 
configRoot
  = Config の場所
 
contentRoot
  = Markdown / Vault の場所

と覚えておけば十分です。

通常の Site では3つの違いを意識する必要はほとんどありません。

外部 Vault、別 repository の Content、特殊な monorepo 構成を使う場合だけ、この境界を意識してください。

Config の変更後は riebeckite check、実際にどう解決されたか確認したい場合は riebeckite inspect config を使用してください。

History

1 changesCollapseExpand
1 + # Configuration
2 +
3 + Riebeckite の設定は `riebeckite.config.ts` に記述します。
4 +
5 + 基本的な Site では、`defineConfig()` を使って次のように設定します。
6 +
7 + ```ts id="vps2rj"
8 + import { defineConfig } from "@riebeckite/core";
9 +
10 + export default defineConfig({
11 + site: {
12 + title: "My site",
13 + baseUrl: "https://example.com",
14 + },
15 +
16 + content: {
17 + directory: "content",
18 + exclude: ["drafts/**"],
19 + filters: {
20 + publishStrategy: "explicit",
21 + },
22 + },
23 +
24 + theme: {
25 + colorMode: "system",
26 + articleLayout: "article",
27 + },
28 +
29 + plugins: [],
30 + });
31 + ```
32 +
33 + 必須なのは `site` です。
34 +
35 + そのほかは必要に応じて設定します。
36 +
37 + | Field | 用途 |
38 + | --- | --- |
39 + | `site` | Site の基本情報 |
40 + | `content` | コンテンツの場所と公開条件 |
41 + | `theme` | Theme の設定 |
42 + | `plugins` | 使用する Plugin |
43 + | `cache` | build cache の設定 |
44 +
45 + Config は Content や Plugin の処理が始まる前に Integration によって解決されます。
46 +
47 + ## Navigation の設定
48 +
49 + Navigation はトップレベルの Config 項目ではなく、**`@riebeckite/plugin-navigation`** Plugin が提供します。Plugin は `{ primary, secondary }` という意味的なモデルを返し、`SiteNav` を通じて描画の仕組みを持ちます。**Site がそれぞれのリストをどこに配置するか**を決めます。`primary` と `secondary` は目立たせ方の違いを表すもので、配置そのものではありません。Plugin API に `header` / `footer` というキーはありません。
50 +
51 + ```ts
52 + import { defineConfig } from "@riebeckite/core";
53 + import { navigation } from "@riebeckite/plugin-navigation";
54 +
55 + export default defineConfig({
56 + site: { title: "My site", baseUrl: "https://example.com" },
57 + plugins: [navigation()],
58 + });
59 + ```
60 +
61 + ### 描画
62 +
63 + `@riebeckite/plugin-navigation` の `SiteNav` は、解決済みのツリーを標準の `rb-nav` 構造で描画し、現在パスの判定、言語を考慮した正規化、`aria-current` を提供します。ツリーをどこに置くかは Site が決めます。
64 +
65 + ```tsx
66 + import { SiteNav } from "@riebeckite/plugin-navigation";
67 +
68 + <SiteNav
69 + items={model.primary}
70 + path={c.req.path}
71 + language={c.get("htmlLanguage")}
72 + />;
73 + ```
74 +
75 + `localizeHref` を渡すと href を書き換えられます(たとえば docs リンクのローカライズ)。`label` でランドマークのラベルを、`class` / `className` で `<nav>` のクラスを拡張できます。
76 +
77 + ### 引数なしの導出
78 +
79 + 引数なしで呼び出すと、Vault の **discoverable entries**(`manifest.discoverableEntries`。public かつ discoverable で、draft や非 routable な Content を除く)から `primary` を導出します。専用の Vault ファイルを要求せず、既存の Riebeckite の情報を再利用します。
80 +
81 + - folder 構造(folder はセクションになり、ネストした folder は `children` になります)
82 + - README / index の解決(`index` または `README` のノートがその folder を表し、folder の `href` になります)
83 + - README / index を持たない folder は、リンクを持たない label になります
84 + - ルート直下の README / index は Navigation には現れません
85 + - ページの `title`(無い場合は slug のセグメントを整形)
86 + - `permalink`
87 +
88 + **Riebeckite 専用の Vault ファイル(`navigation.md` など)も、必須の frontmatter も必要ありません。**
89 +
90 + 導出は表示中の言語に追従します。`l10n` Plugin が付与する言語 metadata をもとに同じ翻訳の entry をひとつの項目へ集約し、現在の言語の `href` だけを使います。`/ja/guide/` を表示しているときに `/en/...` が混ざることはありません。`l10n` を使っていない Vault では、これまでどおり全 entry が対象になります。
91 +
92 + ### 手動リンクと補助リンク
93 +
94 + `items` を渡すと、導出された `primary` を置き換えます。`secondary` には、Site がより控えめに表示する補助リンクを渡します。
95 +
96 + ```ts
97 + navigation({
98 + items: [
99 + { label: "Guide", href: "/guide" },
100 + {
101 + label: "Notes",
102 + href: "/notes/planning",
103 + children: [
104 + { label: "Planning", href: "/notes/planning" },
105 + { label: "Writing", href: "/notes/writing" },
106 + ],
107 + },
108 + ],
109 + secondary: [
110 + { label: "GitHub", href: "https://github.com/example/site", external: true },
111 + ],
112 + });
113 + ```
114 +
115 + ### NavigationItem
116 +
117 + 各リンクは `NavigationItem` として設定します。
118 +
119 + | Field | 型 | 説明 |
120 + | --- | --- | --- |
121 + | `label` | `string` | リンクに表示する名前 |
122 + | `href` | `string` | 遷移先の path または URL |
123 + | `children` | `NavigationItem[]` | 子項目。サブメニューとして表示されます |
124 + | `external` | `boolean` | `true` の場合は別タブで開きます |
125 +
126 + 手動指定の item では `label` と `href` が必須です。index ノートを持たない導出 folder は label のみで描画されるため、導出モデルでは `href` は省略可能です。
127 +
128 + ### 配置
129 +
130 + 配置は Plugin ではなく Site の shell が決めます。Reference Site では `primary` を header、`secondary` を footer に表示します。Site タイトルがすでに Home へのリンクになっている shell では、`href: "/"` の item を表示しないことがあります。
131 +
132 + ### サブメニューを作る
133 +
134 + `children` を使うと、Navigation を入れ子にできます。
135 +
136 + ```ts
137 + {
138 + label: "Notes",
139 + href: "/notes/planning",
140 + children: [
141 + { label: "Planning", href: "/notes/planning" },
142 + { label: "Writing", href: "/notes/writing" },
143 + ],
144 + }
145 + ```
146 +
147 + `children` はサブメニューとして表示されます。
148 +
149 + ### 外部サイトへリンクする
150 +
151 + 外部サイトへのリンクには `external: true` を指定できます。
152 +
153 + ```ts
154 + {
155 + label: "GitHub",
156 + href: "https://github.com/example/site",
157 + external: true,
158 + }
159 + ```
160 +
161 + この場合は別タブで開き、リンクに `rel="noreferrer"` が付きます。
162 +
163 + ### 現在のページを示す
164 +
165 + 現在表示しているページに対応するリンクが自動的に active になります。
166 +
167 + たとえば、
168 +
169 + ```ts
170 + { label: "Guide", href: "/guide" }
171 + ```
172 +
173 + という項目がある場合、次のようなページで active になります。
174 +
175 + ```text
176 + /guide
177 + /guide/getting-started
178 + /en/guide
179 + /en/guide/getting-started
180 + ```
181 +
182 + 末尾の `/` や先頭の locale は判定時に調整されるため、`/guide/` と `/en/guide` のような違いを意識する必要はありません。
183 +
184 + active なリンクには `aria-current="page"` が付きます。
185 +
186 + 外部 URL など `/` から始まらない `href` と、`external: true` の項目は active 判定の対象になりません。
187 +
188 + ### モバイルでの表示
189 +
190 + 画面が狭い場合、Site の Navigation は `Menu` から開閉できる表示になります。
191 +
192 + Navigation の内容や HTML 構造が別のものになるわけではなく、画面幅に応じて CSS で表示方法が変わります。
193 +
194 + ### 設定の検証
195 +
196 + `navigation` の Option は Plugin の読み込み時に検証されます。
197 +
198 + 主な条件は次のとおりです。
199 +
200 + - `label` は空でない文字列
201 + - `href` は空でない文字列
202 + - `external` を指定する場合は `boolean`
203 + - `children` に祖先の項目を含めることはできない
204 +
205 + 不正な設定は Plugin の読み込み時にエラーになります。
206 +
207 + ### Plugin のページは自動追加されない
208 +
209 + Plugin は Vault の discoverable entries からリンクを導出します。Plugin が生成するページを自動で surface することはありません。
210 +
211 + たとえば、次のようなものは自動的には追加されません。
212 +
213 + - Search
214 + - Tag / Folder 一覧
215 + - Taxonomy のページ
216 + - Plugin の Page Type
217 + - Breadcrumbs
218 + - Backlinks
219 + - Related Posts
220 + - その他の Content graph 機能
221 +
222 + Plugin が作るページを表示したい場合は、そのページへのリンクを `navigation({ items })` に追加してください。
223 +
224 + Navigation と Plugin の役割の違いについては、[サイトのカスタマイズ](../guides/customizing-your-site.md#navigation-を変える) を参照してください。
225 +
226 + ### エクスポートされる helper と型
227 +
228 + `@riebeckite/plugin-navigation` は `navigation`、`buildNavigation`、`resolveSiteNavigation`、`NAVIGATION_PLUGIN_NAME`、描画 primitive の `SiteNav` と、型 `NavigationItem`、`NavigationOptions`、`SiteNavigation`、`SiteNavProps` をエクスポートします。
229 +
230 + ## Content の設定
231 +
232 + 通常は `content.directory` で Markdown などを読み込むディレクトリを指定します。
233 +
234 + ```ts id="dn44si"
235 + content: {
236 + directory: "content",
237 + }
238 + ```
239 +
240 + 標準的な Site なら、
241 +
242 + ```text id="w3ifap"
243 + my-site/
244 + ├─ app/
245 + ├─ content/
246 + ├─ public/
247 + ├─ package.json
248 + ├─ vite.config.ts
249 + └─ riebeckite.config.ts
250 + ```
251 +
252 + のような構成になります。
253 +
254 + ### 除外するファイル
255 +
256 + `exclude` を使うと、Content System に読み込ませないファイルを指定できます。
257 +
258 + ```ts id="4kv6vn"
259 + content: {
260 + directory: "content",
261 + exclude: [
262 + "drafts/**",
263 + "Templates/**",
264 + ],
265 + }
266 + ```
267 +
268 + `exclude` に一致したファイルは、コンテンツとして処理される前に除外されます。
269 +
270 + 内部では `isExcluded` がこの判定を行います。
271 +
272 + ### 公開条件
273 +
274 + どのコンテンツを公開するかは、既定では `publishStrategy` で設定します。
275 +
276 + ```ts id="hp9zx8"
277 + content: {
278 + filters: {
279 + publishStrategy: "explicit",
280 + },
281 + }
282 + ```
283 +
284 + 公開判定には frontmatter も利用されます。
285 +
286 + ```yaml id="osj0d5"
287 + ---
288 + publish: true
289 + ---
290 + ```
291 +
292 + 現在の公開状態は Core で一度だけ解決され、Plugin には次の manifest view として渡されます。
293 +
294 + | View | 含まれるもの | 用途 |
295 + | --- | --- | --- |
296 + | `manifest.publicEntries` | ルーティングできるページ。`public` と `unlisted` | ページ表示、SSG の path 列挙 |
297 + | `manifest.discoverableEntries` | 発見可能な `public` ページだけ | docs navigation、search、feed、sitemap、taxonomy、graph、backlinks、related/recent |
298 +
299 + frontmatter で明示的な公開状態を指定できます。
300 +
301 + | Frontmatter | 結果 |
302 + | --- | --- |
303 + | `visibility: public` | URL で表示でき、一覧や検索にも出る |
304 + | `visibility: unlisted` | URL を知っていれば表示できるが、一覧や検索には出ない |
305 + | `visibility: draft` | URL でも表示されず、一覧や検索にも出ない |
306 + | `publishAt: 2026-01-01T00:00:00.000Z` | build 時刻がその日時より前なら非公開、以後の build で `public` になる |
307 + | `visibility` / `publishAt` なし | `publishStrategy` に従う。`explicit` は `publish: true` が必要。`selective` は `private: true` と `draft: true` を除外する |
308 +
309 + `visibility` や `publishAt` が不正な場合、推測せず build を失敗させます。scheduled publishing は build 時刻だけで判定します。Riebeckite は runtime timer を起動しません。
310 +
311 + `exclude` と `publishStrategy` は似ていますが、役割が異なります。
312 +
313 + ```mermaid id="1t5jnq"
314 + flowchart LR
315 + Files["Files"]
316 + Exclude{"exclude ?"}
317 + Content["Content"]
318 + Publish{"Published ?"}
319 + Public["Public Content"]
320 + Private["Not Published"]
321 +
322 + Files --> Exclude
323 + Exclude -->|Yes| Skip["読み込まない"]
324 + Exclude -->|No| Content
325 + Content --> Publish
326 + Publish -->|Yes| Public
327 + Publish -->|No| Private
328 + ```
329 +
330 + `exclude` は **Content System に入れるか**、`publishStrategy` や `visibility` は **Site でどう扱うか**を決めます。`exclude` されたファイルは link resolution や graph、diagnostics にも現れません。一方、`draft`、`unlisted`、公開前の `publishAt` は raw manifest には残りますが、Core が route 用 view と discovery 用 view から適切に外します。
331 +
332 + ## ContentSource
333 +
334 + 通常は `content.directory` を使用しますが、独自の読み込み元を使用する場合は `content.source` を指定できます。
335 +
336 + ```text id="rbdlxj"
337 + content.directory
338 + ↓
339 + 標準 filesystem ContentSource
340 + ```
341 +
342 + または、
343 +
344 + ```text id="hrp9p5"
345 + content.source
346 + ↓
347 + 独自 ContentSource
348 + ```
349 +
350 + のどちらかです。
351 +
352 + 同じコンテンツに対して2つの reader を動かすための設定ではありません。
353 +
354 + `content.source` を指定する場合は、標準 filesystem reader を置き換えるものとして扱います。
355 +
356 + ## 3つの Root
357 +
358 + 外部 Vault や monorepo 構成を扱う場合に重要なのが、
359 +
360 + - `appRoot`
361 + - `configRoot`
362 + - `contentRoot`
363 +
364 + の違いです。
365 +
366 + ```mermaid id="w3x2dv"
367 + flowchart TD
368 + App["appRoot<br/>Site Application"]
369 + Config["configRoot<br/>Config の場所"]
370 + Content["contentRoot<br/>Content / Vault の場所"]
371 +
372 + App -->|"既定"| Config
373 + App -->|"content.directory を解決"| Content
374 + ```
375 +
376 + | 名前 | 何を表す? | 既定値 / 基準 |
377 + | --- | --- | --- |
378 + | `appRoot` | HonoX / Vite Site の root | Vite `root` |
379 + | `configRoot` | `riebeckite.config.*` を探す場所 | `appRoot` |
380 + | `contentRoot` | 実際にコンテンツを読む場所 | `path.resolve(appRoot, content.directory)` |
381 +
382 + この3つは別の役割を持ちます。
383 +
384 + ## appRoot
385 +
386 + `appRoot` は **Site Application の基準となるディレクトリ**です。
387 +
388 + たとえば、
389 +
390 + ```text id="xkjg5v"
391 + site/
392 + ├─ app/
393 + ├─ public/
394 + ├─ package.json
395 + ├─ vite.config.ts
396 + └─ riebeckite.config.ts
397 + ```
398 +
399 + なら通常、
400 +
401 + ```text id="2hwbbd"
402 + appRoot = site/
403 + ```
404 +
405 + です。
406 +
407 + `appRoot` は、
408 +
409 + - `app/`
410 + - `public/`
411 + - route
412 + - generated styles
413 + - Build 設定
414 +
415 + など Site Application の基準になります。
416 +
417 + ## configRoot
418 +
419 + `configRoot` は、
420 +
421 + ```text id="53pg8g"
422 + riebeckite.config.ts
423 + riebeckite.config.js
424 + riebeckite.config.mjs
425 + ```
426 +
427 + を探す基準です。
428 +
429 + 通常は `appRoot` と同じです。
430 +
431 + ```text id="7uqfyx"
432 + appRoot
433 + └─ riebeckite.config.ts
434 + ```
435 +
436 + 特殊な repository 構成で config を別の場所へ置く場合のみ変更します。
437 +
438 + **`configRoot` を変更しても `content.directory` の基準は変わらない**点に注意してください。
439 +
440 + ## contentRoot
441 +
442 + `contentRoot` は、実際に Markdown や asset を読み込む場所です。
443 +
444 + 相対 `content.directory` は常に `appRoot` を基準に解決されます。
445 +
446 + ```ts id="jgnfqm"
447 + content: {
448 + directory: "../vault",
449 + }
450 + ```
451 +
452 + なら、
453 +
454 + ```text id="y6zaw5"
455 + contentRoot
456 + = path.resolve(appRoot, "../vault")
457 + ```
458 +
459 + となります。
460 +
461 + ```mermaid id="uupc5x"
462 + flowchart LR
463 + App["appRoot<br/>workspace/site"]
464 + Directory["content.directory<br/>../vault"]
465 + Root["contentRoot<br/>workspace/vault"]
466 +
467 + App --> Directory
468 + Directory --> Root
469 + ```
470 +
471 + `process.cwd()` を基準にするわけではありません。
472 +
473 + そのため CLI を別の directory から実行しても、同じ Site Application を解決できれば同じ Vault を参照できます。
474 +
475 + ## 外部 Vault を使う
476 +
477 + Obsidian Vault を Site と独立して管理したい場合は、Site の外へ置く構成を推奨します。
478 +
479 + たとえば、
480 +
481 + ```text id="bgmthg"
482 + workspace/
483 + ├─ site/
484 + │ ├─ package.json
485 + │ ├─ vite.config.ts
486 + │ ├─ riebeckite.config.ts
487 + │ ├─ app/
488 + │ └─ public/
489 + │
490 + └─ vault/
491 + ├─ index.md
492 + ├─ notes/
493 + ├─ attachments/
494 + └─ media/
495 + ```
496 +
497 + という構成です。
498 +
499 + ```mermaid id="63njzu"
500 + flowchart LR
501 + Site["site/<br/>HonoX / Vite Application"]
502 + Config["riebeckite.config.ts"]
503 + Vault["vault/<br/>Obsidian Content"]
504 +
505 + Site --> Config
506 + Config -->|"content.directory = ../vault"| Vault
507 + ```
508 +
509 + Site と Vault の役割が明確に分離されます。
510 +
511 + ```text id="wx10hc"
512 + site/
513 + → Application
514 +
515 + vault/
516 + → Source Content
517 + ```
518 +
519 + Vault を Vite application root にする必要はありません。
520 +
521 + ## 外部 Vault の設定例
522 +
523 + ```ts id="p5vg19"
524 + // site/riebeckite.config.ts
525 +
526 + import { defineConfig } from "@riebeckite/core";
527 + import { attachment } from "@riebeckite/plugin-attachment";
528 + import { media } from "@riebeckite/plugin-media";
529 + import { obsidianMarkdown } from "@riebeckite/plugin-obsidian-markdown";
530 +
531 + export default defineConfig({
532 + site: {
533 + title: "My notes",
534 + },
535 +
536 + content: {
537 + directory: "../vault",
538 + exclude: [
539 + ".obsidian/**",
540 + "Templates/**",
541 + ],
542 + },
543 +
544 + plugins: [
545 + obsidianMarkdown(),
546 + media(),
547 + attachment(),
548 + ],
549 + });
550 + ```
551 +
552 + この場合、
553 +
554 + ```text id="9w7l08"
555 + appRoot
556 + = workspace/site
557 +
558 + content.directory
559 + = ../vault
560 +
561 + contentRoot
562 + = workspace/vault
563 + ```
564 +
565 + となります。
566 +
567 + 絶対パスを指定することもできます。
568 +
569 + ```ts id="vmohm9"
570 + content: {
571 + directory: "C:/Users/example/Documents/vault",
572 + }
573 + ```
574 +
575 + ただし絶対パスは開発 PC や CI で場所が変わると使えなくなるため、通常は Site からの相対パスを推奨します。
576 +
577 + ## `process.cwd()` に依存しない
578 +
579 + Content directory を次のように組み立てることは避けてください。
580 +
581 + ```ts id="i3cfla"
582 + directory: path.resolve(
583 + process.cwd(),
584 + "../vault",
585 + )
586 + ```
587 +
588 + CLI をどこから実行したかによって結果が変化するためです。
589 +
590 + また、
591 +
592 + ```text id="p2j2rb"
593 + appRoot = Vault
594 + ```
595 +
596 + とする必要もありません。
597 +
598 + Vault は **source data**、`appRoot` は **Site Application** です。
599 +
600 + ```mermaid id="xzz84j"
601 + flowchart LR
602 + Vault["Vault<br/>Source Data"]
603 + Site["Site<br/>Application"]
604 + Build["Riebeckite"]
605 +
606 + Vault --> Build
607 + Site --> Build
608 +
609 + Build --> Output["Generated Site"]
610 + ```
611 +
612 + この境界を維持してください。
613 +
614 + ## Application から ContentManager を使う
615 +
616 + 通常、HonoX Integration が `contentRoot` を自動的に解決します。解決済みの値は Framework 所有の module として公開されるため、Site が `riebeckite.config.ts` を読み直す必要はありません。
617 +
618 + ```ts id="veou0j"
619 + import { config } from "virtual:riebeckite/config";
620 + import { content } from "virtual:riebeckite/content";
621 + ```
622 +
623 + `config.content.directory` は絶対パスで、`content` はそれに結びついた `ContentManager` です。そのため、別の基準からもう一度 `path.resolve()` しないでください。この module は `riebeckiteVite()` が解決します。Vite の外で動く script(`tsx` で起動する Node script など)は `@riebeckite/honox/runtime` の `resolveHonoxConfig` で同じ値を解決できます。
624 +
625 + ```ts id="9pr6g1"
626 + import { fileURLToPath } from "node:url";
627 + import { resolveConfigModule } from "@riebeckite/core";
628 + import { resolveHonoxConfig } from "@riebeckite/honox/runtime";
629 + import * as rawConfigModule from "../riebeckite.config";
630 +
631 + const appRoot = fileURLToPath(new URL("../", import.meta.url));
632 + export const config = resolveHonoxConfig(
633 + resolveConfigModule(rawConfigModule),
634 + appRoot,
635 + );
636 + ```
637 +
638 + ```mermaid id="54gq7n"
639 + flowchart LR
640 + Relative["../vault"]
641 + Resolve["appRoot から一度だけ resolve"]
642 + Absolute["C:/.../vault"]
643 + Manager["ContentManager"]
644 +
645 + Relative --> Resolve
646 + Resolve --> Absolute
647 + Absolute --> Manager
648 + ```
649 +
650 + ## Attachment と Media
651 +
652 + Obsidian の attachment や media も `contentRoot` を基準に扱います。
653 +
654 + ここで attachment / media とは **Markdown でも画像でもないファイル**です。Vault 内の画像(png、jpg、svg など)は content image として別の扱いになるため、混同しないよう次の節で分けて説明します。
655 +
656 + たとえば Vault に、
657 +
658 + ```text id="qv3qcs"
659 + vault/
660 + ├─ notes/
661 + │ └─ report.md
662 + ├─ attachments/
663 + │ └─ report.pdf
664 + └─ media/
665 + └─ interview.mp3
666 + ```
667 +
668 + があるとします。
669 +
670 + Markdown では、
671 +
672 + ```md id="csovb2"
673 + ![[attachments/report.pdf]]
674 +
675 + ![[media/interview.mp3]]
676 + ```
677 +
678 + のように参照できます。
679 +
680 + `obsidianMarkdown()` はこれらを Vault からの相対 logical path として扱います。
681 +
682 + `attachment()` と `media()` が対応する embed を描画します。
683 +
684 + 公開 URL は安定した形式になります。
685 +
686 + ```text id="j9hboh"
687 + /assets/attachments/<Vault からの相対 logical path>
688 + ```
689 +
690 + たとえば、
691 +
692 + ```text id="pfr5c3"
693 + attachments/report.pdf
694 +
695 + ↓
696 +
697 + /assets/attachments/attachments/report.pdf
698 + ```
699 +
700 + のように logical path を維持します。
701 +
702 + ## Asset URL と実ファイルは別
703 +
704 + ここは特に重要です。
705 +
706 + URL を生成することと、そのファイルが Site へ公開されることは別のことです。
707 +
708 + Asset は次の3種類に分かれ、公開を担当する場所も異なります。
709 +
710 + | 種類 | 対象 | 公開 URL | 公開を担当する場所 |
711 + | --- | --- | --- | --- |
712 + | Content image | Vault 内の画像 | `/<Vault からの相対 logical path>` | Riebeckite の build |
713 + | Attachment / Media | Markdown でも画像でもないファイル | `/assets/attachments/<Vault からの相対 logical path>` | Site Application |
714 + | Static asset | Site Application 自身が管理するファイル | `/` 配下 | Vite の `public/` |
715 +
716 + ```mermaid id="otcz8u"
717 + flowchart LR
718 + Vault["Vault"]
719 + Image["Content image"]
720 + Attach["Attachment / Media"]
721 +
722 + Vault --> Image
723 + Vault --> Attach
724 +
725 + Image -->|"build が書き出す"| Output["Build Output"]
726 + Attach -->|"URL だけ生成"| Public["public/"]
727 + Public --> Output
728 + ```
729 +
730 + ### Content image は build で公開される
731 +
732 + `obsidianMarkdown()` は、公開ページから参照されている image を build の出力として書き出します。
733 +
734 + Site Application が何もしなくても、その image は build output に含まれ、生成された URL から取得できます。
735 +
736 + たとえば、
737 +
738 + ```text id="b1t4hs"
739 + assets/logo.png
740 +
741 + ↓
742 +
743 + /assets/logo.png
744 + ```
745 +
746 + という論理 path をそのまま公開します。
747 +
748 + 開発サーバーでも、同じ論理 path のまま Content から直接配信されます。
749 +
750 + 参照されていない image、非公開ページからの image は書き出されません。どの image を書き出すかは公開ページと参照関係から決まります。
751 +
752 + ### Attachment と Media の公開は Site Application の担当
753 +
754 + Attachment と Media については、URL を生成しただけでは実ファイルは公開されません。
755 +
756 + Plugin が、
757 +
758 + ```text id="8m59d5"
759 + /assets/attachments/attachments/report.pdf
760 + ```
761 +
762 + という URL を生成したからといって、`report.pdf` が自動的に Vite の `public/` へコピーされるわけではありません。
763 +
764 + Site Application は、公開する必要がある asset だけを、
765 +
766 + ```text id="5jd2hq"
767 + public/assets/attachments/
768 + ```
769 +
770 + へコピーしてください。
771 +
772 + その際も Vault からの相対 logical path を維持します。
773 +
774 + 参照 Application の `build_images.ts` は、実際に参照されている attachment だけを差分コピーする実装例です。
775 +
776 + ### `public/` を使う Static asset
777 +
778 + `public/` は Site Application 自身が管理する asset 用の directory です。
779 +
780 + `public/` 以下のファイルは、Vite の build でそのまま build output へコピーされます。
781 +
782 + Content から取り込んだ画像や attachment をここへまとめて置くのではなく、上記のように公開する対象を絞って配置します。
783 +
784 + ## Vault 全体を公開しない
785 +
786 + 次のような実装は避けてください。
787 +
788 + ```text id="wp29zc"
789 + vault/**
790 + ↓
791 + public/**
792 + ```
793 +
794 + Vault 全体をそのまま `public/` へコピーすると、
795 +
796 + - 非公開の記事
797 + - 未公開ページからしか参照されていない画像
798 + - 未参照の attachment
799 + - `.obsidian/` の metadata
800 + - 公開するつもりのないファイル
801 +
802 + まで公開される可能性があります。
803 +
804 + ```mermaid id="mrfz4d"
805 + flowchart TD
806 + Vault["Vault"]
807 +
808 + Vault --> Published["公開対象Content"]
809 + Vault --> UsedAssets["参照されているAssets"]
810 + Vault --> Private["非公開Content"]
811 + Vault --> Metadata[".obsidian / Metadata"]
812 +
813 + Published --> Public["Public Site"]
814 + UsedAssets --> Public
815 +
816 + Private -. "公開しない" .-> Public
817 + Metadata -. "公開しない" .-> Public
818 + ```
819 +
820 + **必要な asset だけを公開する**ことが重要です。
821 +
822 + Content image は build が公開対象を判断します。Attachment と Media をどの範囲で公開するかは Site Application 側の責務です。
823 +
824 + ## Build cache
825 +
826 + `cache` は build 時に使う永続 cache の設定です。省略可能で、既定では Integration が適切な directory を決めます。
827 +
828 + | Field | 既定値 | 説明 |
829 + | --- | --- | --- |
830 + | `cache.enabled` | `true` | `false` にすると永続 cache を無効化し、毎回すべてを再生成します。 |
831 + | `cache.directory` | `<buildDirectory>/cache` | cache の保存先を上書きします。cache を別の場所へ移したり共有したい場合に指定します。未指定なら Integration の既定値を使います。 |
832 +
833 + cache には build 間で再利用する処理済み Content や Plugin の結果が入ります。無効化や保存先の変更は build の速度にだけ影響し、出力は変わりません。cold build でも同じ結果になります。
834 +
835 + ## Plugin の設定
836 +
837 + Plugin は `plugins` に指定します。
838 +
839 + ```ts id="93f0rv"
840 + plugins: [
841 + obsidianMarkdown(),
842 + media(),
843 + attachment(),
844 + ]
845 + ```
846 +
847 + 条件によって Plugin を切り替えることもできます。
848 +
849 + `PluginInput` では、
850 +
851 + ```text id="r4h4s2"
852 + false
853 + null
854 + undefined
855 + ```
856 +
857 + を無効な Plugin input として扱えます。
858 +
859 + たとえば、
860 +
861 + ```ts id="7tk1jx"
862 + plugins: [
863 + enableAnalytics && analytics(),
864 + ]
865 + ```
866 +
867 + のような conditional configuration が可能です。
868 +
869 + Config resolve 時に無効な input は除外され、有効な Plugin は安定した順序で整理されます。
870 +
871 + その後 capability の整合性が検証されます。
872 +
873 + ## Theme の設定
874 +
875 + Theme は `theme` で設定します。
876 +
877 + ```ts id="pgj2be"
878 + theme: {
879 + colorMode: "system",
880 + articleLayout: "article",
881 + }
882 + ```
883 +
884 + raw configuration または宣言済み Theme を利用できます。
885 +
886 + Theme は presentation の設定です。
887 +
888 + HonoX、Vite、Cloudflare など Integration 固有の設定を Core configuration へ入れないでください。
889 +
890 + ```mermaid id="m54zmx"
891 + flowchart TD
892 + Config["Riebeckite Config"]
893 +
894 + Config --> Site["Site"]
895 + Config --> Content["Content"]
896 + Config --> Markdown["Markdown"]
897 + Config --> Theme["Theme"]
898 + Config --> Plugins["Plugins"]
899 +
900 + Framework["HonoX / Vite / Platform"]
901 + Framework --> Integration["Integration Config"]
902 +
903 + Integration -. "Core Configへ混ぜない" .-> Config
904 + ```
905 +
906 + ## Config の検証
907 +
908 + Configuration に問題がある場合は `ConfigValidationError` として報告されます。
909 +
910 + 不正な Configuration を無視してそのまま起動するのではなく、早い段階で失敗させます。
911 +
912 + Config を変更した後は、
913 +
914 + ```sh id="khhx15"
915 + npm exec riebeckite check
916 + ```
917 +
918 + を実行してください。
919 +
920 + ## 外部 Vault のトラブルシュート
921 +
922 + 外部 Vault や複雑な directory 構成を使っている場合は、次の順番で確認すると原因を切り分けやすくなります。
923 +
924 + ```mermaid id="5yfgda"
925 + flowchart LR
926 + Check["1. check"]
927 + Doctor["2. doctor"]
928 + Config["3. inspect config"]
929 + Content["4. inspect content --list"]
930 + Build["5. build"]
931 +
932 + Check --> Doctor
933 + Doctor --> Config
934 + Config --> Content
935 + Content --> Build
936 + ```
937 +
938 + ### 1. Config を検証する
939 +
940 + ```sh id="7x4ypg"
941 + npm exec riebeckite check
942 + ```
943 +
944 + Config と Plugin contract が正しいか確認します。
945 +
946 + ### 2. Content Source を診断する
947 +
948 + ```sh id="t7e22k"
949 + npm exec riebeckite doctor
950 + ```
951 +
952 + filesystem content source が存在しない、読み込めないなどの問題を確認します。
953 +
954 + ### 3. 解決された Directory を確認する
955 +
956 + ```sh id="rgnpgo"
957 + npm exec riebeckite inspect config
958 + ```
959 +
960 + `content.directory` が期待する絶対 path に解決されているか確認します。
961 +
962 + ### 4. Content を確認する
963 +
964 + ```sh id="4ssq64"
965 + npm exec -- riebeckite inspect content --list
966 + ```
967 +
968 + WikiLink や embed を調査する前に、期待する logical path と content が認識されていることを確認します。
969 +
970 + ### 5. 実際に Build する
971 +
972 + ```sh id="9wnm73"
973 + npm exec riebeckite build
974 + ```
975 +
976 + 最後に Integration、SSG、route rendering まで含めて確認します。
977 +
978 + ## Config を Site の外へ置く
979 +
980 + 通常、
981 +
982 + ```text id="4p7m8h"
983 + appRoot
984 + = configRoot
985 + ```
986 +
987 + ですが、意図的に `riebeckite.config.ts` を別の directory に置くこともできます。
988 +
989 + その場合は `riebeckiteVite()` に `configRoot` を指定します。
990 +
991 + ただし、
992 +
993 + ```text id="8y2qf9"
994 + appRoot
995 + → Site Application
996 +
997 + configRoot
998 + → Config
999 +
1000 + contentRoot
1001 + → Content / Vault
1002 + ```
1003 +
1004 + という役割は変わりません。
1005 +
1006 + 特に `appRoot` を Vault 側へ変更しないでください。
1007 +
1008 + 相対 `content.directory` は、`configRoot` ではなく引き続き **`appRoot` を基準**に指定します。
1009 +
1010 + ## まとめ
1011 +
1012 + Configuration では、Site と Content の場所を混同しないことが重要です。
1013 +
1014 + ```mermaid id="zzmq0d"
1015 + flowchart LR
1016 + App["appRoot<br/>Siteはどこ?"]
1017 + Config["configRoot<br/>Configはどこ?"]
1018 + Content["contentRoot<br/>Contentはどこ?"]
1019 +
1020 + Config --> Resolve["Configuration Resolution"]
1021 + App --> Resolve
1022 + Resolve --> Content
1023 +
1024 + Content --> Manager["Content System"]
1025 + Manager --> Build["Build"]
1026 + ```
1027 +
1028 + 基本的には、
1029 +
1030 + ```text id="htn7yn"
1031 + appRoot
1032 + = Site Application の場所
1033 +
1034 + configRoot
1035 + = Config の場所
1036 +
1037 + contentRoot
1038 + = Markdown / Vault の場所
1039 + ```
1040 +
1041 + と覚えておけば十分です。
1042 +
1043 + 通常の Site では3つの違いを意識する必要はほとんどありません。
1044 +
1045 + 外部 Vault、別 repository の Content、特殊な monorepo 構成を使う場合だけ、この境界を意識してください。
1046 +
1047 + Config の変更後は `riebeckite check`、実際にどう解決されたか確認したい場合は `riebeckite inspect config` を使用してください。
1048 +

Local Graph

Nearby Notes

Open in Explorer →
configurationwriting-content
CurrentOutgoingBacklink