Skip to content

Instantly share code, notes, and snippets.

@mimonelu
Last active January 23, 2026 13:55
Show Gist options
  • Select an option

  • Save mimonelu/f0e35ce16631c13179df0124baff14eb to your computer and use it in GitHub Desktop.

Select an option

Save mimonelu/f0e35ce16631c13179df0124baff14eb to your computer and use it in GitHub Desktop.
Nicosky - Blueskyのポストをニコニコ風に表示するブックマークレット
(() => {
const JETSTREAM_URL = "wss://jetstream1.us-west.bsky.network/subscribe?wantedCollections=app.bsky.feed.post"
const NUMBER_OF_SLOTS = 15
const TARGET_LANG = "ja"
const FONT_SIZE = 5
const SLOT_HEIGHT = 6.25
const TRANSITION_DURATIONS = [100, 2500]
const CONTAINER_CLASS_NAME = "nicosky"
const slots = Array(NUMBER_OF_SLOTS).fill(false)
let numberOfMessages = 0
let numberOfLangPosts = 0
let numberOfDisplayPosts = 0
let container = document.querySelector(`.${CONTAINER_CLASS_NAME}`)
if (container) {
if (container.__nicoskySocket) {
try {
container.__nicoskySocket.close()
} catch {}
}
container.parentNode.removeChild(container)
return
} else {
container = document.createElement("div")
container.style.cssText = `
all: initial;
contain: layout paint style;
overflow: hidden;
pointer-events: none;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100vh;
z-index: 65535;
`
container.classList.add(CONTAINER_CLASS_NAME)
document.body.appendChild(container)
}
const infoContainer = document.createElement("div")
infoContainer.style.cssText = `
${makeTextStyles()}
font-size: ${FONT_SIZE / 2}vh;
color: #c0c0c0;
position: absolute;
right: 0.5em;
bottom: 0.5em;
`
container.appendChild(infoContainer)
const socket = new WebSocket(JETSTREAM_URL)
container.__nicoskySocket = socket
socket.addEventListener("message", event => {
numberOfMessages ++
updateInfo()
if (event.data == null) {
return
}
const record = JSON.parse(event.data)?.commit?.record
if (
!record ||
!record.langs?.length ||
!record.text
) {
return
}
const hasTargetLang = record.langs.includes(TARGET_LANG)
if (!hasTargetLang) {
return
}
numberOfLangPosts ++
const hasFacets = record.facets != null
if (hasFacets) {
return
}
const hasEmbed = record.embed != null
if (hasEmbed) {
return
}
const slotIndex = slots.findIndex(slot => slot === false)
if (slotIndex === - 1) {
return
}
updateInfo()
slots[slotIndex] = true
const textContainer = document.createElement("div")
textContainer.addEventListener("transitionend", () => {
if (container && textContainer) {
container.removeChild(textContainer)
slots[slotIndex] = false
}
})
const textNode = document.createTextNode(record.text)
textContainer.appendChild(textNode)
container.appendChild(textContainer)
const isReply = record.reply != null
const duration = record.text.length * TRANSITION_DURATIONS[0] + TRANSITION_DURATIONS[1]
textContainer.style.cssText = `
${makeTextStyles()}
color: ${isReply ? "#c0e0ff" : "#ffffff" };
font-size: ${FONT_SIZE}vh;
position: absolute;
left: 0;
top: 0;
transform: translate3d(100vw, 0, 0);
transition: transform ${duration}ms linear;
`
requestAnimationFrame(() => {
const width = textContainer.clientWidth
const y = slotIndex * SLOT_HEIGHT
textContainer.style.cssText += `
top: ${y}vh;
transform: translate3d(-${width}px, 0, 0);
`
})
if (++ numberOfDisplayPosts >= 1000) {
socket.close()
}
})
let lastInfo = 0
function updateInfo () {
const now = performance.now()
if (now - lastInfo < 125) {
return
}
lastInfo = now
infoContainer.innerText = `${numberOfDisplayPosts} shown (${numberOfLangPosts} "${TARGET_LANG}" / ${numberOfMessages} total)`
}
function makeTextStyles () {
return `
font-family: monospace;
-webkit-font-smoothing: none;
font-weight: bold;
text-shadow: -1px -1px 0 #000000, 1px -1px 0 #000000, -1px 1px 0 #000000, 1px 1px 0 #000000;
user-select: none;
white-space: nowrap;
`
}
})()
@mimonelu
Copy link
Author

mimonelu commented Jan 23, 2026

説明

Blueskyのポストを某動画サイト風に表示するブックマークレットです。

  • 「ブックマークレット登録用圧縮版」をブックマークとして登録し、任意のWebページで実行します。再実行で終了
    • Webページによってはセキュリティの都合上、実行できない場合があります
  • サーバ負荷軽減のため、1000件表示で自動終了します
  • 表示するポストの条件
    • ポスト言語に ja を含むポスト
    • facets のないポスト
    • embed のないポスト
    • かつ、表示する場所があれば表示します
  • 薄い青色のテキストはリプライです。リポストや引用RPは対象外
  • 右下に簡易統計情報を表示します

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment