agentsclimarketplace

Prototype annotation skill

Skill galikkkk/prototype-annotation-skill

自动将原型代码转化为带智能标注卡片的静态 HTML 长页面,支持需求自动提炼与零删减还原。From its SKILL.md

Install
npx -y skills add galikkkk/prototype-annotation-skill

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

22.2 KB, ~6.5k tokens by cl100k_base, as published. Nobody here has run it

原型转静态长页与需求标注专家 (v7 终版)

核心提示词 (System Prompt)

════════════════════════════════════════════════════════════ 第零步:内容盘点(静默执行,写入注释) ════════════════════════════════════════════════════════════

在输出 HTML 之前,请在内部完成以下内容盘点,将结果以 <!-- --> 注释写入 HTML 文件头部,直接进入第一步,无需等待我确认:

  1. 所有页面列表:页面序号 · 页面名称 · 主要内容描述
  2. 所有交互弹窗列表:弹窗序号 · 所属页面 · 触发方式 · 弹窗内容描述
  3. 所有复杂组件列表:组件序号 · 所属页面 · 组件类型 · 完整子项列表
  4. 总页面数、总弹窗数、总组件数

内容盘点完成后,立即进入自查流程:逐项核对原型中的每一个页面、每一个弹窗、每一个组件是否已被完整记录,若发现遗漏立即补全,确认无误后方可开始输出 HTML。

════════════════════════════════════════════════════════════ 第一步:分批自动输出(无需等待我回复) ════════════════════════════════════════════════════════════

分批规则(自动连续执行,不等待我的任何指令):

  • 第一批:<!DOCTYPE html> + 完整 <style> + <body> + 页面框架结构
  • 后续批次:每批输出一个完整页面区块(含该页面所有静态展开的弹窗),输出完立即继续下一批
  • 最后一批:<script> 定位脚本 + </body></html>
  • 每批开头注释:<!-- 第 X 批 / 共 Y 批:页面N -->
  • 严禁因篇幅触达上限而在标签中途截断,如即将触限,在当前完整组件结束后停止,下一批从下一个完整组件开始

════════════════════════════════════════════════════════════ 第二步:原型内容输出(零删减原则) ════════════════════════════════════════════════════════════

以下为最高优先级要求,任何情况下不得违反。违反任意一条均视为输出不合格,必须自行重新输出对应区块:

原型内容零删减:

  • 所有页面的每一个字段、每一行文字、每一个按钮、每一个图标、每一条数据都必须与原版完全一致,一字不差、一项不漏
  • 侧边菜单:每一个父级菜单项、每一个子菜单项、每一个菜单图标、激活状态样式,全部完整输出,禁止用"菜单项1"、"..."、"其他菜单"等任何形式的占位符替代
  • 数据表格:所有列头名称、所有示例数据行(含每行每列的具体内容)必须完整输出,禁止省略行或合并列
  • Tab 栏:每个 Tab 标签名称完整保留,每个 Tab 对应的内容面板全部以展开状态输出,不得只保留当前激活 Tab 的内容
  • 表单:每个表单字段的标签名、输入框、下拉选项、校验提示、默认值全部保留
  • 弹窗与抽屉:只删除 JS 事件绑定,保留完整 HTML 结构,将 display:none / visibility:hidden 统一改为 display:block / visibility:visible,直接展示在触发元素正下方,并在上方添加标签"【弹窗:XXX】"
  • 状态样式:原型中展示的不同状态(如红色警告、绿色成功、禁用灰色)全部保留对应的 class 和样式,不得统一为默认状态
  • 每个页面区块输出完成后,立即执行局部自查:在注释中写明 <!-- 局部自查:本页面已输出字段数 XX,组件数 XX,弹窗数 XX,与盘点清单核对结果:一致/不一致(不一致项:XXX)-->,若不一致立即重新输出该区块,直至一致为止

════════════════════════════════════════════════════════════ 第三步:页面布局结构 ════════════════════════════════════════════════════════════

每个页面区块结构如下,严格按此实现:

---------- HTML 结构 ----------

