██████╗ ██╗ █████╗ ██████╗██╗ ██╗ ███╗ ███╗██╗██████╗ ██████╗ ██████╗ ██████╗ ██╔══██╗██║ ██╔══██╗██╔════╝██║ ██╔╝ ████╗ ████║██║██╔══██╗██╔══██╗██╔═══██╗██╔══██╗ ██████╔╝██║ ███████║██║ █████╔╝ ██╔████╔██║██║██████╔╝██████╔╝██║ ██║██████╔╝ ██╔══██╗██║ ██╔══██║██║ ██╔═██╗ ██║╚██╔╝██║██║██╔══██╗██╔══██╗██║ ██║██╔══██╗ ██████╔╝███████╗██║ ██║╚██████╗██║ ██╗ ██║ ╚═╝ ██║██║██║ ██║██║ ██║╚██████╔╝██║ ██║ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
Black Mirror Board は、ターミナル駆動型の無限キャンバス。 マウス/タッチ/ペン入力を1つのモデルに統合し、「図形を描く」と「コマンドを打つ」の境界を溶かすツールとして構築された。
This document catalogs the full development arc from initial sketch (v0.1) through the v1.0 release. Each phase is presented in chronological order with the driving prompt, the resolved challenge, and the resulting capability.
dark mode / light mode engages a glitch-flash mode transition.resolveFill() channel inversion).#DCDBD5 / cobalt ink #1600A2. Dedicated CSS-variable theme alongside light/dark, triggered by gray command.S.modifiers ledger so holding a key mid-drag updates geometry instantly; window blur resets the ledger to defeat the classic "stuck Shift after tab-swap" bug.Esc abandons any in-flight gesture (drag, marquee, rotate, resize, text edit) back to neutral; double-click exits text mode cleanly.Enter, cancel on Esc; click outside drops focus without losing the object.svg in the terminal writes a native SVG of the current selection (or the whole canvas). Every primitive translates: circle→<ellipse>, square→<rect>, triangle→<polygon>, arrow→<line>+<polygon>, text→<text dominant-baseline="hanging">, pen stroke→<path M…Q…>, image→<image>. Rotation wraps as <g transform="rotate()">. Colors resolved via resolveFill(). Opens in Figma / Illustrator / Inkscape editable.Black Mirror flips on getUserMedia and docks a 220×165 PIP in the top-right (128×170 on mobile). Red REC dot, ◼ BLACK MIRROR label in mix-blend-mode: difference, selfie-mirrored with a faint grayscale + contrast filter for the CRT look. Hover exposes an × close; Black Mirror off also releases all tracks. The wipe is draggable — place it anywhere on screen.share with no args gzips the canvas + terminal state via native CompressionStream, base64-urls it, and copies a self-contained link. Opening the URL on any device restores the full board. share vs share url selects between file download and clipboard.BM API + natural language prompts to Claude / ChatGPT produce runnable spells.scatter, grid-3x3, biwako-blue, fetch-img, monoclo, svg ship with the app, each as JSON-portable spells that can be dumped, remixed, and re-shared.fetch-img <keyword> pulls public images for the word, scatters them on the canvas with rotation + margin, turning the board into a self-writing mood-board.v1.2_manual_ja/ and v1.2_manual_en/.99letters.github.io/projects/black_mirror/ to the canonical blackmirrorboard.github.io/bmboard/. All ../../md.html / ../../history.html references absolutized to https://99letters.github.io/… so the new origin still reaches the dev log.README.md, usage docs, app.html footer, terminal ghost lines (Playing: kinoshita studio / Black Mirror Board), and INDEX frontmatter tags. The 99letters domain remains as the GitHub identity only.█ (U+2588) shorter than the cell, leaving vertical gaps. Loaded JetBrains Mono via Google Fonts and force-applied font-size: 14px; line-height: 1; letter-spacing: 0; word-spacing: 0 to the logo <pre> with !important.● · ○) next to the EN/JA switch. All colors run through CSS variables (--ink / --void / --scan-color / --glow-halo / --checker-fill / --header-bg); hard-coded whites and blacks were removed. prefers-color-scheme is honored on first visit; choice persists via bmboard.theme in localStorage. An inline head script applies the saved theme before paint to defeat flash-of-wrong-theme. Dark-ink logo.png is inverted via CSS filter: invert(1) in dark mode only, left as-is in light mode.dark and gray commands now also theme the header (background, border, .hbtn, .hlink, .zoom-label, .logo*). All colors read from the existing --bm-tb-* / --bm-fg / --bm-fg-dim tokens. Logo PNG inverted only in dark mode.FLUSH + CLEAR hidden (still reachable via terminal), icon+label stacked vertically inside each .hbtn (36px square), OVERVIEW split across two lines as OVER / VIEW with the arrow removed. All buttons' icons centered in their box via inline-flex; justify-content: center.margin-left: auto trick with an explicit 2-column grid (grid-template-columns: auto 1fr), logo in column 1, right buttons in column 2 with justify-self: end. Predictable on every viewport.og-image.svg (1200×630) authored for both index.html and app.html: black ground with scanline texture, corner brackets, top-right ● REC ON AIR, 400-px logo (base64-embedded, SVG feColorMatrix inverted to white), title stack BLACK MIRROR / BOARD, subtitle DRAW · SPEAK · CAST, version strip.README tab — BM pixel ASCII now renders crisply in Courier-deprived browsers via the JetBrains Mono fix + font-feature-settings: "liga" 0, "calt" 0.The core prompts that shaped v1.0 — quoted verbatim from Kinoshita Studio, paraphrased where long.
Dark Mode 時の図形・塗りの視認性改善。背景は反転するが、図形の「塗り(Fill)」や一部のスタイルが以前の黒(または濃い色)のまま残っているため、視認性が著しく低下している。
第3のモードとして、ユーザーが最も頻繁に使用する "gray" モードを追加してください。 背景#DCDBD5/ アクセント#1600A2。最高にクールな青のコントラストを実現して。
PCでの操作時、特に細い鉛筆やパスツールで描いたオブジェクトを選択・移動する際、正確に線の上をクリックしないと掴めない。判定領域を「見た目の線+左右に 5〜8px」に拡張し、「直感的に掴める」プロの道具としての精度を完成させて。
Shiftキー連動のアスペクト比固定。角のハンドルをドラッグしてリサイズする際、Shiftキーが押されている間は、オブジェクトの元のアスペクト比を完全に維持して拡大縮小するようにしてください。画像のリサイズで比率が崩れるのは致命的。
python -m http.server を ~/ から起動した際、OS のアクセス制限でサブディレクトリの HTML が 500 を返した。
プロジェクトローカルの server.py を projects/black_mirror/ 直下で立ち上げるフローに変更して解決。
applyAnchor ブロックに同一の switch が copy-paste されていた。
resizeBoxWithMods(anchorId, ob, nx, ny, shift, alt) に一本化し、Shift/Alt の挙動を一点で管理できるようにした。
pointToSegDist() によるセグメント距離判定 + 閉パスの ray-cast fill check に切替えて解決。
fill="#111" / fill="#FAFAF8" 直書きで、dark/gray モードで溶けていた。
.sw-black / .sw-grey / .sw-white / .sw-ind クラスに置換し、var(--bm-swatch-*) で自動反転させる設計に。
shiftKey をその都度読んでいたため、マウスが停止した状態で Shift を押しても再評価されなかった。
window keydown/keyup で S.modifiers を更新し、S.lastAnchorPt を保持して即座に applyAnchor() を再実行することで解決。
window blur で S.modifiers を強制リセット。タブを戻したときに「Shift が押されっぱなし扱い」になる問題を根絶。
Single S object holds all mutable canvas state; VIEW holds pan/zoom; MODE holds theme. No framework, no reactive glue — changes call redrawAll() explicitly.
Per-mode CSS custom properties (--bm-canvas-bg, --bm-fg, --bm-tb-bg, --bm-accent, --bm-swatch-*) toggled via body.bm-dark / body.bm-gray. Single class swap cascades the entire UI.
Objects store canonical light-mode fills. resolveFill() translates at render time: dark → channel-inverted, gray → cobalt family. No mutation of stored data — mode switch is pure view.
One helper (resizeBoxWithMods) normalizes 8-handle bbox math + shift/alt constraints. Single source of truth for single-object anchors and multi-select group resize alike.