This commit is contained in:
2026-05-18 18:04:16 +09:00
parent b627224308
commit c7a7fe0937
139 changed files with 135625 additions and 139 deletions
+10 -116
View File
@@ -381,7 +381,7 @@ const sessionsData = visibleSessions.map((session, idx) => {
e.tsStr || '',
]);
return { title, entries };
return { title, chatCount, entries };
});
// ─── Build output HTML ────────────────────────────────────────────────────────
@@ -425,6 +425,10 @@ summary::-webkit-details-marker { display: none; }
summary::before { content: '▶ '; font-size: 0.75em; }
details[open] > summary::before { content: '▼ '; }
details[open] { background: #fff; }
details.tier-10 > summary { background: #ddeeff; }
details.tier-50 > summary { background: #d0f0d0; }
details.tier-100 > summary { background: #fff0b0; }
details.tier-200 > summary { background: #ffd0a0; }
.session-body { padding: 4px 6px; }
.loading { color: #999; font-style: italic; padding: 8px; }
.chat, .notice {
@@ -498,6 +502,11 @@ for (let i = 0; i < SESSIONS.length; i++) {
const s = SESSIONS[i];
const det = document.createElement('details');
det.dataset.idx = i;
const cc = SESSIONS[i].chatCount;
if (cc >= 200) det.classList.add('tier-200');
else if (cc >= 100) det.classList.add('tier-100');
else if (cc >= 50) det.classList.add('tier-50');
else if (cc >= 10) det.classList.add('tier-10');
const sum = document.createElement('summary');
sum.textContent = s.title;
@@ -571,119 +580,4 @@ for (let i = 0; i < targets.length; i++) {
if (targets.length > 1) {
console.log(`\n${'='.repeat(60)}`);
console.log(`Done. Processed ${targets.length} rooms.`);
generateIndex(targets);
}
// ─── Generate index.html ──────────────────────────────────────────────────────
function generateIndex(targets) {
const base = process.cwd();
const indexFile = path.join(base, 'index.html');
// 各部屋のメタ情報を収集
const rooms = targets.map(({ roomDir, outputFile }) => {
const roomName = path.basename(roomDir);
const mergedPath = outputFile;
const relPath = path.relative(base, mergedPath).replace(/\\/g, '/');
const exists = require('fs').existsSync(mergedPath);
// _merged.html から統計を取得(簡易スキャン)
let fileCount = 0, entryCount = 0, sessionCount = 0;
if (exists) {
const content = fs.readFileSync(mergedPath, 'utf8');
const m = content.match(/ファイル数: ([\d,]+) \s*総エントリ: ([\d,]+) \s*セッション数: ([\d,]+)/);
if (m) {
fileCount = parseInt(m[1].replace(/,/g, ''));
entryCount = parseInt(m[2].replace(/,/g, ''));
sessionCount = parseInt(m[3].replace(/,/g, ''));
}
}
return { roomName, relPath, exists, fileCount, entryCount, sessionCount };
});
const rowsHtml = rooms.map(r => {
const nameEsc = r.roomName.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
const link = r.exists
? `<a href="${r.relPath}">${nameEsc}</a>`
: `<span style="color:#999">${nameEsc}</span>`;
return `<tr>
<td>${link}</td>
<td class="num">${r.fileCount || '-'}</td>
<td class="num">${r.entryCount ? r.entryCount.toLocaleString() : '-'}</td>
<td class="num">${r.sessionCount || '-'}</td>
</tr>`;
}).join('\n');
const totalEntries = rooms.reduce((s, r) => s + (r.entryCount || 0), 0);
const totalSessions = rooms.reduce((s, r) => s + (r.sessionCount || 0), 0);
const now = new Date();
const updatedAt = `${now.getFullYear()}/${String(now.getMonth()+1).padStart(2,'0')}/${String(now.getDate()).padStart(2,'0')} ${String(now.getHours()).padStart(2,'0')}:${String(now.getMinutes()).padStart(2,'0')}`;
const indexHtml = `<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>luvul ログ一覧</title>
<link href="style/font.css" rel="stylesheet" type="text/css">
<link href="style/style.css" rel="stylesheet" type="text/css">
<link href="style/style_luvul.css" rel="stylesheet" type="text/css">
<style>
body { font-size: 13px; margin: 16px; }
h1 { font-size: 1.3em; margin: 0 0 6px; color: #333; }
.stats { color: #666; font-size: 0.9em; margin: 0 0 12px; }
#search-bar { margin: 0 0 8px; display:flex; gap:6px; align-items:center; }
#search-bar input { flex:1; max-width:320px; padding:4px 6px; font-size:13px; border:1px solid #ccc; border-radius:3px; }
table { border-collapse: collapse; width: 100%; max-width: 820px; }
th, td { border: 1px solid #ddd; padding: 5px 8px; }
th { background: #e8e8f0; text-align: left; white-space: nowrap; }
td.num { text-align: right; white-space: nowrap; color: #555; }
tr:nth-child(even) td { background: #fafafa; }
tr:hover td { background: #f0f4ff; }
tr.hidden-by-search { display: none; }
a { color: #25a; text-decoration: none; }
a:hover { text-decoration: underline; }
.updated { color: #aaa; font-size: 0.85em; margin-top: 12px; }
</style>
</head>
<body>
<h1>luvul ログ一覧</h1>
<p class="stats">
部屋数: ${rooms.length}
総エントリ: ${totalEntries.toLocaleString()}
総セッション数: ${totalSessions}
</p>
<div id="search-bar">
<input type="text" id="q" placeholder="部屋名で絞り込み…" oninput="filterRooms()">
</div>
<table>
<thead>
<tr>
<th>部屋名</th>
<th>ファイル数</th>
<th>総エントリ</th>
<th>セッション数</th>
</tr>
</thead>
<tbody id="room-list">
${rowsHtml}
</tbody>
</table>
<p class="updated">最終更新: ${updatedAt}</p>
<script>
function filterRooms() {
const q = document.getElementById('q').value.trim().toLowerCase();
for (const tr of document.querySelectorAll('#room-list tr')) {
const text = tr.textContent.toLowerCase();
tr.classList.toggle('hidden-by-search', !!q && !text.includes(q));
}
}
</script>
</body>
</html>`;
fs.writeFileSync(indexFile, indexHtml, 'utf8');
console.log(`\nIndex: ${indexFile}`);
}