quick-view

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Quick View

快速查看

Generate minimal HTML to review structured data in a browser. Minimal styling, maximum readability.
生成极简HTML,以便在浏览器中查看结构化数据。极简样式,极致可读性。

When to Use

适用场景

  • User wants to review output that's hard to read in terminal
  • Lists, tables, drafts, summaries that benefit from visual layout
  • User says: "show me", "view this", "make reviewable", "open as webpage"
  • 用户希望查看在终端中难以阅读的输出内容
  • 列表、表格、草稿、摘要等受益于可视化布局的内容
  • 用户说出:“show me”“view this”“make reviewable”“open as webpage”

Output Rules

输出规则

DO:
  • Semantic HTML:
    <table>
    ,
    <ul>
    ,
    <details>
    ,
    <pre>
    ,
    <h1-3>
  • Use the base template with CSS variables
  • Write to
    _private/views/
  • Open with
    open _private/views/{filename}
DO NOT:
  • Add decorative styling beyond the base template
  • Use CSS frameworks
  • Over-engineer or "make it nice"
执行要求:
  • 使用语义化HTML:
    <table>
    <ul>
    <details>
    <pre>
    <h1-3>
  • 使用带有CSS变量的基础模板
  • 写入至
    _private/views/
    目录
  • 使用
    open _private/views/{filename}
    命令打开
禁止操作:
  • 在基础模板之外添加装饰性样式
  • 使用CSS框架
  • 过度设计或刻意“美化”页面

File Naming

文件命名规则

Views have a lifecycle: temporary → keeper → archived.
StageFilenameWhen
Temporary
name-temp.html
Default for new views
Keeper
name.html
User says "keep this", "this is good"
Archived
name.2025-01-01.html
Previous keeper when promoting new one
Rules:
  1. Always create with
    -temp
    suffix
    — Every new view starts as
    name-temp.html
  2. Promote on approval — When user approves, rename to
    name.html
  3. Archive before replacing — If
    name.html
    exists, rename to
    name.DATE.html
    before promoting
  4. Never regenerate keepers — Only regenerate
    -temp
    files
Workflow:
undefined
视图文件有三个生命周期阶段:临时文件 → 保留文件 → 归档文件。
阶段文件名格式使用时机
临时
name-temp.html
新视图文件的默认命名
保留
name.html
用户说出“keep this”“this is good”时
归档
name.2025-01-01.html
升级新的保留文件时,将旧的保留文件重命名归档
规则细节:
  1. 所有新视图初始均为临时文件 — 每个新视图都以
    name-temp.html
    的形式创建
  2. 经批准后升级为保留文件 — 当用户确认后,重命名为
    name.html
  3. 替换前先归档旧文件 — 如果
    name.html
    已存在,在升级新文件前需将其重命名为
    name.DATE.html
    归档
  4. 永不重新生成保留文件 — 仅重新生成临时文件
工作流示例:
undefined

First iteration

第一次迭代

drafts-temp.html ← created
drafts-temp.html ← 创建完成

User: "keep this"

用户:“keep this”

drafts.html ← promoted (temp deleted)
drafts.html ← 升级为保留文件(临时文件删除)

Later iteration

后续迭代

drafts-temp.html ← new temp created drafts.html ← keeper untouched
drafts-temp.html ← 创建新的临时文件 drafts.html ← 保留文件保持不变

User: "this is better, keep it"

用户:“this is better, keep it”

drafts.2025-01-01.html ← old keeper archived drafts.html ← new keeper promoted

**Trigger phrases for promotion:**

- "keep this", "this is good", "save this"
- "make this the default", "lock this in"
- "I like this one"
drafts.2025-01-01.html ← 旧保留文件归档 drafts.html ← 新临时文件升级为保留文件

**触发文件升级的关键词:**

- "keep this"、"this is good"、"save this"
- "make this the default"、"lock this in"
- "I like this one"

Base Template

基础模板

