ブックマーカー図鑑ツールチップ

    @@ -40,41 +40,44 @@ --danger: #b14a3b; --muted: #8a8071; --line: #d8cdb4; - + font-size: 0.8rem; font-weight: 700; color: var(--gold); - + max-width: 700px; margin: -7px; background: var(--card); border: 3px solid var(--ink); border-radius: 7px; - box-shadow: 10px 12px 0 rgba(28, 26, 23, .18); - opacity: .5; - + box-shadow: 0px 0px 0 rgba(28, 26, 23, .18); + + width: fit-content; position-area: center span-right; align-self: start; - transition: all .7s .2s allow-discrete; + transition: all .3s allow-discrete; interpolate-size: allow-keywords; - width: fit-content; - height: 0px; - overflow: hidden; + translate: 10px 12px; - &:popover-open { - height: auto; - opacity: 1; + box-shadow: 10px 12px 0 rgba(28, 26, 23, .18); + translate: 0px 0px; @starting-style { - height: 0px; - opacity: .5; + box-shadow: 0px 0px 0 rgba(28, 26, 23, .18); + translate: 10px 12px; } } } [interestfor="${ID}"] { interest-delay: 2s .1s; --interest-delay: 2s .1s; + img{ + transition: rotate .3s .5s; + &:hover{ + rotate: 15deg; + } + } } </style> `); @@ -95,8 +98,7 @@ let profile = localStorage.getItem(`bookmarker-encyclopedia_profile_${userId}`); if (!profile) { const response = await fetch(`https://bookmarker-encyclopedia.netlify.app/api/profiles/${userId}.json`); - if (!response.ok) return; - profile = await response.text(); + profile = response.ok ? await response.text() : 'null'; localStorage.setItem(`bookmarker-encyclopedia_profile_${userId}`, profile); }
  • // ==UserScript==
    // @name         ブックマーカー図鑑ツールチップ
    // @title         ブックマーカー図鑑ツールチップ
    // @namespace    https://let.hatelabo.jp/Lhankor_Mhy/let/laGdx-DigsAA
    // @version      0.1
    // @description  はてブで https://bookmarker-encyclopedia.netlify.app のキャッチフレーズをツールチップで表示します。アイコンの上で2秒ぐらいホバーしてください。
    // @author       Lhankor_Mhy
    // @match        https://b.hatena.ne.jp/entry/*
    // @icon         https://b.hatena.ne.jp/favicon.ico
    // @license      CC0
    // @noframes
    // @grant        none
    // ==/UserScript==
    
    (async function () {
        'use strict';
        const ID = "bookmarker-encyclopedia-tooltip";
    
        // polyfill for interestfor
        const supported = Object.hasOwn(
            HTMLButtonElement.prototype,
            "interestForElement",
        );
        if (!supported) {
            await import('https://cdn.jsdelivr.net/npm/interestfor@1.0.8/src/interestfor.min.js');
        }
    
        // insert tooltip elements and styles
        // document.querySelectorAll('a:has(img[src*="profile.png"])')
        document.body.insertAdjacentHTML('beforeend', `
            <div id="${ID}" popover>...</div>
            <style>
            #${ID} {
                --ink: #1c1a17;
                --paper: #f5f0e6;
                --card: #fffdf7;
                --gold: #b8893a;
                --gold-soft: #e8d9b5;
                --accent: #2f6f6b;
                --danger: #b14a3b;
                --muted: #8a8071;
                --line: #d8cdb4;
                
                font-size: 0.8rem;
                font-weight: 700;
                color: var(--gold);
                
                max-width: 700px;
                margin: -7px;
                background: var(--card);
                border: 3px solid var(--ink);
                border-radius: 7px;
                box-shadow: 0px 0px 0 rgba(28, 26, 23, .18);
                
                width: fit-content;
                position-area: center span-right;
                align-self: start;
    
                transition: all .3s allow-discrete;
                interpolate-size: allow-keywords;
                translate: 10px 12px;
    
                &:popover-open {
                    box-shadow: 10px 12px 0 rgba(28, 26, 23, .18);
                    translate: 0px 0px;
                    @starting-style {
                        box-shadow: 0px 0px 0 rgba(28, 26, 23, .18);
                        translate: 10px 12px;
                    }
                }
            }
            [interestfor="${ID}"] {
                interest-delay: 2s .1s;
                --interest-delay: 2s .1s;
                img{
                    transition: rotate .3s .5s;
                    &:hover{
                        rotate: 15deg;
                    }
                }
            }
            </style>
            `);
        const tooltip = document.getElementById(ID);
    
        function init() {
            // add interest invokers
            document.querySelectorAll('.entry-user-icon').forEach(icon => {
                icon.setAttribute('interestfor', tooltip.id);
            })
        }
    
        // add interest event listener
        tooltip.addEventListener("interest", async (e) => {
            const icon = e.source;
            const userId = icon.getAttribute('href').split('/')[1].toLowerCase();
    
            let profile = localStorage.getItem(`bookmarker-encyclopedia_profile_${userId}`);
            if (!profile) {
                const response = await fetch(`https://bookmarker-encyclopedia.netlify.app/api/profiles/${userId}.json`);
                profile = response.ok ? await response.text() : 'null';
                localStorage.setItem(`bookmarker-encyclopedia_profile_${userId}`, profile);
            }
    
            const { generated: { catchphrase } } = JSON.parse(profile);
            tooltip.textContent = catchphrase;
            const clonedIcon = icon.cloneNode(true);
            clonedIcon.setAttribute('interestfor', null);
            clonedIcon.setAttribute('href', `https://bookmarker-encyclopedia.netlify.app/profiles/${userId}`);
            tooltip.insertBefore(clonedIcon, tooltip.firstChild);
        });
        tooltip.addEventListener("loseinterest", (e) => {
            tooltip.textContent = "...";
        });
    
        // add mutation observer for lazy-loaded bookmarks
        const targetNode = document.querySelector('.js-bookmarks-recent');
    
        const mutationConfig = { childList: true };
        const mutationCallback = function (mutationsList, observer) {
            for (const mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    init();
                }
            }
        };
        const mutationObserver = new MutationObserver(mutationCallback);
        mutationObserver.observe(targetNode, mutationConfig);
    
        init();
    })();
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。

History

  1. 2026/07/30 15:53:55 - 14 hours ago
  2. 2026/07/29 17:18:23 - 1 days ago
  3. 2026/07/29 16:47:18 - 1 days ago
  4. 2026/07/29 16:44:33 - 1 days ago
  5. 2026/07/29 16:31:45 - 1 days ago
  6. 2026/07/29 16:25:49 - 1 days ago
  7. 2026/07/29 15:57:15 - 1 days ago
  8. 2026/07/29 15:55:20 - 1 days ago
  9. 2026/07/29 15:54:28 - 1 days ago
  10. 2026/07/29 15:51:01 - 1 days ago
  11. 2026/07/29 15:50:34 - 1 days ago