product-planning(同步 26 改 65 新)
- 目录约定:docs/pm/<项目>/ ⇒ <工作区>/项目/<产品slug>/ 为产品根,下分 产品规划/ 与 产品原型/{designs,pages}
- ④段骨架六节 ⇒ 七节(新增「原型地址」;后两节改「页面导航」「原型状态取值」)
- ③段新增真机组件库;结案「源站只有暗色主题」,tokens.css(亮色)标为不可用于生产
- ③段改版规则反转为「一个产品只用一套原型、只留当前态」;②段新增 产品规划模板.md(表头唯一定义处)
- scripts:产品根定位重写(含 .registry/current-project 指针),新增 check_terms.py 与 selftest_product_root.py
session-mechanism(同步 3 改 2 新)
- references/06-技能优化规程.md 新增 §5「要求落在生成时看得见的面上」,总表 16 ⇒ 17 条;SKILL.md 同步
- scripts/collabctl.py 取回 _find_board() 候选定位(旧版写死 scripts/board.py,而看板已搬进 boards/会话看板/ ⇒ 旧版本就是坏的)
element-picker(新装)
- 按用户口径作「独立功能加入 product-planning」,非独立全局技能
- 修掉它自身三处路径漂移(技能根写法 ⇒ 同目录;README 两处旧落点;剪贴板示例文案与代码对齐)
工具归拢到技能级 assets/
- diagram-design / design-capture / element-picker / video-capture 从各段 assets/ 移到技能包 assets/
- 新增红线:本包有两处 assets/(技能级工具箱 vs 段内资产),引工具一律写「技能级 assets/<工具名>/」
- 路径口径由两类改三类(技能级工具箱 / 本段自带资产 / 同根技能)
不入库(磁盘保留)
- design-system-tiaoyue 整个目录(用户 2026-10-16 令):git rm -r --cached + .gitignore,理由=14 MB 且可 CDP 重采;
已在 ③段 SKILL.md 注明「取仓后这一层是空的属正常,用自带套需另取或重采」
- element-picker 的运行期存档 marks/(服务自己会建目录)
143 lines
13 KiB
HTML
143 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Package rank by quarter · Bump chart</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
<style>
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
:root {
|
|
--color-paper: #2d3142;
|
|
--color-ink: #f5f5f5;
|
|
--color-muted: #bfc0c0;
|
|
--color-accent: #f08a59;
|
|
--font-sans: 'Geist', system-ui, sans-serif;
|
|
--font-serif: 'Instrument Serif', serif;
|
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
|
}
|
|
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
|
|
.frame { max-width: 1200px; width: 100%; }
|
|
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
|
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
|
|
svg { width: 100%; min-width: 760px; display: block; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="frame">
|
|
<p class="eyebrow">Bump chart · Diagram Design</p>
|
|
<h1>The default everyone stopped choosing</h1>
|
|
|
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="bump-dark-title bump-dark-desc">
|
|
<title id="bump-dark-title">The default everyone stopped choosing</title>
|
|
<desc id="bump-dark-desc">Bump chart ranking six internal packages by weekly downloads across four quarters; legacy-http slides from first to sixth while fetch-kit takes the top rank in the second quarter and keeps it.</desc>
|
|
<defs>
|
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
|
<circle cx="1" cy="1" r="0.9" fill="rgba(245,245,245,0.10)"/>
|
|
</pattern>
|
|
</defs>
|
|
|
|
<rect width="100%" height="100%" fill="#2d3142"/>
|
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
|
|
|
<!-- Rank is ORDINAL: every vertex sits exactly on a rank row, y = 88 + 56 x
|
|
(rank - 1), and every snapshot's ranks are a permutation of 1..6.
|
|
scripts/verify-bump.py enforces both against the ranks each series
|
|
declares - there is no tolerance, because rank has no fractions. -->
|
|
<text transform="rotate(-90 24 228)" x="24" y="228" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">RANK BY WEEKLY DOWNLOADS · 1 = MOST</text>
|
|
|
|
<!-- One axis rule per snapshot. No gridlines: the dots are the readable
|
|
positions and each series prints its first and last rank. -->
|
|
<line x1="320" y1="64" x2="320" y2="392" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
|
|
<text data-axis="0" data-state="Q1" x="320" y="416" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">Q1</text>
|
|
<line x1="440" y1="64" x2="440" y2="392" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
|
|
<text data-axis="1" data-state="Q2" x="440" y="416" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">Q2</text>
|
|
<line x1="560" y1="64" x2="560" y2="392" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
|
|
<text data-axis="2" data-state="Q3" x="560" y="416" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">Q3</text>
|
|
<line x1="680" y1="64" x2="680" y2="392" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
|
|
<text data-axis="3" data-state="Q4" x="680" y="416" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">Q4</text>
|
|
|
|
<!-- Straight segments between adjacent snapshots - never splines, which
|
|
invent a trajectory between ranks that were only measured quarterly.
|
|
Ramp ordered by Q1 rank: opacity records the opening order. -->
|
|
<path data-series="authx" data-ranks="2,3,3,2" d="M320,144 L440,200 L560,200 L680,144" fill="none" stroke="rgba(245,245,245,0.80)" stroke-width="1.2"/>
|
|
<circle cx="320" cy="144" r="3" fill="rgba(245,245,245,0.80)"/>
|
|
<circle cx="440" cy="200" r="3" fill="rgba(245,245,245,0.80)"/>
|
|
<circle cx="560" cy="200" r="3" fill="rgba(245,245,245,0.80)"/>
|
|
<circle cx="680" cy="144" r="3" fill="rgba(245,245,245,0.80)"/>
|
|
<text data-series="authx" data-end="first" data-role="name" x="272" y="147.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif" text-anchor="end">authx</text>
|
|
<text data-series="authx" data-end="first" data-role="rank" x="304" y="147.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end">#2</text>
|
|
<text data-series="authx" data-end="last" data-role="rank" x="696" y="147.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">#2</text>
|
|
<text data-series="authx" data-end="last" data-role="name" x="728" y="147.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif">authx</text>
|
|
|
|
<path data-series="fetch-kit" data-ranks="3,1,1,1" d="M320,200 L440,88 L560,88 L680,88" fill="none" stroke="rgba(245,245,245,0.75)" stroke-width="1.2"/>
|
|
<circle cx="320" cy="200" r="3" fill="rgba(245,245,245,0.75)"/>
|
|
<circle cx="440" cy="88" r="3" fill="rgba(245,245,245,0.75)"/>
|
|
<circle cx="560" cy="88" r="3" fill="rgba(245,245,245,0.75)"/>
|
|
<circle cx="680" cy="88" r="3" fill="rgba(245,245,245,0.75)"/>
|
|
<text data-series="fetch-kit" data-end="first" data-role="name" x="272" y="203.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif" text-anchor="end">fetch-kit</text>
|
|
<text data-series="fetch-kit" data-end="first" data-role="rank" x="304" y="203.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end">#3</text>
|
|
<text data-series="fetch-kit" data-end="last" data-role="rank" x="696" y="91.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">#1</text>
|
|
<text data-series="fetch-kit" data-end="last" data-role="name" x="728" y="91.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif">fetch-kit</text>
|
|
|
|
<path data-series="queuelib" data-ranks="4,5,5,4" d="M320,256 L440,312 L560,312 L680,256" fill="none" stroke="rgba(245,245,245,0.70)" stroke-width="1.2"/>
|
|
<circle cx="320" cy="256" r="3" fill="rgba(245,245,245,0.70)"/>
|
|
<circle cx="440" cy="312" r="3" fill="rgba(245,245,245,0.70)"/>
|
|
<circle cx="560" cy="312" r="3" fill="rgba(245,245,245,0.70)"/>
|
|
<circle cx="680" cy="256" r="3" fill="rgba(245,245,245,0.70)"/>
|
|
<text data-series="queuelib" data-end="first" data-role="name" x="272" y="259.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif" text-anchor="end">queuelib</text>
|
|
<text data-series="queuelib" data-end="first" data-role="rank" x="304" y="259.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end">#4</text>
|
|
<text data-series="queuelib" data-end="last" data-role="rank" x="696" y="259.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">#4</text>
|
|
<text data-series="queuelib" data-end="last" data-role="name" x="728" y="259.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif">queuelib</text>
|
|
|
|
<path data-series="logfmt" data-ranks="5,4,2,3" d="M320,312 L440,256 L560,144 L680,200" fill="none" stroke="rgba(245,245,245,0.66)" stroke-width="1.2"/>
|
|
<circle cx="320" cy="312" r="3" fill="rgba(245,245,245,0.66)"/>
|
|
<circle cx="440" cy="256" r="3" fill="rgba(245,245,245,0.66)"/>
|
|
<circle cx="560" cy="144" r="3" fill="rgba(245,245,245,0.66)"/>
|
|
<circle cx="680" cy="200" r="3" fill="rgba(245,245,245,0.66)"/>
|
|
<text data-series="logfmt" data-end="first" data-role="name" x="272" y="315.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif" text-anchor="end">logfmt</text>
|
|
<text data-series="logfmt" data-end="first" data-role="rank" x="304" y="315.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end">#5</text>
|
|
<text data-series="logfmt" data-end="last" data-role="rank" x="696" y="203.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">#3</text>
|
|
<text data-series="logfmt" data-end="last" data-role="name" x="728" y="203.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif">logfmt</text>
|
|
|
|
<path data-series="schema-gen" data-ranks="6,6,6,5" d="M320,368 L440,368 L560,368 L680,312" fill="none" stroke="rgba(245,245,245,0.62)" stroke-width="1.2"/>
|
|
<circle cx="320" cy="368" r="3" fill="rgba(245,245,245,0.62)"/>
|
|
<circle cx="440" cy="368" r="3" fill="rgba(245,245,245,0.62)"/>
|
|
<circle cx="560" cy="368" r="3" fill="rgba(245,245,245,0.62)"/>
|
|
<circle cx="680" cy="312" r="3" fill="rgba(245,245,245,0.62)"/>
|
|
<text data-series="schema-gen" data-end="first" data-role="name" x="272" y="371.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif" text-anchor="end">schema-gen</text>
|
|
<text data-series="schema-gen" data-end="first" data-role="rank" x="304" y="371.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end">#6</text>
|
|
<text data-series="schema-gen" data-end="last" data-role="rank" x="696" y="315.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">#5</text>
|
|
<text data-series="schema-gen" data-end="last" data-role="name" x="728" y="315.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif">schema-gen</text>
|
|
|
|
<!-- legacy-http FOCAL - and it is the series that FELL. The accent marks the
|
|
editorially focal line, never the winner. Focus is carried by stroke
|
|
weight, not tone, for the reason the slopegraph section gives: the
|
|
accent measures 2.86:1 on light paper. Drawn last so its descent
|
|
crosses over, not under, the lines it overtakes downward. -->
|
|
<path data-series="legacy-http" data-ranks="1,2,4,6" d="M320,88 L440,144 L560,256 L680,368" fill="none" stroke="#f08a59" stroke-width="2.4"/>
|
|
<circle cx="320" cy="88" r="4" fill="#f08a59"/>
|
|
<circle cx="440" cy="144" r="4" fill="#f08a59"/>
|
|
<circle cx="560" cy="256" r="4" fill="#f08a59"/>
|
|
<circle cx="680" cy="368" r="4" fill="#f08a59"/>
|
|
<text data-series="legacy-http" data-end="first" data-role="name" x="272" y="91.5" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="end">legacy-http</text>
|
|
<text data-series="legacy-http" data-end="first" data-role="rank" x="304" y="91.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end">#1</text>
|
|
<text data-series="legacy-http" data-end="last" data-role="rank" x="696" y="371.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">#6</text>
|
|
<text data-series="legacy-http" data-end="last" data-role="name" x="728" y="371.5" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif">legacy-http</text>
|
|
|
|
<!-- Legend -->
|
|
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
|
|
<text x="40" y="478" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
|
<text x="960" y="478" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.06em" text-anchor="end">RANKED BY WEEKLY DOWNLOADS, TIES KEEP PRIOR ORDER · SAME MEASURE EVERY QUARTER · ILLUSTRATIVE FIGURES, NOT A MEASURED REGISTRY</text>
|
|
|
|
<line x1="40" y1="492" x2="64" y2="492" stroke="#f08a59" stroke-width="2.4"/>
|
|
<circle cx="52" cy="492" r="4" fill="#f08a59"/>
|
|
<text x="72" y="496" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">legacy-http · focal, first to sixth in a year</text>
|
|
<line x1="320" y1="492" x2="344" y2="492" stroke="rgba(245,245,245,0.80)" stroke-width="1.2"/>
|
|
<circle cx="332" cy="492" r="3" fill="rgba(245,245,245,0.80)"/>
|
|
<text x="352" y="496" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Other packages · opacity records the Q1 order</text>
|
|
</svg>
|
|
</div>
|
|
</body>
|
|
</html>
|