MosswartOverlord/docs/superpowers/specs/2026-07-15-inventory-search-redesign-mockup.html
Erik edf76f50db docs(spec): inventory search redesign — React full-page view + approved mockup
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 05:57:10 +02:00

294 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Inventory Search — mockup</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body { background:#111; color:#eee; font-family:"Segoe UI",sans-serif; font-size:13px; display:flex; flex-direction:column; }
/* ── Top bar ── */
.topbar { display:flex; align-items:center; gap:10px; padding:10px 14px; background:#1a1a1a; border-bottom:2px solid #333; }
.topbar .title { color:#88f; font-weight:600; font-size:15px; white-space:nowrap; }
.searchbox { flex:1; max-width:520px; background:#222; border:1px solid #444; border-radius:4px; padding:7px 12px; color:#eee; font-size:13px; outline:none; }
.searchbox:focus { border-color:#88f; }
.searchbox::placeholder { color:#666; }
.btn { background:#333; border:1px solid #555; color:#ccc; border-radius:4px; padding:6px 14px; font-size:12px; cursor:pointer; }
.btn:hover { background:#444; color:#fff; }
.count { margin-left:auto; color:#888; font-size:12px; white-space:nowrap; }
.count b { color:#eee; }
/* ── Chips ── */
.chipsrow { display:flex; align-items:center; flex-wrap:wrap; gap:6px; padding:8px 14px; background:#151515; border-bottom:1px solid #2a2a2a; min-height:37px; }
.chipsrow .lbl { color:#666; font-size:10px; letter-spacing:1px; }
.chip { display:inline-flex; align-items:center; gap:6px; background:#23233a; border:1px solid #55f; border-radius:12px; padding:2px 10px; font-size:11px; color:#bbd; cursor:default; }
.chip .x { color:#c66; cursor:pointer; font-weight:bold; }
.chip .x:hover { color:#f88; }
.chip.gold { border-color:#a80; color:#fc6; background:#2a2418; }
/* ── Main ── */
.main { display:flex; flex:1; overflow:hidden; }
/* ── Sidebar ── */
.sidebar { width:210px; min-width:210px; background:#1a1a1a; border-right:2px solid #333; overflow-y:auto; padding:8px 10px; }
.grp { border-bottom:1px solid #262626; padding:6px 0; }
.grp-head { display:flex; align-items:center; gap:6px; cursor:pointer; color:#999; text-transform:uppercase; font-size:10px; letter-spacing:1px; padding:3px 0; user-select:none; }
.grp-head:hover { color:#ccc; }
.grp-head .arrow { font-size:9px; transition:transform .15s; }
.grp.open .arrow { transform:rotate(90deg); }
.grp-head .badge { margin-left:auto; background:#3a3a6e; color:#cce; border-radius:8px; padding:0 7px; font-size:9px; }
.grp-body { display:none; padding:5px 0 3px; }
.grp.open .grp-body { display:block; }
.minisearch { width:100%; background:#222; border:1px solid #3a3a3a; border-radius:3px; padding:4px 8px; color:#ccc; font-size:11px; outline:none; margin-bottom:5px; }
.minisearch::placeholder { color:#555; }
.fitem { display:flex; align-items:center; gap:6px; padding:2px 0; font-size:12px; color:#bbb; cursor:pointer; }
.fitem:hover { color:#fff; }
.fitem input { accent-color:#88f; }
.fitem .online { width:6px; height:6px; border-radius:50%; background:#4c4; }
.fitem.gold { color:#fc6; }
.subhead { color:#666; font-size:9px; text-transform:uppercase; letter-spacing:1px; margin:6px 0 2px; }
.linky { color:#88f; font-size:10px; cursor:pointer; }
.linky:hover { text-decoration:underline; }
.range { display:flex; gap:4px; align-items:center; margin:2px 0; }
.range label { flex:1; color:#999; font-size:11px; }
.range input { width:52px; background:#222; border:1px solid #3a3a3a; border-radius:3px; color:#ccc; padding:2px 5px; font-size:11px; }
/* ── Results ── */
.results { flex:1; overflow-y:auto; display:flex; flex-direction:column; }
table { width:100%; border-collapse:collapse; }
thead { position:sticky; top:0; background:#191919; z-index:1; }
th { text-align:left; color:#88f; font-size:11px; font-weight:600; padding:8px 10px; border-bottom:2px solid #333; cursor:pointer; white-space:nowrap; user-select:none; }
th:hover { color:#aaf; }
th.sorted::after { content:" ▼"; font-size:9px; }
td { padding:6px 10px; border-bottom:1px solid #1e1e1e; font-size:12px; color:#bbb; vertical-align:top; }
tbody tr { cursor:pointer; }
tbody tr:hover td { background:#191922; }
tbody tr.sel td { background:#20203a; }
td .leg { color:#fc6; }
td .spells { color:#889; font-size:11px; }
td.num { text-align:right; font-variant-numeric:tabular-nums; }
.equipped { color:#4c4; font-size:10px; }
.pager { display:flex; align-items:center; gap:8px; padding:8px 14px; color:#888; font-size:12px; border-top:1px solid #262626; background:#151515; margin-top:auto; }
.pager .pg { padding:2px 8px; border:1px solid #333; border-radius:3px; cursor:pointer; }
.pager .pg.cur { background:#3a3a6e; border-color:#88f; color:#fff; }
/* ── Detail panel ── */
.detail { width:250px; min-width:250px; background:#161620; border-left:2px solid #333; overflow-y:auto; padding:12px; }
.detail h3 { color:#88f; font-size:14px; margin-bottom:2px; }
.detail .sub { color:#777; font-size:11px; margin-bottom:10px; }
.kv { display:flex; justify-content:space-between; padding:2px 0; font-size:12px; color:#999; }
.kv b { color:#ddd; font-weight:normal; text-align:right; }
.detail hr { border:none; border-top:1px solid #2a2a35; margin:9px 0; }
.detail .sphead { color:#666; font-size:10px; text-transform:uppercase; letter-spacing:1px; margin-bottom:4px; }
.sp { padding:2px 0; font-size:12px; color:#bbb; }
.sp.leg { color:#fc6; }
.detail .hint { color:#555; font-size:10px; margin-top:12px; }
.closex { float:right; color:#666; cursor:pointer; font-size:14px; }
.closex:hover { color:#f88; }
</style>
</head>
<body>
<div class="topbar">
<span class="title">⚔ Inventory Search</span>
<input class="searchbox" placeholder="Search name or material… (e.g. 'sapphire ring')">
<button class="btn" onclick="document.querySelectorAll('.chip').forEach(c=>c.remove())">Reset</button>
<button class="btn">⚙ Columns</button>
<span class="count"><b>56</b> items · 24 ms</span>
</div>
<div class="chipsrow">
<span class="lbl">ACTIVE</span>
<span class="chip">Jewelry <span class="x" onclick="this.parentNode.remove()">×</span></span>
<span class="chip">Slot: Ring <span class="x" onclick="this.parentNode.remove()">×</span></span>
<span class="chip gold">Legendary Invulnerability <span class="x" onclick="this.parentNode.remove()">×</span></span>
<span class="chip gold">Legendary Summoning <span class="x" onclick="this.parentNode.remove()">×</span></span>
</div>
<div class="main">
<div class="sidebar">
<div class="grp open">
<div class="grp-head" onclick="this.parentNode.classList.toggle('open')"><span class="arrow"></span> Characters <span class="badge">All</span></div>
<div class="grp-body">
<input class="minisearch" placeholder="filter characters…">
<div style="margin-bottom:4px"><span class="linky">All</span> · <span class="linky">None</span> · <span class="linky">Online</span></div>
<div class="fitem"><input type="checkbox" checked> Bank of Sawato</div>
<div class="fitem"><input type="checkbox" checked> Bowgod <span class="online"></span></div>
<div class="fitem"><input type="checkbox" checked> Larsson <span class="online"></span></div>
<div class="fitem"><input type="checkbox" checked> Mrbow</div>
<div class="fitem"><input type="checkbox" checked> Kosmo Kramer <span class="online"></span></div>
<div class="fitem" style="color:#555">… 33 more</div>
</div>
</div>
<div class="grp open">
<div class="grp-head" onclick="this.parentNode.classList.toggle('open')"><span class="arrow"></span> Item type <span class="badge">1</span></div>
<div class="grp-body">
<div class="fitem"><input type="radio" name="t"> All items</div>
<div class="fitem"><input type="radio" name="t"> Armor</div>
<div class="fitem"><input type="radio" name="t" checked> Jewelry</div>
<div class="fitem"><input type="radio" name="t"> Weapons</div>
<div class="fitem"><input type="radio" name="t"> Clothing</div>
</div>
</div>
<div class="grp open">
<div class="grp-head" onclick="this.parentNode.classList.toggle('open')"><span class="arrow"></span> Slots <span class="badge">1</span></div>
<div class="grp-body">
<div class="fitem"><input type="checkbox" checked> Ring</div>
<div class="fitem"><input type="checkbox"> Bracelet</div>
<div class="fitem"><input type="checkbox"> Neck</div>
<div class="fitem"><input type="checkbox"> Trinket</div>
<div class="fitem"><input type="checkbox"> Cloak</div>
<span class="linky">show armor slots ▾</span>
</div>
</div>
<div class="grp open">
<div class="grp-head" onclick="this.parentNode.classList.toggle('open')"><span class="arrow"></span> Cantrips <span class="badge">2</span></div>
<div class="grp-body">
<input class="minisearch" id="cantripSearch" placeholder="find cantrip… e.g. invuln" oninput="filterCantrips(this.value)">
<div class="subhead">Selected</div>
<div class="fitem gold"><input type="checkbox" checked> Invulnerability</div>
<div class="fitem gold"><input type="checkbox" checked> Summoning</div>
<div class="subhead">Attributes</div>
<div class="fitem cf"><input type="checkbox"> Strength</div>
<div class="fitem cf"><input type="checkbox"> Endurance</div>
<div class="fitem cf"><input type="checkbox"> Quickness</div>
<div class="fitem cf"><input type="checkbox"> Focus</div>
<div class="subhead">Skills</div>
<div class="fitem cf"><input type="checkbox"> Heavy Weapon</div>
<div class="fitem cf"><input type="checkbox"> War Magic</div>
<div class="fitem cf"><input type="checkbox"> Defender</div>
<div class="subhead">Defense</div>
<div class="fitem cf"><input type="checkbox"> Impenetrability</div>
<div class="fitem cf"><input type="checkbox"> Magic Resistance</div>
<div class="fitem" style="color:#555">… all 45 in real page</div>
<div class="subhead">Any-tier spell search</div>
<input class="minisearch" placeholder="spell name contains… e.g. Epic Invuln">
</div>
</div>
<div class="grp">
<div class="grp-head" onclick="this.parentNode.classList.toggle('open')"><span class="arrow"></span> Ratings</div>
<div class="grp-body">
<div class="range"><label>Dmg rating ≥</label><input placeholder="min"></div>
<div class="range"><label>Crit dmg ≥</label><input placeholder="min"></div>
<div class="range"><label>Heal boost ≥</label><input placeholder="min"></div>
<div class="range"><label>Vitality ≥</label><input placeholder="min"></div>
<span class="linky">all 20 ratings ▾</span>
</div>
</div>
<div class="grp">
<div class="grp-head" onclick="this.parentNode.classList.toggle('open')"><span class="arrow"></span> Equipment sets</div>
<div class="grp-body"><input class="minisearch" placeholder="find set…"></div>
</div>
<div class="grp">
<div class="grp-head" onclick="this.parentNode.classList.toggle('open')"><span class="arrow"></span> Item state</div>
<div class="grp-body">
<div class="fitem"><input type="checkbox"> Equipped only</div>
<div class="fitem"><input type="checkbox"> Bonded</div>
<div class="fitem"><input type="checkbox"> Rare</div>
</div>
</div>
<div class="grp">
<div class="grp-head" onclick="this.parentNode.classList.toggle('open')"><span class="arrow"></span> Reqs & value</div>
<div class="grp-body">
<div class="range"><label>Wield lvl ≤</label><input placeholder="max"></div>
<div class="range"><label>Value ≥</label><input placeholder="min"></div>
<div class="range"><label>Workmanship ≥</label><input placeholder="min"></div>
</div>
</div>
</div>
<div class="results">
<table>
<thead>
<tr><th>Item</th><th>Character</th><th>Slot</th><th class="num sorted">Value</th><th>Wield</th><th>Spells / Cantrips</th></tr>
</thead>
<tbody id="rows"></tbody>
</table>
<div class="pager">
<span class="pg"></span><span class="pg cur">1</span><span class="pg">2</span><span class="pg">3</span><span class="pg"></span>
<span style="margin-left:auto">200 / page</span>
</div>
</div>
<div class="detail" id="detail"></div>
</div>
<script>
const items = [
{ name:"Gold Ring", char:"Bowgod", slot:"Ring", value:24510, wield:"180", equipped:false, burden:50, work:"—", mana:"142/200",
spells:[["Legendary Invulnerability",1],["Legendary Summoning Prowess",1],["Major Heavy Weapon Aptitude",0],["Blessing of the Arrow Turner",0]] },
{ name:"Sapphire Ring", char:"Larsson", slot:"Ring", value:18200, wield:"150", equipped:true, burden:50, work:"—", mana:"98/180",
spells:[["Legendary Invulnerability",1],["Epic Focus",0],["Minor Impenetrability",0]] },
{ name:"Bronze Ring", char:"Mrbow", slot:"Ring", value:9340, wield:"125", equipped:false, burden:50, work:"—", mana:"77/120",
spells:[["Legendary Invulnerability",1],["Epic Willpower",0]] },
{ name:"Diamond Ring", char:"Kosmo Kramer", slot:"Ring", value:31000, wield:"200", equipped:false, burden:50, work:"—", mana:"200/200",
spells:[["Legendary Invulnerability",1],["Legendary Willpower",1],["Epic Mana Conversion Prowess",0]] },
{ name:"Ivory Ring", char:"Bank of Sawato", slot:"Ring", value:12750, wield:"150", equipped:false, burden:50, work:"—", mana:"64/150",
spells:[["Legendary Invulnerability",1],["Major Void Magic Aptitude",0]] },
{ name:"Black Opal Ring", char:"Bowgod", slot:"Ring", value:27890, wield:"180", equipped:true, burden:50, work:"—", mana:"180/220",
spells:[["Legendary Invulnerability",1],["Legendary Critical Damage",1]] },
];
function render() {
const tb = document.getElementById('rows');
tb.innerHTML = items.map((it,i) => `
<tr onclick="sel(${i})" id="r${i}">
<td>${it.name}${it.equipped ? ' <span class="equipped">⚔</span>' : ''}</td>
<td>${it.char}</td><td>${it.slot}</td>
<td class="num">${it.value.toLocaleString()}</td><td>${it.wield}</td>
<td class="spells">${it.spells.map(([s,l]) => l ? `<span class="leg">${s}</span>` : s).join(', ')}</td>
</tr>`).join('');
}
function sel(i) {
document.querySelectorAll('tbody tr').forEach(r=>r.classList.remove('sel'));
document.getElementById('r'+i).classList.add('sel');
const it = items[i];
document.getElementById('detail').innerHTML = `
<span class="closex" onclick="document.getElementById('detail').innerHTML=hint">×</span>
<h3>${it.name}</h3>
<div class="sub">${it.slot} · ${it.equipped ? '⚔ Equipped' : '📦 Inventory'}</div>
<div class="kv"><span>Character</span><b>${it.char}</b></div>
<div class="kv"><span>Value</span><b>${it.value.toLocaleString()}</b></div>
<div class="kv"><span>Burden</span><b>${it.burden}</b></div>
<div class="kv"><span>Wield req</span><b>Level ${it.wield}</b></div>
<div class="kv"><span>Workmanship</span><b>${it.work}</b></div>
<div class="kv"><span>Mana</span><b>${it.mana}</b></div>
<hr>
<div class="sphead">Spells (${it.spells.length})</div>
${it.spells.map(([s,l]) => `<div class="sp${l ? ' leg' : ''}">${s}</div>`).join('')}
<div class="hint">↑/↓ next item · Esc close</div>`;
}
function filterCantrips(q) {
q = q.toLowerCase();
document.querySelectorAll('.cf').forEach(el => {
el.style.display = el.textContent.toLowerCase().includes(q) ? '' : 'none';
});
}
const hint = '<div class="hint">Click a row to inspect an item.</div>';
document.getElementById('detail').innerHTML = hint;
render();
sel(0);
document.addEventListener('keydown', e => {
const cur = document.querySelector('tbody tr.sel');
if (!cur) return;
const i = +cur.id.slice(1);
if (e.key === 'ArrowDown' && i < items.length-1) { sel(i+1); e.preventDefault(); }
if (e.key === 'ArrowUp' && i > 0) { sel(i-1); e.preventDefault(); }
if (e.key === 'Escape') { document.querySelectorAll('tbody tr').forEach(r=>r.classList.remove('sel')); document.getElementById('detail').innerHTML = hint; }
});
</script>
</body>
</html>