Every quick-view HTML file:
html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{title}</title>
  <style>
    :root {
      --bg: #fff;
      --text: #222;
      --muted: #666;
      --border: #ddd;
      --accent: #1976d2;
    }
    @media (prefers-color-scheme: dark) {
      :root {
        --bg: #1a1a1a;
        --text: #e0e0e0;
        --muted: #999;
        --border: #333;
        --accent: #64b5f6;
      }
    }
    body {
      max-width: 800px;
      margin: 40px auto;
      padding: 0 20px;
      font-family: system-ui;
      background: var(--bg);
      color: var(--text);
    }
    table { border-collapse: collapse; width: 100%; }
    td, th { border: 1px solid var(--border); padding: 8px; text-align: left; }
    .meta { color: var(--muted); font-size: 0.875rem; margin-bottom: 1rem; }
    details { margin: 0.5rem 0; }
    summary { cursor: pointer; }
    pre {
      background: var(--border);
      padding: 1rem;
      overflow-x: auto;
      border-radius: 4px;
    }

    /* Long content truncation */
    .truncate {
      max-height: 200px;
      overflow: hidden;
      position: relative;
    }
    .truncate.expanded { max-height: none; }
    .truncate:not(.expanded)::after {
      content: '';
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      height: 60px;
      background: linear-gradient(transparent, var(--bg));
    }
    .expand-btn {
      color: var(--accent);
      background: none;
      border: none;
      cursor: pointer;
      padding: 0.5rem 0;
      font-size: 0.875rem;
    }

    /* Type borders */
    .type-user { border-left: 3px solid var(--accent); padding-left: 1rem; }
    .type-draft { border-left: 3px solid #ff9800; padding-left: 1rem; }
    .type-done { border-left: 3px solid #4caf50; padding-left: 1rem; }

    /* Source attribution */
    .source { color: var(--muted); font-size: 0.75rem; }
    .source a { color: var(--muted); }
    .source a:hover { color: var(--accent); }
  </style>
</head>
<body>
<p class="meta">Generated: {timestamp} · {count} items</p>
{content}
<script>
// Truncation toggle
document.querySelectorAll('.truncate').forEach(el => {
  if (el.scrollHeight > 220) {
    const btn = document.createElement('button');
    btn.className = 'expand-btn';
    btn.textContent = 'Show more';
    btn.onclick = () => {
      el.classList.toggle('expanded');
      btn.textContent = el.classList.contains('expanded') ? 'Show less' : 'Show more';
    };
    el.after(btn);
  } else {
    el.classList.add('expanded'); // No truncation needed
  }
});
</script>
</body>
</html>
每个快速查看HTML文件均使用以下模板:
html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{title}</title>
  <style>
    :root {
      --bg: #fff;
      --text: #222;
      --muted: #666;
      --border: #ddd;
      --accent: #1976d2;
    }
    @media (prefers-color-scheme: dark) {
      :root {
        --bg: #1a1a1a;
        --text: #e0e0e0;
        --muted: #999;
        --border: #333;
        --accent: #64b5f6;
      }
    }
    body {
      max-width: 800px;
      margin: 40px auto;
      padding: 0 20px;
      font-family: system-ui;
      background: var(--bg);
      color: var(--text);
    }
    table { border-collapse: collapse; width: 100%; }
    td, th { border: 1px solid var(--border); padding: 8px; text-align: left; }
    .meta { color: var(--muted); font-size: 0.875rem; margin-bottom: 1rem; }
    details { margin: 0.5rem 0; }
    summary { cursor: pointer; }
    pre {
      background: var(--border);
      padding: 1rem;
      overflow-x: auto;
      border-radius: 4px;
    }

    /* Long content truncation */
    .truncate {
      max-height: 200px;
      overflow: hidden;
      position: relative;
    }
    .truncate.expanded { max-height: none; }
    .truncate:not(.expanded)::after {
      content: '';
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      height: 60px;
      background: linear-gradient(transparent, var(--bg));
    }
    .expand-btn {
      color: var(--accent);
      background: none;
      border: none;
      cursor: pointer;
      padding: 0.5rem 0;
      font-size: 0.875rem;
    }

    /* Type borders */
    .type-user { border-left: 3px solid var(--accent); padding-left: 1rem; }
    .type-draft { border-left: 3px solid #ff9800; padding-left: 1rem; }
    .type-done { border-left: 3px solid #4caf50; padding-left: 1rem; }

    /* Source attribution */
    .source { color: var(--muted); font-size: 0.75rem; }
    .source a { color: var(--muted); }
    .source a:hover { color: var(--accent); }
  </style>
</head>
<body>
<p class="meta">Generated: {timestamp} · {count} items</p>
{content}
<script>
// Truncation toggle
document.querySelectorAll('.truncate').forEach(el => {
  if (el.scrollHeight > 220) {
    const btn = document.createElement('button');
    btn.className = 'expand-btn';
    btn.textContent = 'Show more';
    btn.onclick = () => {
      el.classList.toggle('expanded');
      btn.textContent = el.classList.contains('expanded') ? 'Show less' : 'Show more';
    };
    el.after(btn);
  } else {
    el.classList.add('expanded'); // No truncation needed
  }
});
</script>
</body>
</html>

Patterns

常用模式

List of items

项目列表

html
<h1>Title</h1>
<ul>
  <li><strong>@username</strong> — action item</li>
</ul>
html
<h1>Title</h1>
<ul>
  <li><strong>@username</strong> — action item</li>
</ul>

Table

表格

html
<table>
  <tr><th>Contact</th><th>Action</th><th>Draft</th></tr>
  <tr><td>@name</td><td>Follow up</td><td>Hey...</td></tr>
</table>
html
<table>
  <tr><th>Contact</th><th>Action</th><th>Draft</th></tr>
  <tr><td>@name</td><td>Follow up</td><td>Hey...</td></tr>
</table>

Expandable sections (for long content)

可展开区域(适用于长内容)

html
<details>
  <summary><strong>@username</strong> — action</summary>
  <div class="truncate">
    <pre>Long content here that may need truncation...</pre>
  </div>
</details>
html
<details>
  <summary><strong>@username</strong> — action</summary>
  <div class="truncate">
    <pre>Long content here that may need truncation...</pre>
  </div>
</details>

Type-differentiated items

类型区分的项目

html
<div class="type-user">User message or input</div>
<div class="type-draft">Draft content</div>
<div class="type-done">Completed item</div>
html
<div class="type-user">User message or input</div>
<div class="type-draft">Draft content</div>
<div class="type-done">Completed item</div>

With actions

带操作按钮的内容

html
<p>
  <a href="tg://resolve?domain=username">Open Telegram</a> ·
  <button onclick="navigator.clipboard.writeText('draft text')">Copy</button>
</p>
html
<p>
  <a href="tg://resolve?domain=username">Open Telegram</a> ·
  <button onclick="navigator.clipboard.writeText('draft text')">Copy</button>
</p>

Sourced data (citations & drill-down)

带来源标注的数据(引用与溯源)

When displaying data gathered from external sources, always include attribution links for drill-down.
Add to base template CSS:
css
.source { color: var(--muted); font-size: 0.75rem; }
.source a { color: var(--muted); }
.source a:hover { color: var(--accent); }
Inline attribution (preferred for lists):
html
<div class="tip">
  <strong>Tip title</strong> — Description of the tip.
  <span class="source"><a href="https://x.com/user/status/123">@username</a></span>
</div>
Table with source column:
html
<table>
  <tr><th>Tip</th><th>Source</th></tr>
  <tr>
    <td>Description here</td>
    <td class="source"><a href="https://x.com/user/status/123">@user</a></td>
  </tr>
</table>
Expandable with source in summary:
html
<details>
  <summary><strong>Tip title</strong> <span class="source"><a href="URL">@source</a></span></summary>
  <p>Full content...</p>
</details>
Meta header with main source:
html
<p class="meta">
  Generated: {timestamp} · {count} items ·
  Source: <a href="https://x.com/user/status/123">Original thread</a>
</p>
Principles:
  • Always link to original when data comes from external sources
  • Use
    @username
    for social media, domain for articles
  • Source links should be muted/subtle, not prominent
  • Include main source in meta header for collections from single source
当展示来自外部来源的数据时,始终需要添加可跳转至原文的来源链接。
在基础模板CSS中添加以下样式:
css
.source { color: var(--muted); font-size: 0.75rem; }
.source a { color: var(--muted); }
.source a:hover { color: var(--accent); }
内联来源标注(列表内容首选):
html
<div class="tip">
  <strong>Tip title</strong> — Description of the tip.
  <span class="source"><a href="https://x.com/user/status/123">@username</a></span>
</div>
带来源列的表格:
html
<table>
  <tr><th>Tip</th><th>Source</th></tr>
  <tr>
    <td>Description here</td>
    <td class="source"><a href="https://x.com/user/status/123">@user</a></td>
  </tr>
</table>
来源标注在摘要中的可展开区域:
html
<details>
  <summary><strong>Tip title</strong> <span class="source"><a href="URL">@source</a></span></summary>
  <p>Full content...</p>
</details>
带主来源的元数据头部:
html
<p class="meta">
  Generated: {timestamp} · {count} items ·
  Source: <a href="https://x.com/user/status/123">Original thread</a>
</p>
原则:
  • 数据来自外部来源时,始终链接至原文
  • 社交媒体使用
    @username
    标注,文章使用域名标注
  • 来源链接应低调不突出
  • 单一来源的合集内容,在元数据头部标注主来源

Editable drafts (with diff tracking)

可编辑草稿(带差异追踪)

For drafts that user may edit before sending. Tracks original vs edited for later analysis.
html
<details>
  <summary><strong>@username</strong> — action <span class="status"></span></summary>
  <pre contenteditable="true"
       data-username="username"
       data-original="Original draft text here"
       onblur="saveDraft(this)">Original draft text here</pre>
  <div class="actions">
    <a href="tg://resolve?domain=username">Open Telegram</a>
    <button onclick="copyDraft(this)">Copy</button>
  </div>
</details>
Include this script block at end of
<body>
(before closing
</body>
tag):
javascript
function saveDraft(el) {
  const key = 'draft_' + el.dataset.username;
  const edited = el.textContent.trim();
  const original = el.dataset.original;
  if (edited !== original) {
    localStorage.setItem(key, edited);
    el.closest('details').querySelector('.status').textContent = '(edited)';
  }
}

function copyDraft(btn) {
  const pre = btn.closest('details').querySelector('pre');
  navigator.clipboard.writeText(pre.textContent.trim());
  btn.textContent = 'Copied!';
  setTimeout(() => btn.textContent = 'Copy', 1500);
}

function restoreEdits() {
  document.querySelectorAll('pre[data-username]').forEach(el => {
    const saved = localStorage.getItem('draft_' + el.dataset.username);
    if (saved) {
      el.textContent = saved;
      el.closest('details').querySelector('.status').textContent = '(edited)';
    }
  });
}

function exportEdits() {
  const edits = [];
  document.querySelectorAll('pre[data-username]').forEach(el => {
    const original = el.dataset.original;
    const current = el.textContent.trim();
    if (original !== current) {
      edits.push({ username: el.dataset.username, original, edited: current });
    }
  });
  if (edits.length === 0) { alert('No edits to export'); return; }
  const blob = new Blob([JSON.stringify({exported_at: new Date().toISOString(), edits}, null, 2)], {type: 'application/json'});
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'draft_edits.json';
  a.click();
}

restoreEdits();
Add export button in header when using editable drafts:
html
<p class="meta">Generated: {timestamp} · {count} drafts · <button onclick="exportEdits()">Export Edits</button></p>
适用于用户可能在发送前编辑的草稿内容,可追踪原文与编辑后的内容以便后续分析。
html
<details>
  <summary><strong>@username</strong> — action <span class="status"></span></summary>
  <pre contenteditable="true"
       data-username="username"
       data-original="Original draft text here"
       onblur="saveDraft(this)">Original draft text here</pre>
  <div class="actions">
    <a href="tg://resolve?domain=username">Open Telegram</a>
    <button onclick="copyDraft(this)">Copy</button>
  </div>
</details>
<body>
标签末尾(
</body>
之前)添加以下脚本块:
javascript
function saveDraft(el) {
  const key = 'draft_' + el.dataset.username;
  const edited = el.textContent.trim();
  const original = el.dataset.original;
  if (edited !== original) {
    localStorage.setItem(key, edited);
    el.closest('details').querySelector('.status').textContent = '(edited)';
  }
}

function copyDraft(btn) {
  const pre = btn.closest('details').querySelector('pre');
  navigator.clipboard.writeText(pre.textContent.trim());
  btn.textContent = 'Copied!';
  setTimeout(() => btn.textContent = 'Copy', 1500);
}

function restoreEdits() {
  document.querySelectorAll('pre[data-username]').forEach(el => {
    const saved = localStorage.getItem('draft_' + el.dataset.username);
    if (saved) {
      el.textContent = saved;
      el.closest('details').querySelector('.status').textContent = '(edited)';
    }
  });
}

function exportEdits() {
  const edits = [];
  document.querySelectorAll('pre[data-username]').forEach(el => {
    const original = el.dataset.original;
    const current = el.textContent.trim();
    if (original !== current) {
      edits.push({ username: el.dataset.username, original, edited: current });
    }
  });
  if (edits.length === 0) { alert('No edits to export'); return; }
  const blob = new Blob([JSON.stringify({exported_at: new Date().toISOString(), edits}, null, 2)], {type: 'application/json'});
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'draft_edits.json';
  a.click();
}

restoreEdits();
使用可编辑草稿时,在头部添加导出按钮:
html
<p class="meta">Generated: {timestamp} · {count} drafts · <button onclick="exportEdits()">Export Edits</button></p>

Workflow

工作流

  1. Identify the data to display (file, variable, recent output)
  2. Choose pattern: list, table, or expandable sections
  3. Generate HTML using template above
  4. Write to
    _private/views/{name}-temp.html
  5. Run
    open _private/views/{name}-temp.html
  6. If user approves, promote to
    {name}.html
  1. 确定需要展示的数据(文件、变量、最新输出)
  2. 选择合适的模式:列表、表格或可展开区域
  3. 使用上述模板生成HTML
  4. 写入至
    _private/views/{name}-temp.html
  5. 执行
    open _private/views/{name}-temp.html
    命令
  6. 若用户确认,将文件升级为
    {name}.html

Example

示例

User: "show me the drafts"
Claude:
  1. Reads
    _private/drafts/outreach_drafts.md
  2. Parses each draft (heading = contact, body = draft)
  3. Generates HTML with
    <details>
    for each draft
  4. Writes to
    _private/views/drafts-temp.html
  5. Runs
    open _private/views/drafts-temp.html
Result: Browser opens, user sees expandable list of drafts with auto dark/light mode, long content truncated with "Show more", can copy each one.
User: "this looks good, keep it"
Claude:
  1. Renames
    drafts-temp.html
    drafts.html
  2. Confirms: "Saved as drafts.html"
用户:“show me the drafts”
Claude执行步骤:
  1. 读取
    _private/drafts/outreach_drafts.md
    文件
  2. 解析每个草稿(标题为联系人,正文为草稿内容)
  3. 为每个草稿生成带
    <details>
    标签的HTML
  4. 写入至
    _private/views/drafts-temp.html
  5. 执行
    open _private/views/drafts-temp.html
    命令
结果:浏览器打开页面,用户可看到可展开的草稿列表,支持自动深色/浅色模式,长内容会被截断并显示“Show more”按钮,还可复制每个草稿内容。
用户:“this looks good, keep it”
Claude执行步骤:
  1. drafts-temp.html
    重命名为
    drafts.html
  2. 回复确认:“Saved as drafts.html”

Styling Handoff

样式交接

This skill produces functional HTML with minimal styling. For full visual styling, invoke the
html-style
skill after generating.
Classes used by quick-view (compatible with html-style):
ClassPurpose
.type-user
User input/message
.type-draft
Draft content
.type-done
Completed item
.source
Attribution links
.meta
Metadata header
.truncate
Long content container
.actions
Action button container
Data attributes for JS hooks:
  • data-username
    — Identifier for drafts
  • data-original
    — Original text for diff tracking
本技能生成的是具备基础功能的HTML,仅包含极简样式。如需完整的视觉样式,可在生成后调用
html-style
技能。
快速视图使用的类名(与html-style兼容):
类名用途
.type-user
用户输入/消息
.type-draft
草稿内容
.type-done
已完成项目
.source
来源链接
.meta
元数据头部
.truncate
长内容容器
.actions
操作按钮容器
用于JS钩子的属性:
  • data-username
    — 草稿的标识
  • data-original
    — 用于差异追踪的原文内容

Attribution

致谢

Truncation pattern and CSS variables approach inspired by simon willison's claude-code-transcripts.
截断内容的实现模式与CSS变量的使用方式灵感来自simon willison的claude-code-transcripts