Agent Terminal Replay
A dark terminal window replays an agent searching, comparing prices and making calls line by line, with each call’s cost right-aligned and an accent balance tag in the title bar. It pauses briefly after finishing and loops.
Taken from the page template Qishu Agent Tool Platform Home. Use it on its own, or combine it with the other components from the same template to rebuild the full page.
Plain HTML, CSS and a little vanilla JS, with no framework dependency. Copy the prompt into Claude Code, Cursor, Trae or Codex and the model adds Agent Terminal Replay to your project in your colors.
Where to use it
Scroll, hover and entrance motion that gives a page rhythm; animation uses only transform and opacity.
Install
With Builder, install it with the 8pt CLI or call it directly from an editor connected to 8pt MCP.
Code
Source for Agent Terminal Replay. Motion uses only transform and opacity and respects the Reduce motion setting.
<div class="x-qtr"><div class="qs-term" data-qs-term><div class="qs-term__bar"><span class="qs-dots"><i></i><i></i><i></i></span><span class="qs-term__path">~/agents/调研助手</span><span class="qs-term__bal">余额 · ¥ —</span></div><ol class="qs-term__body"><li class="qs-tl qs-tl--cmd" style="--d:0"><span class="qs-tl__p">$</span><span class="qs-tl__t">对比两款云笔记产品:定位、定价、增长</span><span class="qs-tl__c"></span></li><li class="qs-tl qs-tl--dim" style="--d:1"><span class="qs-tl__p">›</span><span class="qs-tl__t">qs.find("竞品对比")</span><span class="qs-tl__c"></span></li><li class="qs-tl qs-tl--dim" style="--d:2"><span class="qs-tl__p">›</span><span class="qs-tl__t">→ 候选若干 · 按匹配度与价格排序</span><span class="qs-tl__c"></span></li><li class="qs-tl qs-tl--dim" style="--d:3"><span class="qs-tl__p">›</span><span class="qs-tl__t">1. 竞品透镜 /compare · ¥—/次</span><span class="qs-tl__c"></span></li><li class="qs-tl qs-tl--dim" style="--d:4"><span class="qs-tl__p">›</span><span class="qs-tl__t">2. 远望 /search · ¥—/次</span><span class="qs-tl__c"></span></li><li class="qs-tl qs-tl--dim" style="--d:5"><span class="qs-tl__p">›</span><span class="qs-tl__t">3. 人脉库 /company/enrich · ¥—/条</span><span class="qs-tl__c"></span></li><li class="qs-tl qs-tl--dim" style="--d:6"><span class="qs-tl__p">›</span><span class="qs-tl__t">qs.inspect("竞品透镜") → 接口结构 · 计价正常</span><span class="qs-tl__c"></span></li><li class="qs-tl qs-tl--run" style="--d:7"><span class="qs-tl__p">→</span><span class="qs-tl__t">qs.call("竞品透镜", "/compare")</span><span class="qs-tl__c">¥—</span></li><li class="qs-tl qs-tl--ok" style="--d:8"><span class="qs-tl__p">✓</span><span class="qs-tl__t">定位 · 定价 · 能力差异 已生成</span><span class="qs-tl__c"></span></li><li class="qs-tl qs-tl--run" style="--d:9"><span class="qs-tl__p">→</span><span class="qs-tl__t">qs.call("远望", "/search")</span><span class="qs-tl__c">¥—</span></li></ol></div></div>
.x-qtr{--acc:#0016D7;--ink:#1c1c1e;--mut:#6e6e73;--line:#ececee;--soft:#fafafa;
--sans:"Inter","PingFang SC","Hiragino Sans GB","Noto Sans CJK SC","Microsoft YaHei",system-ui,sans-serif;
--mono:ui-monospace,"SFMono-Regular","JetBrains Mono",Menlo,Consolas,"DejaVu Sans Mono",monospace;
font-family:var(--sans);color:var(--ink);-webkit-font-smoothing:antialiased;line-height:1.5;width:100%;box-sizing:border-box;text-align:left}.x-qtr *,.x-qtr *::before,.x-qtr *::after{box-sizing:border-box;margin:0;padding:0}.x-qtr a{color:inherit;text-decoration:none}.x-qtr button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}.x-qtr .qs-wrap{max-width:1100px;margin:0 auto;padding:0 24px}.x-qtr .qs-h2{font-size:clamp(30px,4.4vw,52px);font-weight:800;letter-spacing:-.035em;line-height:1.08;text-align:center}.x-qtr .qs-mark{display:inline-grid;grid-template-columns:repeat(2,6px);gap:2px;transform:rotate(45deg);margin:0 2px}.x-qtr .qs-mark i{width:6px;height:6px;border-radius:1.5px;background:currentColor;display:block}.x-qtr .qs-mark i:nth-child(4){opacity:.35}.x-qtr .qs-term{max-width:880px;margin:0 auto;background:#121214;border-radius:14px;box-shadow:0 40px 80px -40px rgba(0,0,30,.55),0 0 0 1px rgba(255,255,255,.04) inset;overflow:hidden;text-align:left}.x-qtr .qs-term__bar{display:flex;align-items:center;gap:16px;padding:14px 16px;border-bottom:1px solid #222226}.x-qtr .qs-dots{display:flex;gap:6px}.x-qtr .qs-dots i{width:9px;height:9px;border-radius:50%;background:#3a3a40;display:block}.x-qtr .qs-term__path{font:12px var(--mono);color:#77777f;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.x-qtr .qs-term__bal{font:12px var(--mono);color:#fff;background:var(--acc);padding:5px 10px;border-radius:3px;white-space:nowrap}.x-qtr .qs-term__body{list-style:none;padding:22px 20px 34px;min-height:300px;font:12.5px/1.95 var(--mono);color:#8b8b93}.x-qtr .qs-tl{display:flex;gap:10px;opacity:0;transform:translateY(6px);transition:opacity .4s ease,transform .4s ease;transition-delay:calc(var(--d) * .32s + .3s)}.x-qtr .qs-term.is-in .qs-tl{opacity:1;transform:none}.x-qtr .qs-tl__p{flex:none;width:12px;color:#5d5d66}.x-qtr .qs-tl__t{flex:1;min-width:0;overflow-wrap:anywhere}.x-qtr .qs-tl__c{flex:none;color:#cfcfd6}.x-qtr .qs-tl--cmd .qs-tl__t{color:#d4d4da}.x-qtr .qs-tl--run .qs-tl__t{color:#fff}.x-qtr .qs-tl--ok,.x-qtr .qs-tl--ok .qs-tl__p{color:#34d399}@media (max-width:560px){.x-qtr .qs-term__body{font-size:11.5px;padding:18px 14px 26px}.x-qtr .qs-term__path{display:none}}@media (prefers-reduced-motion:reduce){.x-qtr .qs-tl{opacity:1;transform:none;transition:none}}.x-qtr{padding:64px 24px;min-height:min(860px,96vh);display:flex;flex-direction:column;justify-content:center;background:radial-gradient(rgba(140,150,255,.22) 1px,transparent 1.3px) 0 0/18px 18px;-webkit-mask-image:radial-gradient(75% 85% at 50% 50%,#000 55%,transparent 100%);mask-image:radial-gradient(75% 85% at 50% 50%,#000 55%,transparent 100%)}.x-qtr .qs-term{border:1px solid rgba(255,255,255,.09);box-shadow:0 40px 90px -40px rgba(0,22,215,.45)}
(root) => {
const el = (root.classList && root.classList.contains('x-qtr')) ? root : (root.querySelector('.x-qtr') || root);
const offs = [];
const term = el.querySelector('.qs-term');
const RM = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches;
let tm = 0;
const play = () => { term.classList.add('is-in'); if (!RM) tm = setTimeout(reset, 9000); };
const reset = () => { term.classList.remove('is-in'); tm = setTimeout(play, 700); };
tm = setTimeout(play, 60);
offs.push(() => clearTimeout(tm));
return () => offs.forEach((f) => f());
}