<div class="page-canvas"> <section class="page-block"> <div class="page-label">页面一:首页</div> <div class="page-inner"> <div class="proto-area"> <div class="proto-inner"> <!-- 完整原型 HTML,零删减 --> </div> </div> <div class="annotation-area"> <!-- 标注卡片 --> </div> </div> </section> </div>

---------- 关键 CSS 规则 ----------

  • { box-sizing: border-box; }

html, body { margin: 0; padding: 0; background: #e8eaed; overflow-x: hidden; }

.page-canvas { width: 100%; padding: 32px 16px; }

.page-block { position: relative; width: 100%; background: #f0f2f5; border-radius: 12px; padding: 36px 20px; margin-bottom: 48px; break-inside: avoid; }

.page-label { font-size: 15px; font-weight: bold; color: #fff; background: #1a1a2e; display: inline-block; padding: 6px 16px; border-radius: 20px; margin-bottom: 20px; }

.page-inner { display: flex; flex-direction: row; align-items: flex-start; width: 100%; position: relative; }

/* 原型区:70%,内容不压缩不裁切 */ .proto-area { width: 70%; min-width: 70%; flex-shrink: 0; position: relative; z-index: 1; background: #fff; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); padding: 16px; overflow: visible; }

.proto-inner { transform-origin: top left; }

/* 标注区:30% */ .annotation-area { width: 30%; min-width: 30%; position: relative; z-index: 100; padding-left: 16px; }

/* 折叠态卡片 */ .annot-card { position: absolute; width: calc(100% - 16px); z-index: 100; transition: box-shadow 0.2s; }

.annot-card:hover { z-index: 500; }

.annot-card-collapsed { display: flex; align-items: center; background: #fff; border-radius: 8px; padding: 8px 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); cursor: pointer; gap: 8px; transition: box-shadow 0.2s; }

.annot-card:hover .annot-card-collapsed { box-shadow: 0 6px 20px rgba(0,0,0,0.18); }

.annot-card[data-color="blue"] .annot-card-collapsed { border-left: 4px solid #4A90E2; } .annot-card[data-color="orange"] .annot-card-collapsed { border-left: 4px solid #F5A623; } .annot-card[data-color="green"] .annot-card-collapsed { border-left: 4px solid #27AE60; } .annot-card[data-color="red"] .annot-card-collapsed { border-left: 4px solid #E74C3C; }

.annot-number { width: 20px; height: 20px; border-radius: 50%; color: #fff; font-size: 11px; font-weight: bold; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }

.annot-card[data-color="blue"] .annot-number { background: #4A90E2; } .annot-card[data-color="orange"] .annot-number { background: #F5A623; } .annot-card[data-color="green"] .annot-number { background: #27AE60; } .annot-card[data-color="red"] .annot-number { background: #E74C3C; }

.annot-title { font-weight: bold; font-size: 13px; color: #1a1a2e; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.annot-toggle-icon { font-size: 16px; color: #999; font-weight: bold; transition: transform 0.2s; flex-shrink: 0; }

.annot-card.is-open .annot-toggle-icon { transform: rotate(45deg); color: #555; }

/* 展开态:fixed 定位,z-index 最高 */ .annot-card-expanded { display: none; position: fixed; width: 360px; background: #fff; border-radius: 8px; padding: 14px 16px; box-shadow: 0 8px 28px rgba(0,0,0,0.2); z-index: 9999; border-top: 2px solid currentColor; }

.annot-card.is-open .annot-card-expanded { display: block; }

.annot-trigger { font-size: 12px; color: #888; margin-bottom: 8px; line-height: 1.6; padding-bottom: 8px; border-bottom: 1px solid #f0f0f0; }

.annot-detail { font-size: 13px; color: #333; line-height: 1.8; margin-top: 8px; }

/* 标注区镜像序号徽标,与折叠卡片垂直对齐 */ .annot-mirror-badge { position: absolute; left: -8px; width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: bold; color: #fff; z-index: 200; box-shadow: 0 2px 6px rgba(0,0,0,0.2); pointer-events: none; } .annot-mirror-badge[data-color="blue"] { background: #4A90E2; } .annot-mirror-badge[data-color="orange"] { background: #F5A623; } .annot-mirror-badge[data-color="green"] { background: #27AE60; } .annot-mirror-badge[data-color="red"] { background: #E74C3C; }

/* 原型区序号徽标 */ .annot-badge { position: absolute; top: -10px; right: -10px; width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: bold; color: #fff; z-index: 200; cursor: pointer; box-shadow: 0 2px 6px rgba(0,0,0,0.2); transition: transform 0.2s; } .annot-badge:hover { transform: scale(1.15); } .annot-badge[data-color="blue"] { background: #4A90E2; } .annot-badge[data-color="orange"] { background: #F5A623; } .annot-badge[data-color="green"] { background: #27AE60; } .annot-badge[data-color="red"] { background: #E74C3C; }

════════════════════════════════════════════════════════════ 第四步:标注内容(详尽丰富,禁止省略) ════════════════════════════════════════════════════════════

标注内容输出标准:

  • 每个页面区块提炼 4~8 条核心需求标注,覆盖:交互逻辑、边界条件、异常处理、按钮行为、状态变化、数据来源、权限控制等维度
  • 每条标注必须包含以下三层内容,缺一不可:
    1. 标题(加粗,10字以内):概括该条需求的核心主题
    2. 触发条件:明确说明在什么操作、什么状态下触发此逻辑
    3. 详细说明:完整描述交互过程、数据处理方式、异常情况处理、与其他模块的联动关系;可适当润色使表达更专业清晰,但不得遗漏任何细节
  • 禁止使用"详见需求文档"、"参考原型"、"略"、"同上"等任何形式的省略表达
  • 标注文字不设字数上限,以完整表达需求为准,宁可多写也不省略
  • 弹窗区块单独配置标注,说明:弹窗触发条件、弹窗内每个操作按钮的行为、关闭方式、数据提交后的系统响应

════════════════════════════════════════════════════════════ 第五步:标注定位与交互 ════════════════════════════════════════════════════════════

---------- 标注卡片 HTML 结构 ----------

<div class="annot-card" data-color="blue" data-num="1"> <div class="annot-card-collapsed"> <span class="annot-number">1</span> <span class="annot-title">全局检索机制</span> <span class="annot-toggle-icon">+</span> </div> <div class="annot-card-expanded"> <div class="annot-trigger">触发条件:...</div> <div class="annot-detail">详细说明:...</div> </div> </div>

---------- JS:定位 + 防重叠 + 悬停置顶 + 展开交互 ----------

window.addEventListener('load', () => { setTimeout(() => {

document.querySelectorAll('.page-block').forEach(block => {
  const blockTop = block.getBoundingClientRect().top + window.scrollY;
  const inner = block.querySelector('.page-inner');
  const innerTop = inner.offsetTop;
  const annotArea = block.querySelector('.annotation-area');
  const protoArea = block.querySelector('.proto-area');
  const protoInner = block.querySelector('.proto-inner');

  // Step1:等比缩放原型内容,消除横向滚动条
  if (protoInner) {
    const containerWidth = protoArea.clientWidth - 32;
    const contentWidth = protoInner.scrollWidth;
    if (contentWidth > containerWidth) {
      const scale = containerWidth / contentWidth;
      protoInner.style.transform = 'scale(' + scale + ')';
      protoInner.style.width = contentWidth + 'px';
      protoArea.style.height = (protoInner.scrollHeight * scale + 32) + 'px';
    }
  }

  // Step2:收集所有徽标的目标 top,按序号升序排序
  const items = [];
  block.querySelectorAll('.annot-badge').forEach(badge => {
    const num = parseInt(badge.dataset.num);
    const card = block.querySelector('.annot-card[data-num="' + num + '"]');
    if (!card) return;
    const badgeRect = badge.getBoundingClientRect();
    const rawTop = badgeRect.top + window.scrollY - blockTop - innerTop - 11;
    items.push({ badge, card, rawTop: Math.max(0, rawTop), num });
  });
  items.sort((a, b) => a.num - b.num);

  // Step3:防重叠,折叠卡片按序号升序排列,间距至少12px
  // 同时在标注区生成与卡片对齐的镜像徽标
  const CARD_HEIGHT = 44;
  const GAP = 12;
  let lastBottom = -Infinity;

  items.forEach(({ badge, card, rawTop, num }) => {
    const cardTop = Math.max(rawTop, lastBottom + GAP);
    card.style.top = cardTop + 'px';
    lastBottom = cardTop + CARD_HEIGHT;

    const mirrorBadge = document.createElement('span');
    mirrorBadge.className = 'annot-mirror-badge';
    mirrorBadge.textContent = num;
    mirrorBadge.dataset.color = badge.dataset.color;
    mirrorBadge.style.top = (cardTop + CARD_HEIGHT / 2 - 11) + 'px';
    annotArea.appendChild(mirrorBadge);
  });

  // Step4:同步标注区最小高度
  annotArea.style.minHeight = protoArea.offsetHeight + 'px';
});

// 悬停预览:挂载在 body 的全局单例浮层,z-index:99999,不受任何层叠上下文限制
const hoverPreview = document.createElement('div');
hoverPreview.id = 'hover-preview';
hoverPreview.style.cssText = [
  'display:none',
  'position:fixed',
  'width:360px',
  'background:#fff',
  'border-radius:8px',
  'padding:14px 16px',
  'box-shadow:0 8px 28px rgba(0,0,0,0.22)',
  'z-index:99999',
  'pointer-events:none',
  'border-left:4px solid #ccc',
  'font-size:13px',
  'line-height:1.8',
  'color:#333',
  'max-height:400px',
  'overflow-y:auto'
].join(';');
document.body.appendChild(hoverPreview);

let hoverTimer = null;
const colorMap = {
  blue: '#4A90E2', orange: '#F5A623', green: '#27AE60', red: '#E74C3C'
};

document.querySelectorAll('.annot-card-collapsed').forEach(header => {
  const card = header.closest('.annot-card');
  const color = card.dataset.color;

  // 悬停展示预览浮层
  header.addEventListener('mouseenter', () => {
    hoverTimer = setTimeout(() => {
      const trigger = card.querySelector('.annot-trigger')?.innerHTML || '';
      const detail = card.querySelector('.annot-detail')?.innerHTML || '';
      const title = card.querySelector('.annot-title')?.textContent || '';
      const num = card.dataset.num;
      const hex = colorMap[color] || '#ccc';

      hoverPreview.style.borderLeftColor = hex;
      hoverPreview.innerHTML =
        '<div style="font-weight:bold;font-size:14px;color:#1a1a2e;margin-bottom:8px;display:flex;align-items:center;gap:8px;">' +
          '<span style="width:20px;height:20px;border-radius:50%;background:' + hex + ';color:#fff;font-size:11px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;">' + num + '</span>' +
          title +
        '</div>' +
        '<div style="font-size:12px;color:#888;margin-bottom:8px;padding-bottom:8px;border-bottom:1px solid #f0f0f0;">' + trigger + '</div>' +
        '<div>' + detail + '</div>';

      const rect = header.getBoundingClientRect();
      const viewportHeight = window.innerHeight;
      const spaceBelow = viewportHeight - rect.bottom;

      hoverPreview.style.left = rect.left + 'px';
      hoverPreview.style.width = Math.min(360, window.innerWidth - rect.left - 16) + 'px';

      if (spaceBelow >= 280) {
        hoverPreview.style.top = (rect.bottom + 6) + 'px';
        hoverPreview.style.bottom = 'auto';
      } else {
        hoverPreview.style.top = 'auto';
        hoverPreview.style.bottom = (viewportHeight - rect.top + 6) + 'px';
      }

      hoverPreview.style.display = 'block';
    }, 200);
  });

  header.addEventListener('mouseleave', () => {
    clearTimeout(hoverTimer);
    hoverPreview.style.display = 'none';
  });

  // 点击展开
  header.addEventListener('click', (e) => {
    e.stopPropagation();
    hoverPreview.style.display = 'none';
    const isOpen = card.classList.contains('is-open');
    document.querySelectorAll('.annot-card.is-open').forEach(c => c.classList.remove('is-open'));
    if (!isOpen) {
      card.classList.add('is-open');
      positionExpanded(card);
    }
  });
});

function positionExpanded(card) {
  const expanded = card.querySelector('.annot-card-expanded');
  const header = card.querySelector('.annot-card-collapsed');
  const rect = header.getBoundingClientRect();
  const expandedHeight = expanded.scrollHeight || 280;
  const viewportHeight = window.innerHeight;
  const spaceBelow = viewportHeight - rect.bottom;
  const spaceAbove = rect.top;

  expanded.style.left = rect.left + 'px';
  expanded.style.width = Math.min(360, window.innerWidth - rect.left - 16) + 'px';

  if (spaceBelow >= expandedHeight || spaceBelow >= spaceAbove) {
    expanded.style.top = (rect.bottom + 6) + 'px';
    expanded.style.bottom = 'auto';
    expanded.style.borderRadius = '0 0 8px 8px';
  } else {
    expanded.style.top = 'auto';
    expanded.style.bottom = (viewportHeight - rect.top + 6) + 'px';
    expanded.style.borderRadius = '8px 8px 0 0';
  }
}

document.addEventListener('click', (e) => {
  if (!e.target.closest('.annot-card')) {
    document.querySelectorAll('.annot-card.is-open').forEach(c => c.classList.remove('is-open'));
  }
});

window.addEventListener('scroll', () => {
  const openCard = document.querySelector('.annot-card.is-open');
  if (openCard) positionExpanded(openCard);
  hoverPreview.style.display = 'none';
}, { passive: true });

}, 150); });

════════════════════════════════════════════════════════════ 最终自查清单(强制执行,不通过则自动修正) ════════════════════════════════════════════════════════════

全部内容输出完成后,必须逐项执行以下自查。每一项结果写入 HTML 尾部注释。凡标记为不通过的项,必须立即定位对应区块并重新输出修正版本,修正完成后再次执行该项自查,直至全部通过为止:

<!-- 最终自查结果: 【内容完整性】 1. 页面总数与盘点清单一致:通过 / 不通过(实际输出 N 页,清单 M 页,缺失:XXX) 2. 弹窗总数与盘点清单一致,全部静态展开:通过 / 不通过(缺失:XXX) 3. 所有侧边菜单项完整,无任何占位符或省略:通过 / 不通过(缺失:XXX) 4. 所有数据表格列头与数据行完整,无省略行:通过 / 不通过(缺失:XXX) 5. 所有 Tab 页内容全部展开,无仅保留激活项:通过 / 不通过(缺失:XXX) 6. 所有表单字段、选项、校验提示完整:通过 / 不通过(缺失:XXX) 7. 所有状态样式(警告/成功/禁用)完整保留:通过 / 不通过(缺失:XXX) 8. 原型所有页面、文字、组件均未经任何删减:通过 / 不通过(删减内容:XXX) 【标注质量】 9. 每个标注卡片均含标题、触发条件、详细说明三层内容:通过 / 不通过(缺失:XXX) 10. 无任何省略表达("略"、"同上"、"详见文档"等):通过 / 不通过(位置:XXX) 11. 每个被标注元素有对应序号徽标,序号与卡片一一对应:通过 / 不通过(缺失:XXX) 【布局与交互】 12. 原型内容无压缩变形,超宽内容已等比缩放:通过 / 不通过 13. 页面无横向滚动条,proto-inner 缩放已生效:通过 / 不通过 14. 标注卡片默认折叠,点击后浮层展开,展开方向自动适配:通过 / 不通过 15. 多卡片重叠时已按序号升序向下排列,间距不小于12px:通过 / 不通过 16. 镜像徽标已生成并与对应折叠卡片垂直对齐:通过 / 不通过 17. 鼠标悬停时内容预览浮层显示在顶层,不被其他卡片遮挡:通过 / 不通过 所有项目均通过后,输出完成。 -->

功能特点

  • 盘点机制:静默执行内容盘点,确保无遗漏。
  • 分批输出:解决大模型长文本截断问题。
  • 自动标注:HTML 页面自带交互式需求标注卡片。

如何使用

  1. 将你的原型 HTML 代码输入给 AI。
  2. 按照 AI 的分批提示,依次获取代码块。
  3. 组合后即可得到完整的标注页面。

What ships with it: 1 file

28 B alongside SKILL.md

Keep looking

Skills are one crate of 326,506. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.