update
This commit is contained in:
+10
-116
@@ -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, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
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}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user