/** * Icons: Lucide (https://lucide.dev) * ISC License - Copyright (c) Lucide Contributors 2026 */ (function () { 'use strict'; var SCRIPT = document.currentScript; var API_BASE = SCRIPT ? SCRIPT.src.replace(/\/widget\.js.*$/, '') : ''; var API_TOKEN = SCRIPT ? (SCRIPT.dataset.token || '') : ''; var USERNAME_KEY = 'fb-username'; var SIDEBAR_WIDTH_KEY = 'fb-sidebar-width'; var PRIORITY_CYCLE = { must: 'better', better: 'want', want: 'must' }; var PRIORITY_COLORS = { must: { bg: '#ef4444', text: '#fff', light: 'rgba(239,68,68,0.1)', border: 'rgba(239,68,68,0.3)' }, better: { bg: '#f59e0b', text: '#fff', light: 'rgba(245,158,11,0.1)', border: 'rgba(245,158,11,0.3)' }, want: { bg: '#22c55e', text: '#fff', light: 'rgba(34,197,94,0.1)', border: 'rgba(34,197,94,0.3)' }, }; var SVG = { message: '', check: '', rotateCcw: '', pencil: '', trash: '', user: '', x: '', panelRight: '', }; function icon(name, size, strokeWidth) { size = size || 14; strokeWidth = strokeWidth || 2; return (SVG[name] || '').replace(/SIZE/g, size).replace(/SW/g, strokeWidth); } var state = { username: localStorage.getItem(USERNAME_KEY) || '', comments: [], filter: 'unresolved', sidebarOpen: false, selectedText: '', selectedQuoteContext: { beforeText: '', afterText: '' }, selectedRect: null, popupContent: '', editingId: null, editContent: '', editPriority: 'want', replyingTo: null, replyText: '', editingName: false, nameInput: '', popupPriority: 'must', sidebarWidth: parseInt(localStorage.getItem(SIDEBAR_WIDTH_KEY), 10) || 400, }; var slug = (function () { return window.location.href .replace(/^https?:\/\//, '') .replace(/[^a-zA-Z0-9\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF]/g, '_') .substring(0, 100); })(); function genId() { return Date.now().toString(36) + Math.random().toString(36).substring(2, 8); } function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; } function fmtTime(ts) { var d = Date.now() - ts; if (d < 60000) return 'たった今'; if (d < 3600000) return Math.floor(d / 60000) + '分前'; if (d < 86400000) return Math.floor(d / 3600000) + '時間前'; return new Date(ts).toLocaleDateString('ja-JP', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } // ===== API ===== function api(method, params) { var url = API_BASE + '/api/comments'; var hdrs = { 'Content-Type': 'application/json' }; if (API_TOKEN) hdrs['Authorization'] = 'Bearer ' + API_TOKEN; var opts = { method: method, headers: hdrs }; if (method === 'GET') { url += '?slug=' + encodeURIComponent(params.slug); } else if (method === 'DELETE') { url += '?id=' + encodeURIComponent(params.id); } else { opts.body = JSON.stringify(params); } return fetch(url, opts).then(function (r) { return r.json(); }); } function loadComments() { return api('GET', { slug: slug }).then(function (c) { if (Array.isArray(c)) state.comments = c; render(); applyHighlights(); }).catch(function () { render(); applyHighlights(); }); } // ===== CSS ===== function injectStyles() { var style = document.createElement('style'); style.id = 'fb-widget-styles'; style.textContent = [ ':root{--fb-bg:#ffffff;--fb-fg:#0a0a0a;--fb-muted:#f5f5f5;--fb-muted-fg:#737373;--fb-border:#e5e5e5;--fb-primary:#171717;--fb-primary-fg:#fafafa;--fb-accent:#3b82f6;--fb-destructive:#ef4444;--fb-font:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Hiragino Sans",sans-serif}', /* Toggle button */ '#fb-toggle{position:fixed;right:0;top:50%;transform:translateY(-50%);width:36px;background:var(--fb-bg);border:1px solid var(--fb-border);border-right:none;border-radius:8px 0 0 8px;cursor:pointer;z-index:99999;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;font-family:var(--fb-font);transition:all .15s;box-shadow:-2px 0 8px rgba(0,0,0,0.06);padding:8px 0}', '#fb-toggle:hover{background:var(--fb-muted);box-shadow:-2px 0 12px rgba(0,0,0,0.1)}', '#fb-toggle .fb-toggle-icon{color:var(--fb-muted-fg);display:flex;align-items:center;justify-content:center;position:relative}', '#fb-toggle .fb-toggle-label{font-size:10px;color:var(--fb-accent);font-weight:700;writing-mode:vertical-rl;letter-spacing:1px;line-height:1;white-space:nowrap}', '#fb-toggle .fb-badge{position:absolute;top:-6px;left:-6px;background:var(--fb-destructive);color:#fff;font-size:9px;font-weight:700;min-width:16px;height:16px;border-radius:8px;display:flex;align-items:center;justify-content:center;padding:0 3px;line-height:1}', /* Sidebar */ '#fb-sidebar{position:fixed;top:0;height:100vh;background:rgba(245,245,245,0.5);border-left:1px solid var(--fb-border);z-index:99998;transition:right .3s ease;display:flex;flex-direction:column;font-family:var(--fb-font);font-size:14px;color:var(--fb-fg)}', '#fb-sidebar *{box-sizing:border-box}', /* Resize handle */ '.fb-resize-handle{position:absolute;left:-3px;top:0;width:6px;height:100%;cursor:col-resize;z-index:1}', '.fb-resize-handle:hover{background:rgba(59,130,246,0.15)}', '.fb-resize-handle.active{background:rgba(59,130,246,0.3)}', 'body.fb-resizing{cursor:col-resize !important;-webkit-user-select:none !important;user-select:none !important}', 'body.fb-resizing *{cursor:col-resize !important}', /* Header */ '.fb-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--fb-border)}', '.fb-header-left{display:flex;align-items:center;gap:8px}', '.fb-header-title{font-size:14px;font-weight:700;color:var(--fb-accent)}', '.fb-header-count{background:var(--fb-muted);color:var(--fb-muted-fg);font-size:11px;padding:1px 7px;border-radius:10px}', '.fb-header-actions{display:flex;align-items:center;gap:2px}', '.fb-hdr-btn{background:none;border:none;cursor:pointer;padding:6px;border-radius:6px;color:var(--fb-muted-fg);transition:all .15s;display:inline-flex;align-items:center;gap:4px;font-family:var(--fb-font);font-size:12px}', '.fb-hdr-btn:hover{background:var(--fb-muted);color:var(--fb-fg)}', /* User row */ '.fb-user-row{display:flex;align-items:center;padding:8px 16px;border-bottom:1px solid var(--fb-border);font-size:13px;color:var(--fb-muted-fg);cursor:pointer;transition:color .15s;gap:6px}', '.fb-user-row:hover{color:var(--fb-fg)}', /* Filters */ '.fb-filters{display:flex;border-bottom:1px solid var(--fb-border)}', '.fb-filter{flex:1;padding:10px 0;text-align:center;font-size:13px;color:var(--fb-muted-fg);border:none;background:none;cursor:pointer;border-bottom:2px solid transparent;transition:all .15s;font-family:var(--fb-font)}', '.fb-filter:hover{color:var(--fb-fg)}', '.fb-filter.active{color:var(--fb-fg);border-bottom-color:var(--fb-accent)}', '.fb-filter .cnt{font-size:11px;margin-left:4px;padding:1px 5px;border-radius:8px;background:var(--fb-muted);color:var(--fb-muted-fg)}', '.fb-filter.active .cnt{background:rgba(59,130,246,0.1);color:var(--fb-accent)}', /* List */ '.fb-list{flex:1;overflow-y:auto;padding:8px}', '.fb-empty{text-align:center;padding:60px 20px;color:var(--fb-muted-fg);font-size:13px}', '.fb-empty svg{margin:0 auto 12px;display:block;color:var(--fb-border)}', /* Card */ '.fb-card{background:var(--fb-bg);border:1px solid var(--fb-border);border-left:3px solid var(--fb-border);border-radius:12px;padding:14px;margin-bottom:6px;transition:box-shadow .3s,background .3s;cursor:pointer}', '.fb-card:hover{box-shadow:0 1px 3px rgba(0,0,0,0.05)}', '.fb-card.resolved{opacity:.5}', /* Card header */ '.fb-card-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}', '.fb-card-head-left{display:flex;align-items:center;gap:6px}', '.fb-avatar{width:22px;height:22px;border-radius:50%;background:var(--fb-primary);color:var(--fb-primary-fg);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;flex-shrink:0}', '.fb-author{font-size:13px;font-weight:700;color:var(--fb-fg)}', '.fb-time{font-size:11px;color:var(--fb-muted-fg)}', '.fb-resolved-mark{font-size:11px;color:#22c55e;margin-left:4px;display:inline-flex;align-items:center;gap:2px}', /* Priority badge */ '.fb-badge-p{font-size:11px;font-weight:700;padding:2px 8px;border-radius:4px;color:#fff;cursor:default;transition:all .15s}', '.fb-badge-p.own{cursor:pointer}', '.fb-badge-p.own:hover{transform:scale(1.1);box-shadow:0 0 0 2px rgba(0,0,0,0.08)}', /* Quote */ '.fb-quote{font-size:12px;color:var(--fb-muted-fg);padding:6px 10px;background:var(--fb-muted);border-left:2px solid var(--fb-primary);border-radius:0 4px 4px 0;margin-bottom:8px;font-style:italic;line-height:1.5;cursor:pointer;transition:background .15s}', '.fb-quote:hover{background:var(--fb-border)}', /* Body */ '.fb-body{font-size:13px;color:var(--fb-muted-fg);line-height:1.6;margin-bottom:6px;white-space:pre-wrap}', /* Actions */ '.fb-actions{display:flex;gap:2px;flex-wrap:wrap}', '.fb-act{font-size:12px;color:#a3a3a3;background:none;border:none;padding:4px 8px;border-radius:4px;cursor:pointer;transition:all .15s;display:inline-flex;align-items:center;gap:3px;font-family:var(--fb-font)}', '.fb-act:hover{color:var(--fb-fg)}', '.fb-act.del:hover{color:var(--fb-destructive)}', '.fb-act.res:hover{color:#22c55e}', /* Replies */ '.fb-replies{margin-top:8px;padding-top:8px;border-top:1px solid var(--fb-muted)}', '.fb-reply-item{display:flex;gap:8px;padding:6px 0}', '.fb-reply-item+.fb-reply-item{border-top:1px solid var(--fb-muted)}', '.fb-reply-avatar{width:20px;height:20px;border-radius:50%;background:var(--fb-primary);color:var(--fb-primary-fg);display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:700;flex-shrink:0;margin-top:2px}', '.fb-reply-meta{font-size:12px;color:var(--fb-muted-fg);margin-bottom:2px}', '.fb-reply-meta strong{color:#525252;font-weight:700}', '.fb-reply-text{font-size:13px;color:var(--fb-muted-fg);line-height:1.5}', /* Reply input */ '.fb-reply-input{display:flex;gap:8px;margin-top:8px}', '.fb-reply-input textarea{flex:1;padding:8px 10px;border:1px solid var(--fb-border);border-radius:6px;font-size:13px;font-family:var(--fb-font);resize:none;outline:none;min-height:36px;color:var(--fb-fg)}', '.fb-reply-input textarea:focus{border-color:var(--fb-accent)}', '.fb-reply-input button{padding:6px 14px;background:var(--fb-primary);color:var(--fb-primary-fg);border:none;border-radius:6px;font-size:12px;cursor:pointer;font-family:var(--fb-font);align-self:flex-end}', '.fb-reply-input button:disabled{opacity:.4;cursor:default}', /* Edit area */ '.fb-edit-area textarea{width:100%;padding:8px 10px;border:1px solid var(--fb-border);border-radius:6px;font-size:13px;font-family:var(--fb-font);resize:vertical;outline:none;min-height:50px;margin-bottom:6px;color:var(--fb-fg)}', '.fb-edit-area textarea:focus{border-color:var(--fb-accent)}', '.fb-edit-btns{display:flex;gap:6px}', '.fb-edit-btns button{padding:4px 12px;border-radius:6px;font-size:12px;cursor:pointer;border:1px solid var(--fb-border);background:var(--fb-bg);color:var(--fb-muted-fg);font-family:var(--fb-font)}', '.fb-edit-btns button.save{background:var(--fb-primary);color:var(--fb-primary-fg);border-color:var(--fb-primary)}', '.fb-edit-pri{display:flex;gap:4px;margin-bottom:6px}', '.fb-edit-pri button{padding:2px 10px;border-radius:4px;font-size:11px;font-weight:700;cursor:pointer;border:1px solid var(--fb-border);background:var(--fb-bg);color:var(--fb-muted-fg);font-family:var(--fb-font);transition:all .15s}', /* Popup */ '.fb-popup{position:fixed;z-index:100000;width:400px;background:var(--fb-bg);border:1px solid var(--fb-border);border-radius:12px;padding:16px;box-shadow:0 10px 25px rgba(0,0,0,0.1);font-family:var(--fb-font);display:none}', '.fb-popup.show{display:block}', '.fb-popup-head{margin-bottom:10px}', '.fb-popup-head span{font-size:14px;font-weight:700;color:var(--fb-fg)}', '.fb-popup-quote{font-size:13px;color:var(--fb-muted-fg);padding:8px 12px;background:var(--fb-muted);border-left:2px solid var(--fb-accent);border-radius:0 6px 6px 0;margin-bottom:10px;font-style:italic;line-height:1.5}', '.fb-popup textarea{width:100%;min-height:70px;padding:10px 12px;border:1px solid var(--fb-border);border-radius:8px;font-size:14px;font-family:var(--fb-font);resize:vertical;outline:none;margin-bottom:10px;color:var(--fb-fg)}', '.fb-popup textarea:focus{border-color:var(--fb-accent)}', '.fb-popup textarea::placeholder{color:var(--fb-muted-fg)}', '.fb-popup-pri{display:flex;gap:6px;margin-bottom:10px}', '.fb-popup-pri button{flex:1;padding:7px 8px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;border:2px solid transparent;transition:all .15s;font-family:var(--fb-font)}', '.fb-popup-actions{display:flex;gap:8px;justify-content:flex-end}', '.fb-popup-actions button{padding:8px 18px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;transition:all .15s;font-family:var(--fb-font)}', '.fb-popup-actions .cancel{background:none;border:1px solid var(--fb-border);color:var(--fb-muted-fg)}', '.fb-popup-actions .cancel:hover{background:var(--fb-muted);color:var(--fb-fg)}', '.fb-popup-actions .submit{border:none;color:#fff}', /* Name dialog */ '.fb-name-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.4);display:flex;align-items:center;justify-content:center;z-index:100001}', '.fb-name-box{background:var(--fb-bg);border-radius:12px;padding:28px;width:360px;box-shadow:0 20px 40px rgba(0,0,0,0.15)}', '.fb-name-box h2{font-size:18px;font-weight:700;margin:0 0 6px;color:var(--fb-fg)}', '.fb-name-box p{font-size:14px;color:var(--fb-muted-fg);margin:0 0 16px}', '.fb-name-box input{width:100%;padding:10px 14px;border:1px solid var(--fb-border);border-radius:8px;font-size:15px;outline:none;margin-bottom:12px;font-family:var(--fb-font);color:var(--fb-fg)}', '.fb-name-box input:focus{border-color:var(--fb-accent)}', '.fb-name-box input::placeholder{color:var(--fb-muted-fg)}', '.fb-name-box button{width:100%;padding:10px;background:var(--fb-primary);color:var(--fb-primary-fg);border:none;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;font-family:var(--fb-font)}', '.fb-name-box button:disabled{opacity:.4;cursor:default}', /* Inline name edit */ '.fb-name-input{width:100%;padding:4px 8px;border:1px solid var(--fb-border);border-radius:4px;font-size:12px;outline:none;font-family:var(--fb-font);color:var(--fb-fg)}', '.fb-name-input:focus{border-color:var(--fb-accent)}', /* Highlights */ '.fb-highlight{padding:1px 0;cursor:pointer;transition:background .15s}', '.fb-highlight-must{background:rgba(239,68,68,0.15);border-bottom:2px solid #ef4444}', '.fb-highlight-must:hover{background:rgba(239,68,68,0.25)}', '.fb-highlight-better{background:rgba(245,158,11,0.15);border-bottom:2px solid #f59e0b}', '.fb-highlight-better:hover{background:rgba(245,158,11,0.25)}', '.fb-highlight-want{background:rgba(34,197,94,0.15);border-bottom:2px solid #22c55e}', '.fb-highlight-want:hover{background:rgba(34,197,94,0.25)}', '@keyframes fb-pulse{0%,100%{opacity:1}50%{opacity:0.4}}', '.fb-highlight.fb-pulse{animation:fb-pulse 1s ease-in-out infinite}', '.fb-card.fb-focused{box-shadow:0 0 0 2px var(--fb-accent);background:rgba(59,130,246,0.04)}', '#fb-sidebar svg,#fb-toggle svg,.fb-popup svg{pointer-events:none}', ].join('\n'); document.head.appendChild(style); } // ===== DOM ===== function el(tag, attrs, children) { var e = document.createElement(tag); if (attrs) Object.keys(attrs).forEach(function (k) { if (k === 'className') e.className = attrs[k]; else if (k === 'innerHTML') e.innerHTML = attrs[k]; else if (k.startsWith('on')) e.addEventListener(k.substring(2).toLowerCase(), attrs[k]); else e.setAttribute(k, attrs[k]); }); if (children) { if (typeof children === 'string') e.textContent = children; else if (Array.isArray(children)) children.forEach(function (c) { if (c) e.appendChild(c); }); else e.appendChild(children); } return e; } // ===== Render ===== function render() { renderToggle(); renderSidebar(); renderPopup(); renderNameDialog(); } function renderToggle() { var btn = document.getElementById('fb-toggle'); if (!btn) { btn = el('button', { id: 'fb-toggle', onClick: toggleSidebar }); document.body.appendChild(btn); } var unresolvedCount = state.comments.filter(function (c) { return !c.parentId && !c.resolved; }).length; var h = ''; h += 'コメント'; btn.innerHTML = h; btn.style.display = state.sidebarOpen ? 'none' : ''; } function applySidebarWidth() { var sb = document.getElementById('fb-sidebar'); if (!sb) return; var w = state.sidebarWidth; sb.style.width = w + 'px'; sb.style.right = state.sidebarOpen ? '0px' : (-(w + 20)) + 'px'; document.body.style.marginRight = state.sidebarOpen ? w + 'px' : ''; } function toggleSidebar() { state.sidebarOpen = !state.sidebarOpen; var btn = document.getElementById('fb-toggle'); if (btn) btn.style.display = state.sidebarOpen ? 'none' : ''; document.body.style.transition = 'margin-right 0.3s ease'; applySidebarWidth(); } function topLevel() { return state.comments.filter(function (c) { return !c.parentId; }); } function getReplies(id) { return state.comments.filter(function (c) { return c.parentId === id; }).sort(function (a, b) { return a.timestamp - b.timestamp; }); } function filtered() { var tl = topLevel(); if (state.filter === 'resolved') return tl.filter(function (c) { return c.resolved; }); if (state.filter === 'all') return tl; return tl.filter(function (c) { return !c.resolved; }); } function renderSidebar() { var sb = document.getElementById('fb-sidebar'); var isNew = !sb; if (isNew) { sb = el('div', { id: 'fb-sidebar' }); document.body.appendChild(sb); sb.addEventListener('mouseover', function (e) { var card = e.target.closest('.fb-card'); if (!card) return; var mark = document.querySelector('.fb-highlight[data-comment-id="' + card.dataset.id + '"]'); if (mark) mark.classList.add('fb-pulse'); }); sb.addEventListener('mouseout', function (e) { var card = e.target.closest('.fb-card'); if (!card) return; if (e.relatedTarget && card.contains(e.relatedTarget)) return; var mark = document.querySelector('.fb-highlight[data-comment-id="' + card.dataset.id + '"]'); if (mark) mark.classList.remove('fb-pulse'); }); } applySidebarWidth(); var tl = topLevel(); var counts = { unresolved: tl.filter(function (c) { return !c.resolved; }).length, resolved: tl.filter(function (c) { return c.resolved; }).length, all: tl.length, }; var html = '
'; // Header html += 'コメントに表示される名前を入力してください。