サーバー管理者なら一度は見たことがあるだろう。長年働いたWindows Serverを退役させる日、シャットダウンダイアログのコメント欄に「12年間お疲れ様でした。」と打ち込んでスクリーンショットを撮る、あの儀式である。
あの画面を誰でも作れるWebツールを作った。
- ツール本体: https://pocraft.net/shutdown/
- ソースコード: https://github.com/yoshiharu-ishii/shutdown-dialog-generator
コメント欄とタイトルバーは直接クリックして編集でき、理由のドロップダウンや「計画済み」チェックボックスは実機同様に連動する。できあがったら2倍解像度のPNGとして保存できる。しかもテーマ切替でNT 3.51からServer 2025まで歴代6世代のUIを再現している。
依存ゼロの単一HTMLという選択
最初に決めた技術方針は「index.html 1ファイル、外部依存ゼロ」である。理由は配信先にあった。このブログが動いているLightsailのWordPress(Bitnami)にそのまま同居させたかったのだ。
ビルドツールもCDNも使わなければ、デプロイは「ファイルを1個置く」で終わる。BitnamiのApacheはWordPressのドキュメントルート配下に実ディレクトリを作れば静的ファイルをそのまま配信してくれる。WordPressのパーマリンク用rewriteは「実在するファイル/ディレクトリは除外」という条件付きなので干渉しない。
sudo mkdir -p /opt/bitnami/wordpress/shutdown
sudo mv index.html /opt/bitnami/wordpress/shutdown/
これだけで https://ドメイン/shutdown/ が生える。月額固定のサーバーに間借りするミームツールとしては、これ以上ないほど身軽である。
もうひとつの方針は、ダイアログをネイティブUI部品を一切使わず自作CSSで描くこと。<select> や <input type="checkbox"> を使うと、見た目がOSとブラウザに支配される。macOSのChromeで見たらWindows感が全部消えてしまう。そこでボタンのベベル(立体枠)は box-shadow の inset 4連、コンボボックスもチェックボックスも全部divで描いた。おかげでどの環境でも同じ見た目になり、後述の画像書き出しも崩れない。
PNG書き出し: SVG foreignObjectという古典芸
「スクショを撮ってください」では味気ないので、ワンクリックでPNGを吐けるようにした。html2canvasのようなライブラリを入れれば簡単だが、依存ゼロ方針に反する。そこでSVGの foreignObject を使う古典芸で自前実装した。
- ダイアログのDOMを
cloneNodeする - スタイルシートごと
<svg><foreignObject>に包んで文字列化する data:image/svg+xmlのURLを<img>に食わせてラスタライズする- canvasに2倍スケールで描いて
toDataURL("image/png")
この方式の罠は「canvasの汚染(taint)」で、外部リソースをひとつでも参照すると toDataURL が例外を投げる。だが今回は外部依存ゼロなので汚染のしようがない。方針が思わぬところで効いた。なおforeignObjectのラスタライズはSafariが苦手なので、書き出しはChrome/Edge/Firefox対応としている。
歴代Windows Serverテーマと時代考証
「最新のWindowsに寄せたVer2は?」から始まって、結局NT系まで遡り、6世代を作り込んだ。調べてみると世代分けには少し面白い事実がある。
| テーマ | 再現UI | 考証メモ |
|---|---|---|
| NT 3.51 | Windows 3.1系 | タイトル中央寄せ・左にシステムメニュー箱・チェックは「×」印 |
| NT 4.0 | Windows 95系 | グラデなし単色紺タイトルバー |
| 2000–2008 R2 | クラシック | 2008系も既定テーマはクラシックなので2003とほぼ同一 |
| 2012 | Windows 8系Metro | 淡青の太枠・タイトル中央寄せ・フラット |
| 2016–2022 | Windows 10系 | 白タイトルバー・角ばったコントロール |
| 2025 | Windows 11系Fluent | 角丸・アクセントブルー |
そもそもこの「シャットダウン イベントの追跡ツール」はWindows Server 2003で初登場した機能で、NT時代には存在しない。つまりNT系テーマは「もしあったら」の架空再現である。ここは誤解されると嫌なので、NT系テーマを選んだときだけページに「実物にはこの画面は存在しません」という注記が出るようにした。ミーム画像そのものに透かしを入れると台無しなので、注記はページ側だけに置いている。
実装面では、HTML構造は全テーマ共通で、#capture に付けるテーマクラス1つに対するCSSオーバーライドだけで切り替えている。1テーマあたり約100行のCSSで増やせる構造になったので、6世代作っても本体は1ファイル約900行で収まった。書き出しはDOMクローン方式なので、テーマもそのままPNGに焼き込まれる。
ビットマップフォントの「掠れ」を再現する
仕上げは「2000系はフォントの掠れも表現できたらすごい」というリクエストだった。あの時代のスクショ特有の、文字がガタガタでところどころ欠けている質感。正体はClearType以前のアンチエイリアスなしビットマップMS UIゴシックである。
現代のブラウザは何をどうやっても文字を滑らかに描いてしまう。そこでSVGフィルター3段で「後から汚す」方針をとった。
feTurbulence … 細かいノイズを生成
feDisplacementMap … ノイズで文字の輪郭を1px弱ランダムに揺らす
feFuncA (discrete) … アルファを2値化し、アンチエイリアスの中間調を消す
ポイントは3段目で、輪郭のグレーな中間画素が全部消えることで、細い画がところどころ欠ける。これがビットマップフォントの「掠れ」にかなり近い。適用はClearType以前の世代(NT系〜2008 R2)だけにして、2012以降のスムージング時代はそのまま滑らかに描いている。
地味に効いたのが、フィルター定義のSVGを書き出し対象の #capture の内側に置いたことだ。DOMクローン→foreignObject書き出しの仕組みにフィルターごと乗るので、保存したPNGにも掠れがそのまま焼き込まれる。
おまけ: リポジトリが404に見えた話
開発中の小さな謎解きをひとつ。GitHubにリポジトリを作った直後、「リポジトリが404になる」という報告があった。privateリポジトリは未ログインだと404に見えるのが定番の原因だが、今回はもうひとつ罠があった。gh repo create --source . をフィーチャーブランチ上で実行したため、GitHubのデフォルトブランチがそのブランチになっていたのだ。masterをデフォルトに直してマージ済みブランチを消したら、見慣れたリポジトリの顔になった。リポジトリ作成はmasterで、という教訓である。
まとめ
- 依存ゼロの単一HTMLは、WordPressサーバーへの間借りデプロイと相性が最高だった
- ネイティブUIを使わない自作CSSコントロールは、見た目の再現性と画像書き出しの安定を両取りできる
- SVGフィルターはビットマップフォントの「掠れ」の偽装にも使える
退役サーバーをお持ちの方は、ぜひ最後の一枚にどうぞ。