AI駆動開発でブログをNext.jsからAstroにリプレイスした
目次
- なぜ作り直したか
- 構成
- 導入手順
- 1. Astro を静的サイトとして作る
- 2. Cloudflare Workers にデプロイする
- 3. 1 ページだけ Worker で描画してキャッシュする
- 4. Sveltia CMS を置く
- 5. GitHub の OAuth App と認証の Worker を作る
- 6. DNS を Cloudflare に切り替える
- 7. プレビュー URL を Cloudflare Access で守る
- 8. 管理画面の本体を自分で配り、CSP を付ける
- 移行: Claude に旧サイトをスクレイピングさせて Markdown を作る
- 公開ページの __NEXT_DATA__ を読む
- 本文を変換する
- 結果
- ハマったところ
- Workers Builds が .tool-versions を asdf で読もうとする
- ビルドのキャッシュでリンクカードの画像が落ちる
- 応答しないサイトの OGP を 5 分待つ
- 管理画面の「公開」を押してもマージされない
- アセットの画面からの画像のアップロードが失敗する
- 結果
- おわりに
microCMS + Next.js + Vercel で作っていたこのブログを、Astro + Sveltia CMS + Cloudflare Workers に作り直しました。
この記事では、新しい構成の導入手順と、旧ブログの記事を Claude(Claude Code)にスクレイピングさせて Markdown に移した方法をまとめます。
なぜ作り直したか
旧ブログは 2021 年に作ったもので、次のような構成でした。
- フレームワーク: Next.js 12(SSG)
- UI: Chakra UI
- CMS: microCMS(本文は MDX のテキスト)
- ホスティング: Vercel
数年放っておいたあいだに Next.js も Chakra UI も大きく変わり、手を入れるには依存の更新から始める必要がありました。作り直すにあたって決めた目的は 3 つです。
- 表示を速くする: 記事を読むだけのページに、クライアントの JavaScript はほとんど要らない
- 構造化データ(JSON-LD)を充実させる: 検索エンジンと AI の要約に、記事の中身を正しく伝える
- 新しい道具に触る: Cloudflare Workers・Astro・Vite+ のキャッチアップ、Claude Codeの素振り (普段はCursor)
構成
| 役割 | 使ったもの |
|---|---|
| フレームワーク | Astro 7(SSG / 1 ページだけ Worker で描画) |
| 記事 | リポジトリの Markdown |
| 管理画面 | Sveltia CMS(Git-based) |
| 配信 | Cloudflare Workers の静的アセット + Workers Builds |
| CMS の認証 | sveltia-cms-auth(別の Cloudflare Worker) |
| ツールチェーン | Vite+(検査・テスト・フォーマット)、pnpm、mise |
記事の正本は GitHub のリポジトリの Markdown です。管理画面で書いた記事も、GitHub にコミットされるだけなので、データベースも外部の CMS もありません。
導入手順
1. Astro を静的サイトとして作る
旧ブログの記事の URL(/articles/<id>、末尾スラッシュなし)をそのまま保ちたかったので、Astro の出力を次のようにしました。
export default defineConfig({ site: "https://tkc310.com", output: "static", trailingSlash: "never", build: { // /articles/<id>.html を出し、Workers の静的アセットが /articles/<id> を 200 で返す format: "file", },});build.format: "file" にすると、/articles/foo/index.html ではなく /articles/foo.html が出ます。Cloudflare Workers の静的アセットは、/articles/foo へのリクエストに foo.html をリダイレクトなしで返してくれます。
記事はコンテンツコレクションで読み、frontmatter を Zod のスキーマで検査します。項目が足りない・余計な項目がある記事は、ビルドで止まります。
const posts = defineCollection({ loader: glob({ base: "./src/content/posts", pattern: "**/*.md" }), schema: z .object({ title: z.string().min(1).max(110), description: z.string().min(1).max(200), pubDate: z.coerce.date(), updatedDate: z.coerce.date().optional(), category: reference("categories"), draft: z.boolean().default(false), }) .strict(),});Markdown の処理は unified() にして、リンクカード(remark-link-card-plus)・コードブロックの装飾(Expressive Code)・外部リンクを別タブで開く設定(rehype-external-links)を入れています。リンクカードの OGP はビルド時に取るので、読み込んだあとでレイアウトがずれることはありません。
2. Cloudflare Workers にデプロイする
Cloudflare のダッシュボードで Worker を作り、GitHub のリポジトリとつなぎます(Workers Builds)。
| 項目 | 値 |
|---|---|
| ビルドコマンド | pnpm run build |
| デプロイコマンド | npx wrangler deploy(既定のまま) |
| プレビュービルド | 有効 |
プレビュービルドを有効にすると、main 以外のブランチを push するたびに、ブランチごとのプレビュー URL にデプロイされます。下書きの記事はプレビュー URL にだけ出し、本番には出しません。Workers Builds がビルド中に入れる WORKERS_CI_BRANCH を見て切り替えています。
プレビュー URL はそのままだと誰でも開けるので、あとで Cloudflare Access で認証をかけました(手順 7)。
export function shouldShowDrafts(env: Record<string, string | undefined>): boolean { if (env.SHOW_DRAFTS === "true") return true; if (env.SHOW_DRAFTS === "false") return false; const branch = env.WORKERS_CI_BRANCH; return branch !== undefined && branch !== "" && branch !== "main";}wrangler.jsonc に書くのは名前と静的アセットの扱いくらいです。Worker の入口とアセットの置き場所は、Astro の Cloudflare アダプターがビルドのときに足してくれます。
{ "name": "astro-blog", "compatibility_date": "2026-10-19", "assets": { "html_handling": "auto-trailing-slash", }, "observability": { "enabled": true },}旧ブログの URL のうち、無くしたページ(タグ・ノート・寄付など)は public/_redirects で 301 にしました。旧サイトのサイトマップにあった URL を一覧にしておき、すべてが 200 か 301 を返すことを CI で確かめています。
3. 1 ページだけ Worker で描画してキャッシュする
ほぼ静的サイトですが、私について(/about)だけは例外です。LAPRAS の公開 API から最近の活動を取って載せたかったのに、ブラウザから fetch すると CORS で止まってしまいました。
そこで /about だけを prerender = false にしてサーバ(Worker)から API を呼び、Astro のルートキャッシュで Cloudflare にキャッシュしています。Vercel の ISR と同じ動き(期限が切れたら、古いものを返しながら裏で描き直す)です。
---export const prerender = false;
const activities = await fetchActivities("tkc310", { limit: 10 });
// 開発中は cache.enabled が false なので、毎回描画されるif (Astro.cache.enabled) { Astro.cache.set( activities.ok ? { maxAge: 60 * 60, swr: 60 * 60 * 24, tags: ["lapras"] } : { maxAge: 60 * 5, tags: ["lapras"] }, );}---import cloudflare from "@astrojs/cloudflare";import { cacheCloudflare } from "@astrojs/cloudflare/cache";
export default defineConfig({ output: "static", adapter: cloudflare({ prerenderEnvironment: "node", imageService: "compile" }), session: false, // セッションを使わないので KV を作らない cache: { provider: cacheCloudflare() },});LAPRAS の API は 3 秒で打ち切り、取れなくてもプロフィールへのリンクを出してページを返すようにしています。
4. Sveltia CMS を置く
Sveltia CMS は、Decap CMS(旧 Netlify CMS)の後継として作り直された Git-based の CMS です。管理画面のページと設定ファイルを置くだけで、/admin が管理画面になります。サーバもデータベースも要らず、ブラウザの中で動く JavaScript が GitHub の API でリポジトリの Markdown を直接読み書きします。
はじめは公式の手順どおり、public/admin/index.html から unpkg の本体(<script src="https://unpkg.com/@sveltia/cms@0.228.0/dist/sveltia-cms.js">)を読み込んでいました。今は npm のパッケージを Astro でまとめて、自分のサイトから配っています(理由は手順 8)。
<!doctype html><html lang="ja"> <head> <meta charset="utf-8" /> <meta name="robots" content="noindex" /> <title>管理画面 | tkc310.com</title> <link href="/admin/config.yml" type="application/yaml" rel="cms-config-url" /> </head> <body> <script> import { init } from "@sveltia/cms";
void init(); </script> </body></html>設定では、保存の方式を Editorial Workflow にしています。管理画面で保存すると、記事ごとのブランチとプルリクエストができます。プレビュー URL で見た目を確かめてからマージすると公開される流れです。
backend: name: github repo: tkc310/astro-blog branch: main base_url: https://astro-blog-sveltia-cms-auth.tkc310.workers.devpublish_mode: editorial_workflowsite_url: https://tkc310.com
collections: - name: posts label: 記事 folder: src/content/posts extension: md format: yaml-frontmatter create: true media_folder: /src/assets/posts/{{filename}} public_folder: ../../assets/posts/{{filename}} fields: - { name: title, label: タイトル, widget: string } - { name: description, label: 説明, widget: text } - { name: pubDate, label: 公開日, widget: datetime } - { name: category, label: カテゴリ, widget: relation, collection: categories, value_field: "{{slug}}", display_fields: [name] } - { name: draft, label: 下書き, widget: boolean, required: false } - { name: body, label: 本文, widget: markdown }frontmatter の項目は、Astro の Zod スキーマと Sveltia の設定の 2 か所に書くことになります。ずれるとビルドで止まるので、両方がそろっているかをテストで確かめています。
5. GitHub の OAuth App と認証の Worker を作る
Sveltia CMS で GitHub にログインするには、OAuth のやり取りを中継するサーバが要ります。公式の sveltia-cms-auth を、ブログとは別の Cloudflare Worker として動かしました。
- 認証の Worker をデプロイする: README の「Deploy to Cloudflare Workers」ボタンから作ります。ビルドコマンドは空欄、プレビュービルドと Cloudflare Access は不要です。できた Worker の URL(
https://<名前>.<サブドメイン>.workers.dev)を控えます - GitHub の OAuth App を作る: GitHub の Settings → Developer settings → OAuth Apps から作ります
- Redirect URIs:
<Worker の URL>/callback - Allow wildcard matching: オフ(戻り先は登録した 1 件だけなので不要。認証の Worker は GitHub に
redirect_uriを送らず、GitHub は登録した URL に戻す) - Enable Device Flow: オフ
- Redirect URIs:
- Worker に値を入れる: Worker の Settings → Variables and Secrets に、
GITHUB_CLIENT_ID・GITHUB_CLIENT_SECRET(シークレットとして)・ALLOWED_DOMAINS(tkc310.com)を入れます - CMS の設定に Worker の URL を書く:
config.ymlのbackend.base_urlに書きます
2026 年 8 月から、GitHub の OAuth App では「Authorization callback URL」が「Redirect URIs」になり、複数の URL を登録できるようになりました。「Allow wildcard matching」は、以前は URL が 1 つのアプリで暗黙にオンだったものが見えるようになっただけなので、要らなければオフにしておくのがよさそうです。
6. DNS を Cloudflare に切り替える
ドメインはお名前.com で取っていて、ネームサーバーは Vercel を向いていました。切り替えは次の順で行いました。
- Cloudflare にドメインを足し、Vercel の DNS から読み込んだレコードを整理する(このときは Vercel を向いた A レコードをプロキシなしで残す)
- お名前.com のネームサーバーを Cloudflare のものに変える(この間も旧サイトが見える)
- ゾーンが有効になったら、A レコードを消し、Worker のカスタムドメインに
tkc310.comを足す wwwは Redirect Rule でtkc310.comへ 301 にする- 旧ブログの URL の一覧がすべて 200 か 301 を返すことを確かめる
7. プレビュー URL を Cloudflare Access で守る
プレビュー URL(<ブランチ名>-astro-blog.<サブドメイン>.workers.dev)には下書きの記事も出るので、そのままだと公開前の記事を誰でも読めてしまいます。Cloudflare Access で、プレビュー URL にだけ認証をかけました。
- Workers & Pages で Worker を開き、Access タブの Protect this Worker behind Access を選ぶ
- 範囲は Previews only にする
- 認証のポリシーを選ぶ(1 人で使うなら、Cloudflare のアカウントのメンバーだけを通すのが簡単)
- Apply Access を押す
範囲を All traffic にすると、Worker につながるすべてのドメインが対象になり、カスタムドメインの本番(tkc310.com)まで認証が必要になります。プレビューだけを守りたいときは Previews only を選びます。
これでプレビュー URL を開くと、Cloudflare のログイン画面が出るようになりました。気をつけたのは次の 3 点です。
- ブランチの URL は 1 つのラベル: プレビュー URL は
cms-posts-foo-astro-blog.<サブドメイン>.workers.devのように、ブランチ名と Worker の名前が 1 つのラベルにつながります。Zero Trust 側で自分でアプリを作り、*.astro-blog.<サブドメイン>.workers.devのようなホスト名を指定しても当たりません。Worker の Access タブから設定するのが確実です - ポリシーの動作は Allow: Bypass にすると、誰でも認証なしで通ります
- CMS の認証の Worker には掛けない: 管理画面のログイン(GitHub)の途中で Access の画面が挟まると、ログインできなくなります
CI の Lighthouse とスクリーンショットは、CI の中で astro preview を起動して localhost に対して動かしているので、Access の影響を受けません。
8. 管理画面の本体を自分で配り、CSP を付ける
管理画面のスクリプトは、ログインした人の GitHub のアクセストークン(repo 権限)を持って動きます。unpkg から本体を読み込む形だと、版を固定していても、配信元が乗っ取られたり途中で中身が変わったりしたときに、ブラウザはそれを確かめずに実行してしまいます。差し替えられた本体は、トークンを外に送ることも、リポジトリに書き込んで本番に出すこともできます。
さらに CDN 版の本体は、日本語の翻訳やコードのハイライト(Shiki)なども実行中に unpkg から追加で読み込むので、unpkg が止まると管理画面も開けません。
そこで次のように変えました。
@sveltia/cmsをpackage.jsonに正確な版で入れる。中身はロックファイルのハッシュで確かめられ、版の更新もほかの依存と同じく PR と CI を通る- 管理画面を Astro のページ(
src/pages/admin.astro)にし、パッケージのinit()を呼ぶ。Astro(Vite)がまとめて/_astro/から配るので、翻訳なども含めて外部の CDN を読まない public/_headersで/adminに Content-Security-Policy を付け、スクリプトは自分のサイトのものだけ、通信は自分のサイトと GitHub だけに絞る
/admin Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' blob: data: https://avatars.githubusercontent.com https://*.githubusercontent.com; connect-src 'self' blob: data: https://api.github.com https://*.githubusercontent.com https://www.githubstatus.com; frame-ancestors 'none'; ...script-src は 'self' だけで、インラインのスクリプトも許していません。Astro は小さなスクリプトを HTML に埋め込むことがありますが、管理画面のスクリプトはパッケージを読み込むので埋め込まれず、別のファイルとして配られます。スタイルだけは、Sveltia CMS が部品のスタイルを差し込むので 'unsafe-inline' を許しています。
壊れていないことは CI で確かめています。ビルド結果の検査で管理画面に外部やインラインのスクリプトが無いことを、スクリーンショットの撮影で管理画面を実際に開き、CSP のヘッダーが付いていること・CSP に止められた読み込みが無いこと・ログインの画面まで描かれることを見ています。
ログインから記事の保存までの流れをシーケンス図付きでリポジトリの文書にまとめました。
CSP以外にも色々とできそうです。
移行: Claude に旧サイトをスクレイピングさせて Markdown を作る
記事の移行は、Claude Code に任せました。はじめは microCMS の API から記事を取る予定でしたが、API キーをエージェントの作業環境に渡したくなかったので、公開しているサイトから取る方法に変えています。
公開ページの __NEXT_DATA__ を読む
旧サイトは Next.js の SSG(getStaticProps)で作っていたので、各ページの HTML に __NEXT_DATA__ として、microCMS の API の応答がそのまま埋め込まれています。HTML から本文を逆算しなくても、記事の本文(MDX のテキスト)・カテゴリ・日付・抜粋がそのまま取れます。
これに気づいたのは Claude でした。旧ブログのリポジトリと公開ページを読ませたところ、API を使わない方法としてこれを提案してきました。
async function nextData(path: string): Promise<Record<string, unknown>> { const response = await fetch(`${LEGACY_SITE}${path}`); if (!response.ok) throw new Error(`${path}: ${response.status}`); const html = await response.text(); const json = /<script id="__NEXT_DATA__" type="application\/json">([\s\S]*?)<\/script>/.exec( html, )?.[1]; if (!json) throw new Error(`${path}: __NEXT_DATA__ がありません`); return (JSON.parse(json) as { props: { pageProps: Record<string, unknown> } }).props.pageProps;}記事の ID は、トップと /articles/page/<n> の一覧を順にたどって集めます。サイトマップには 16 本中 10 本しか載っていなかったので使えませんでした。
本文を変換する
旧ブログは、描画の前に本文を書き換えていました。新しいブログで同じ見た目になるよう、移行のときに Markdown を直します。変換のルールは先に指示書に書き、Claude にスクリプトとテストを書かせました。
| 旧ブログでの書き方 | 移行後 |
|---|---|
<https://...> だけの行(実行時に OGP を取るリンクカード) | URL だけの行(ビルド時にリンクカードになる) |
| microCMS の画像の URL | 画像をダウンロードしてリポジトリに置き、相対パスに直す |
<img src="..." alt="..." /> |  に直す |
本文中の #(h1) | 全体を 1 段下げる(ページの h1 はタイトルだけ) |
| 言語名の無いコードブロック | text を付けて一覧に出す |
| JSX のコンポーネント | 自動では直さず、一覧に出して下書きにする |
スクリプトは何度実行しても同じ結果になるようにしてあります。出力先を消してから書き、最後に整形までするので、旧サイトに新しい記事が無ければ差分は出ません。切り替えの直前にもう一度実行して、取りこぼしが無いことを確かめました。
結果
記事 15 本・カテゴリ 5 個・画像 12 枚を移せました。手で直す必要があったのは、JSX のコンポーネントが残ったテスト用の記事 1 本だけで、これは移さないことにしました。
AI に任せてよかったのは、移行のような「一度きりだが、正確さが要る」作業を、指示書とテスト付きのスクリプトにして残してくれたことです。手作業でコピーしていたら、画像の取りこぼしや見出しのレベルのずれに気づけなかったと思います。
一方で、旧サイトの作り(__NEXT_DATA__)に頼る方法なので、旧サイトを消したら再実行できません。移行が終わるまで、旧サイトは残しておく必要があります。
ハマったところ
Workers Builds が .tool-versions を asdf で読もうとする
ツールの版を .tool-versions に書いていたところ、Workers Builds がこれを asdf で読もうとし、pnpm の行を入れられずにビルドが止まりました。mise には mise.toml、GitHub Actions と Workers Builds には .node-version で版を渡すようにして解決しました。
ビルドのキャッシュでリンクカードの画像が落ちる
本番でリンクカードの画像だけが 404 になりました。Workers Builds が Astro のキャッシュを戻してビルドするため、描画済みの本文が使い回され、画像をダウンロードする処理が走っていなかったのが原因です。ビルドを astro build --force にして、キャッシュを捨てて回すようにしました。
応答しないサイトの OGP を 5 分待つ
CI のビルドが 1 分から 6 分に延びました。リンクカードのプラグインが、OGP を取るライブラリに秒で渡すべきタイムアウトをミリ秒で渡していて、応答しないサイトを Node の既定の 5 分まで待っていました。pnpm の patch で 10 秒に直しています。
管理画面の「公開」を押してもマージされない
管理画面で記事を保存するとプルリクエストができ、「公開」を押すとそれが main にマージされる……はずが、「公開」が失敗しました。原因は 2 つありました。
- マージの方式が合っていない:
mainは保護(ruleset)でスクイズマージだけを許し、履歴を一直線に保っています。Sveltia CMS は設定が無いと普通のマージ(マージコミット)を試みるので、GitHub に拒まれていました - 必須の検査が終わっていない: CI の
checkを必須にしているので、保存の直後で検査が実行中のあいだは、GitHub がマージを受け付けません
対策は次の 3 つです。
config.ymlのbackendにsquash_merges: trueを足し、「公開」をスクイズマージにする- Sveltia CMS を 0.248.0 以上に上げる。この版から、「公開」を押したときに必須の検査が実行中なだけなら、GitHub の自動マージ(auto-merge)を予約するようになりました。検査が通った時点で GitHub がマージするので、管理画面を閉じてもかまいません
- リポジトリの設定で「Allow auto-merge」を有効にする
backend: name: github repo: tkc310/astro-blog branch: main base_url: https://astro-blog-sveltia-cms-auth.tkc310.workers.dev squash_merges: truegh api -X PATCH repos/tkc310/astro-blog -F allow_auto_merge=truecheck を必須から外せば「公開」はすぐ通りますが、壊れた記事がそのまま本番に出てしまいます。実際、記事が 1 本増えただけで記事の一覧に 4 ページ目ができ、旧ブログ用の 301 がそのページを隠していたことがあり、ビルド結果の検査がそれを見つけてくれました。検査は残し、待つのは GitHub に任せる形にしました。
そのかわり、記事と記事の画像(src/content/・src/assets/posts/)だけを変えたプルリクエストでは、検査を最低限に絞っています。型・lint・テストはコードの検査なので飛ばし、ビルドとビルド結果の検査だけを回します。Lighthouse とスクリーンショットの撮影も回しません。必須の check は、飛ばしたステップがあっても結果が付くので、自動マージは止まりません。
ちなみに、はじめは Sveltia CMS の最新の 0.248.1 に上げようとしたのですが、pnpm 12 の minimumReleaseAge(公開から 1 日たっていない版は入れない)に止められました。サプライチェーン攻撃への備えとしてありがたい既定値なので、そのまま 0.248.0 にしています。
アセットの画面からの画像のアップロードが失敗する
管理画面の「すべてのアセット」「グローバルアセット」から画像を上げると失敗しました。Sveltia CMS のソースを読むと、アセットの画面からのアップロードは Editorial Workflow を通らず、main へ直接コミットしようとしていました。main はプルリクエストを必須にしているので、拒まれて当然です。
記事の編集画面から本文に画像を足すと、記事のブランチとプルリクエストに入るので、こちらは問題なく保存できます。置き場所も記事ごとのフォルダ(src/assets/posts/<記事の ID>/)になるので、画像は記事の編集画面から足すことにしました。
管理画面の仕組み(ログインから保存・公開までの流れ)は、こちらにまとめています。
結果
本番の Lighthouse(モバイル)は、測った 5 ページとも、性能・アクセシビリティ・ベストプラクティス・SEO の 4 項目が 100 点になりました。
CI でも Lighthouse を回していて、点が下がる変更はマージできないようにしています。完成したあとにClaudeに指示するだけで、30~1時間ほどで仕組みも含めてチューニングされていました。

おわりに
作り直しのほとんどは、Claude Code と会話しながら進めました。
Opus5.5を使っていますが、コスト的もパフォーマンスも今までと次元が違くて驚いています。
いままで、Cursorから色々なモデルを使っていましたが、外部APIと定額サブスクリプションでは圧倒的に後者が有利だとやっと気づきました。
仕事でもレガシーコードやレビューなどは安いモデルだと使い物にならないため、ClaudeやCodexのモデルを使っていましたが、忙しいときは半月で上限までクレジットを消費していました。
GrokBot / Cursor Projects / Cloude Agents / pstackなど、エコシステムはまだCursorに利がありますが、モデルの賢さ的に併用するしかなさそうです。
しかしながら、そんなにスキルやコマンドなどを知らなくても、対話的にプロンプトを与えるだけでやりたい事をシニアクラスの品質で出力してくれるので本当に驚きです。
エンジニアが不要になるにはもう少し掛かるかも知れませんが、まず急激にやることは変わっていきそうです。
このリプレイスもこの記事の通り、AIが全てのコードを書いており、私はインフラ設定とレビューとプロンプトやコマンドを叩いていただけです。
こだわったりキャッチアップしながらでなければ、マイグレーション含めて数時間で完成していたと思います。
インフラすらもcloudflareに特化したvoidなどのフレームワークを使えば、AIに任せられそうです。
コードを書くのは楽しかったですが、悲観的にばかりならず、この歴史的な時代に生まれたことに感謝して楽しむマインドを忘れないようにしたいです。
一番近くにいるエンジニアから影響を受けていますが、他のホワイトワーカー、次にブルーワーカーも業態が変わっていき1-2年以内には世界が様変わりしていそうです。
一応、AI駆動開発のハーネスとして、技術の選定・要件・設計の判断を先に文書(指示書と ADR)にしてから実装させ、直した内容と再発防止をログに残す流れにしたので、あとから「なぜこうなっているのか」をリポジトリからたどれます。
sveltiaについては仕組みも複雑でカスタマイズもyamlを通じて行われるため、正直流行らなそうな気がしました笑
サーバーが不要で管理画面が使えるのか不思議で使ってみましたが、ざっくりと下記の仕組みで動いているようでした。
- /adminにアクセス
- sveltiaがconfig.ymlから画面を組み立てる (デフォルトだとsveltiaのスクリプトをCDNからダウンロードして動かしている)
- Github APIを通じてリポジトリのファイルを取得 (マークダウンや画像)
- 取得データをindexedDBなどブラウザのメモリに保持して描画
- 記事を変更・保存 (indexedDBに差分を保持)
- Github APIを通じて差分をコミット・PR作成
- 管理画面から公開
- PRマージ
- WebhookでCloudflare Build上でSSGしてCloudflare workerにデプロイ
スキル・コマンド化しておいて、スマホからGrokBotに依頼してクラウド上でビルドしてもらった成果物をPRでマージするなどの方が校正もしてくれるので、今っぽいかもしれません。(クラウド上でプレビュー)
ちなみにこの記事は、sveltiaの管理画面から投稿しています。
