specpipe 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1319 -0
- package/bin/devkit.js +3 -0
- package/package.json +61 -0
- package/src/cli.js +76 -0
- package/src/commands/check.js +33 -0
- package/src/commands/diff.js +84 -0
- package/src/commands/init-adopt.js +54 -0
- package/src/commands/init-agents.js +118 -0
- package/src/commands/init-global.js +102 -0
- package/src/commands/init.js +311 -0
- package/src/commands/list.js +54 -0
- package/src/commands/remove.js +133 -0
- package/src/commands/upgrade.js +215 -0
- package/src/lib/agent-guards.js +100 -0
- package/src/lib/agent-install.js +161 -0
- package/src/lib/agents.js +280 -0
- package/src/lib/claude-global.js +183 -0
- package/src/lib/detector.js +93 -0
- package/src/lib/hasher.js +21 -0
- package/src/lib/installer.js +213 -0
- package/src/lib/logger.js +16 -0
- package/src/lib/manifest.js +102 -0
- package/src/lib/reconcile.js +56 -0
- package/templates/.claude/CLAUDE.md +79 -0
- package/templates/.claude/hooks/comment-guard.js +126 -0
- package/templates/.claude/hooks/file-guard.js +216 -0
- package/templates/.claude/hooks/glob-guard.js +104 -0
- package/templates/.claude/hooks/path-guard.sh +118 -0
- package/templates/.claude/hooks/self-review.sh +27 -0
- package/templates/.claude/hooks/sensitive-guard.sh +227 -0
- package/templates/.claude/settings.json +68 -0
- package/templates/docs/WORKFLOW.md +325 -0
- package/templates/docs/specs/.gitkeep +0 -0
- package/templates/hooks/specpipe-read-guard.sh +42 -0
- package/templates/hooks/specpipe-shell-guard.sh +65 -0
- package/templates/rules/specpipe-guards.md +40 -0
- package/templates/scripts/test-hooks.sh +66 -0
- package/templates/skills/sp-build/SKILL.md +776 -0
- package/templates/skills/sp-challenge/SKILL.md +255 -0
- package/templates/skills/sp-commit/SKILL.md +174 -0
- package/templates/skills/sp-explore/SKILL.md +730 -0
- package/templates/skills/sp-fix/SKILL.md +266 -0
- package/templates/skills/sp-humanize/SKILL.md +212 -0
- package/templates/skills/sp-investigate/SKILL.md +648 -0
- package/templates/skills/sp-md-render/SKILL.md +200 -0
- package/templates/skills/sp-md-render/components.md +415 -0
- package/templates/skills/sp-md-render/template.html +283 -0
- package/templates/skills/sp-plan/SKILL.md +947 -0
- package/templates/skills/sp-review/SKILL.md +268 -0
- package/templates/skills/sp-scaffold/SKILL.md +237 -0
- package/templates/skills/sp-scaffold/references/ARCHITECTURE.md.tmpl +228 -0
- package/templates/skills/sp-scaffold/references/DESIGN.md.tmpl +113 -0
- package/templates/skills/sp-scaffold/references/adr/NNNN-template.md +92 -0
- package/templates/skills/sp-scaffold/references/stack-profiles/react.md +36 -0
- package/templates/skills/sp-spec-render/SKILL.md +254 -0
- package/templates/skills/sp-spec-render/components.md +418 -0
- package/templates/skills/sp-spec-render/examples/user-auth.html +749 -0
- package/templates/skills/sp-spec-render/examples/user-auth.md +114 -0
- package/templates/skills/sp-spec-render/template.html +222 -0
- package/templates/skills/sp-voices/SKILL.md +1184 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="{{LANG}}" data-theme="auto">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>{{TITLE}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--bg:#fff; --bg-elev:#f6f8fa; --bg-sunken:#fafbfc;
|
|
10
|
+
--fg:#0a0c10; --fg-muted:#57606a; --fg-subtle:#8c959f;
|
|
11
|
+
--border:#d0d7de; --border-subtle:#e5e9ef;
|
|
12
|
+
--accent:#d97757; --accent-bg:#fff4ef;
|
|
13
|
+
--note:#0969da; --note-bg:#ddf4ff; --note-border:#54aeff;
|
|
14
|
+
--tip:#1a7f37; --tip-bg:#dafbe1; --tip-border:#4ac26b;
|
|
15
|
+
--warn:#9a6700; --warn-bg:#fff8c5; --warn-border:#d4a72c;
|
|
16
|
+
--danger:#cf222e; --danger-bg:#ffebe9; --danger-border:#ff8182;
|
|
17
|
+
--shadow:0 1px 0 rgba(31,35,40,.04);
|
|
18
|
+
--radius:6px; --radius-lg:10px;
|
|
19
|
+
--mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
|
|
20
|
+
--sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
|
|
21
|
+
}
|
|
22
|
+
@media (prefers-color-scheme: dark) {
|
|
23
|
+
:root[data-theme="auto"] {
|
|
24
|
+
--bg:#0d1117; --bg-elev:#161b22; --bg-sunken:#010409;
|
|
25
|
+
--fg:#e6edf3; --fg-muted:#8b949e; --fg-subtle:#6e7681;
|
|
26
|
+
--border:#30363d; --border-subtle:#21262d;
|
|
27
|
+
--accent:#f0a378; --accent-bg:#2a1810;
|
|
28
|
+
--note:#58a6ff; --note-bg:#0c2d6b; --note-border:#1f6feb;
|
|
29
|
+
--tip:#3fb950; --tip-bg:#0d2818; --tip-border:#238636;
|
|
30
|
+
--warn:#d29922; --warn-bg:#2d2611; --warn-border:#9e6a03;
|
|
31
|
+
--danger:#f85149; --danger-bg:#2d0e10; --danger-border:#b62324;
|
|
32
|
+
--shadow:0 1px 0 rgba(0,0,0,.3);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
:root[data-theme="dark"]{--bg:#0d1117;--bg-elev:#161b22;--bg-sunken:#010409;--fg:#e6edf3;--fg-muted:#8b949e;--fg-subtle:#6e7681;--border:#30363d;--border-subtle:#21262d;--accent:#f0a378;--accent-bg:#2a1810;--note:#58a6ff;--note-bg:#0c2d6b;--note-border:#1f6feb;--tip:#3fb950;--tip-bg:#0d2818;--tip-border:#238636;--warn:#d29922;--warn-bg:#2d2611;--warn-border:#9e6a03;--danger:#f85149;--danger-bg:#2d0e10;--danger-border:#b62324;--shadow:0 1px 0 rgba(0,0,0,.3);}
|
|
36
|
+
*{box-sizing:border-box}html,body{margin:0;padding:0}
|
|
37
|
+
body{font-family:var(--sans);font-size:14px;line-height:1.6;color:var(--fg);background:var(--bg);-webkit-font-smoothing:antialiased}
|
|
38
|
+
code,.mono{font-family:var(--mono);font-size:.92em}
|
|
39
|
+
a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}
|
|
40
|
+
.skip{position:absolute;left:-9999px;top:8px;padding:8px 12px;background:var(--accent);color:#fff;border-radius:6px}.skip:focus{left:8px;z-index:1000}
|
|
41
|
+
.progress{position:fixed;top:0;left:0;height:2px;background:var(--accent);width:0;z-index:60;transition:width 80ms linear}
|
|
42
|
+
.topbar{position:sticky;top:0;z-index:50;display:flex;align-items:center;gap:16px;padding:10px 20px;background:var(--bg);border-bottom:1px solid var(--border);font-size:13px}
|
|
43
|
+
.topbar .doc-type{font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.05em;padding:2px 6px;background:var(--accent-bg);color:var(--accent);border-radius:4px}
|
|
44
|
+
.topbar .doc-type.investigation{background:color-mix(in srgb,var(--note) 14%,transparent);color:var(--note)}
|
|
45
|
+
.topbar .doc-type.retro{background:color-mix(in srgb,var(--tip) 14%,transparent);color:var(--tip)}
|
|
46
|
+
.topbar .title{font-weight:600;color:var(--fg);max-width:40%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
47
|
+
.topbar .meta{color:var(--fg-muted);display:flex;gap:14px;flex-wrap:wrap}
|
|
48
|
+
.topbar .meta .sep{color:var(--fg-subtle)}
|
|
49
|
+
.topbar .spacer{flex:1}
|
|
50
|
+
.icon-btn{background:none;border:1px solid var(--border);color:var(--fg-muted);width:32px;height:32px;border-radius:6px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:background 80ms,color 80ms}
|
|
51
|
+
.icon-btn:hover{background:var(--bg-elev);color:var(--fg)}.icon-btn svg{width:16px;height:16px;fill:currentColor}
|
|
52
|
+
.menu-btn{display:none}
|
|
53
|
+
@media (max-width:900px){.menu-btn{display:inline-flex}}
|
|
54
|
+
.layout{display:grid;grid-template-columns:280px minmax(0,1fr);gap:0}
|
|
55
|
+
@media (max-width:900px){.layout{grid-template-columns:1fr}}
|
|
56
|
+
.sidebar{position:sticky;top:53px;align-self:start;height:calc(100vh - 53px);overflow-y:auto;border-right:1px solid var(--border);padding:16px 12px 32px;background:var(--bg)}
|
|
57
|
+
@media (max-width:900px){
|
|
58
|
+
.sidebar{position:fixed;top:53px;left:0;width:280px;z-index:40;transform:translateX(-100%);transition:transform 160ms;box-shadow:0 8px 24px rgba(0,0,0,.15);border-right:1px solid var(--border)}
|
|
59
|
+
.sidebar.open{transform:translateX(0)}
|
|
60
|
+
}
|
|
61
|
+
.toc-search{width:100%;padding:6px 10px;font:inherit;font-size:13px;background:var(--bg-elev);border:1px solid var(--border);border-radius:6px;color:var(--fg);margin-bottom:12px}
|
|
62
|
+
.toc-search:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
|
|
63
|
+
.toc-label{font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--fg-subtle);margin:4px 8px 8px}
|
|
64
|
+
.toc{display:flex;flex-direction:column;gap:1px}
|
|
65
|
+
.toc a{display:block;padding:5px 10px;color:var(--fg-muted);border-radius:4px;font-size:13px;line-height:1.45}
|
|
66
|
+
.toc a:hover{background:var(--bg-elev);color:var(--fg);text-decoration:none}
|
|
67
|
+
.toc a.active{background:var(--accent-bg);color:var(--accent);font-weight:500}
|
|
68
|
+
.toc a.h3{padding-left:24px;font-size:12.5px;color:var(--fg-subtle)}
|
|
69
|
+
main{padding:24px 36px 96px;max-width:920px;min-width:0}
|
|
70
|
+
@media (max-width:600px){main{padding:16px}}
|
|
71
|
+
h1{font-size:28px;font-weight:700;margin:4px 0 6px;letter-spacing:-.015em;line-height:1.25}
|
|
72
|
+
h2{font-size:20px;font-weight:600;margin:40px 0 14px;padding-bottom:6px;border-bottom:1px solid var(--border-subtle);letter-spacing:-.005em}
|
|
73
|
+
h3{font-size:16px;font-weight:600;margin:24px 0 10px}
|
|
74
|
+
h4{font-size:14px;font-weight:600;margin:18px 0 8px;color:var(--fg-muted);text-transform:uppercase;letter-spacing:.04em}
|
|
75
|
+
p{margin:0 0 12px}
|
|
76
|
+
.subtitle{color:var(--fg-muted);margin:0 0 28px;font-size:15px;line-height:1.5}
|
|
77
|
+
ul,ol{margin:0 0 14px;padding-left:22px}
|
|
78
|
+
li{margin:3px 0}
|
|
79
|
+
li>p:last-child{margin-bottom:0}
|
|
80
|
+
.tldr{background:var(--bg-elev);border:1px solid var(--border-subtle);border-left:3px solid var(--accent);border-radius:var(--radius);padding:14px 18px;margin:8px 0 28px}
|
|
81
|
+
.tldr-label{font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.05em;color:var(--accent);text-transform:uppercase;margin-bottom:6px}
|
|
82
|
+
.tldr p{margin:0 0 8px}.tldr ul{margin:6px 0 0;padding-left:18px}.tldr li{margin:2px 0}
|
|
83
|
+
.steps{display:flex;flex-direction:column;gap:10px;margin:0 0 16px;counter-reset:step}
|
|
84
|
+
.step{display:grid;grid-template-columns:32px 1fr;gap:14px;padding:12px 14px;background:var(--bg-elev);border:1px solid var(--border-subtle);border-radius:var(--radius);counter-increment:step}
|
|
85
|
+
.step::before{content:counter(step);grid-row:1/3;display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;background:var(--accent);color:#fff;font-weight:600;font-size:13px;font-family:var(--mono)}
|
|
86
|
+
.step .step-title{font-weight:600;margin:0 0 4px;font-size:14px}
|
|
87
|
+
.step .step-body{font-size:13.5px;color:var(--fg-muted)}
|
|
88
|
+
.step .step-body :is(p,ul,ol){margin:0}
|
|
89
|
+
.callout{display:grid;grid-template-columns:18px 1fr;gap:10px;padding:12px 14px;border:1px solid;border-radius:var(--radius);margin:0 0 14px;font-size:13.5px}
|
|
90
|
+
.callout .ico{flex-shrink:0;width:18px;height:18px;margin-top:2px}
|
|
91
|
+
.callout-title{font-weight:600;margin:0 0 4px;font-size:12px;font-family:var(--mono);letter-spacing:.05em;text-transform:uppercase}
|
|
92
|
+
.callout p{margin:0 0 6px}.callout p:last-child{margin-bottom:0}
|
|
93
|
+
.callout.note{background:var(--note-bg);border-color:var(--note-border)}
|
|
94
|
+
.callout.note .ico,.callout.note .callout-title{color:var(--note)}
|
|
95
|
+
.callout.tip{background:var(--tip-bg);border-color:var(--tip-border)}
|
|
96
|
+
.callout.tip .ico,.callout.tip .callout-title{color:var(--tip)}
|
|
97
|
+
.callout.warn{background:var(--warn-bg);border-color:var(--warn-border)}
|
|
98
|
+
.callout.warn .ico,.callout.warn .callout-title{color:var(--warn)}
|
|
99
|
+
.callout.danger{background:var(--danger-bg);border-color:var(--danger-border)}
|
|
100
|
+
.callout.danger .ico,.callout.danger .callout-title{color:var(--danger)}
|
|
101
|
+
pre.code{position:relative;background:var(--bg-sunken);border:1px solid var(--border-subtle);border-radius:var(--radius);padding:12px 14px;margin:0 0 14px;overflow-x:auto;font-family:var(--mono);font-size:12.5px;line-height:1.5}
|
|
102
|
+
pre.code code{background:none;padding:0;font-size:inherit}
|
|
103
|
+
pre.code .lang{position:absolute;top:6px;left:12px;font-size:10px;color:var(--fg-subtle);text-transform:uppercase;letter-spacing:.05em}
|
|
104
|
+
pre.code:not(:has(.lang)){padding-top:12px}
|
|
105
|
+
pre.code:has(.lang){padding-top:24px}
|
|
106
|
+
.copy-btn{position:absolute;top:6px;right:8px;background:var(--bg-elev);border:1px solid var(--border);color:var(--fg-muted);font-size:11px;padding:2px 8px;border-radius:4px;cursor:pointer;opacity:0;transition:opacity 120ms}
|
|
107
|
+
pre.code:hover .copy-btn{opacity:1}
|
|
108
|
+
.copy-btn:hover{color:var(--fg);background:var(--bg)}
|
|
109
|
+
.copy-btn.ok{color:var(--tip);border-color:var(--tip)}
|
|
110
|
+
code:not(pre code){background:var(--bg-elev);padding:1px 5px;border-radius:3px;border:1px solid var(--border-subtle)}
|
|
111
|
+
.md-table{width:100%;border-collapse:collapse;font-size:13.5px;margin:0 0 14px;display:block;overflow-x:auto}
|
|
112
|
+
.md-table th,.md-table td{text-align:left;padding:8px 12px;border-bottom:1px solid var(--border-subtle);vertical-align:top}
|
|
113
|
+
.md-table th{font-size:11px;font-weight:600;color:var(--fg-subtle);letter-spacing:.04em;text-transform:uppercase;background:var(--bg-sunken)}
|
|
114
|
+
.md-table tr:last-child td{border-bottom:none}
|
|
115
|
+
.compare{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:0 0 14px}
|
|
116
|
+
@media (max-width:700px){.compare{grid-template-columns:1fr}}
|
|
117
|
+
.compare>div{padding:12px 14px;border:1px solid var(--border-subtle);border-radius:var(--radius);background:var(--bg-elev)}
|
|
118
|
+
.compare h4{margin-top:0}
|
|
119
|
+
.compare.pro>div:first-child{border-left:3px solid var(--tip)}
|
|
120
|
+
.compare.pro>div:last-child{border-left:3px solid var(--danger)}
|
|
121
|
+
details.collapsible{border:1px solid var(--border-subtle);border-radius:var(--radius);background:var(--bg-elev);margin:0 0 14px}
|
|
122
|
+
details.collapsible>summary{list-style:none;cursor:pointer;padding:10px 14px;display:flex;align-items:center;gap:8px;font-weight:500;font-size:13.5px}
|
|
123
|
+
details.collapsible>summary::-webkit-details-marker{display:none}
|
|
124
|
+
details.collapsible>summary::before{content:"›";color:var(--fg-subtle);font-size:18px;line-height:1;transition:transform 120ms}
|
|
125
|
+
details.collapsible[open]>summary::before{transform:rotate(90deg)}
|
|
126
|
+
details.collapsible>div{padding:10px 16px 14px;border-top:1px solid var(--border-subtle);background:var(--bg)}
|
|
127
|
+
blockquote{margin:0 0 14px;padding:6px 14px;border-left:3px solid var(--border);color:var(--fg-muted);background:var(--bg-elev);border-radius:0 var(--radius) var(--radius) 0}
|
|
128
|
+
blockquote p:last-child{margin-bottom:0}
|
|
129
|
+
hr{border:none;border-top:1px solid var(--border-subtle);margin:28px 0}
|
|
130
|
+
img{max-width:100%;height:auto;border-radius:var(--radius);border:1px solid var(--border-subtle)}
|
|
131
|
+
.mermaid{display:flex;justify-content:center;margin:0 0 16px;padding:14px;background:var(--bg-elev);border:1px solid var(--border-subtle);border-radius:var(--radius);overflow-x:auto}
|
|
132
|
+
ul.task-list{list-style:none;padding-left:4px}
|
|
133
|
+
.task-item{display:flex;align-items:flex-start;gap:8px;margin:3px 0}
|
|
134
|
+
.task-item input[type=checkbox]{margin:0;accent-color:var(--accent);flex-shrink:0;margin-top:5px;cursor:default}
|
|
135
|
+
.task-item input:checked~span.task-text{color:var(--fg-subtle);text-decoration:line-through}
|
|
136
|
+
del,s{color:var(--fg-subtle)}
|
|
137
|
+
figure{margin:0 0 18px;text-align:center}
|
|
138
|
+
figure img{display:block;margin:0 auto}
|
|
139
|
+
figcaption{font-size:12px;color:var(--fg-subtle);margin-top:6px;font-style:italic}
|
|
140
|
+
sup.fn-ref{font-size:.75em;vertical-align:super;line-height:0}
|
|
141
|
+
sup.fn-ref a{color:var(--accent);padding:0 2px;text-decoration:none}
|
|
142
|
+
sup.fn-ref a:hover{text-decoration:underline}
|
|
143
|
+
section.footnotes{margin-top:40px;padding-top:16px;border-top:1px solid var(--border-subtle);font-size:13px;color:var(--fg-muted)}
|
|
144
|
+
section.footnotes h4{margin:0 0 8px;color:var(--fg-subtle)}
|
|
145
|
+
section.footnotes ol{padding-left:22px}
|
|
146
|
+
section.footnotes li{margin:6px 0}
|
|
147
|
+
section.footnotes li:target{background:var(--accent-bg);border-radius:4px;padding:4px 8px;margin-left:-8px}
|
|
148
|
+
a.fn-back{margin-left:6px;color:var(--accent);text-decoration:none;font-family:var(--mono)}
|
|
149
|
+
a.fn-back:hover{text-decoration:underline}
|
|
150
|
+
h2,h3{position:relative}
|
|
151
|
+
a.anchor{position:absolute;left:-20px;top:50%;transform:translateY(-50%);color:var(--fg-subtle);opacity:0;text-decoration:none;font-weight:400;font-size:.85em;padding:2px 4px;transition:opacity 120ms,color 120ms;font-family:var(--mono)}
|
|
152
|
+
h2:hover a.anchor,h3:hover a.anchor,a.anchor:focus{opacity:1}
|
|
153
|
+
a.anchor:hover{color:var(--accent);text-decoration:none}
|
|
154
|
+
a.anchor.ok{color:var(--tip);opacity:1}
|
|
155
|
+
@media (max-width:700px){a.anchor{position:static;display:inline-block;transform:none;margin-left:6px;opacity:.45}}
|
|
156
|
+
h1[id],h2[id],h3[id]{scroll-margin-top:70px}
|
|
157
|
+
.toc-hidden{display:none!important}
|
|
158
|
+
@media print{
|
|
159
|
+
.topbar,.sidebar,.icon-btn,.copy-btn,.progress{display:none}
|
|
160
|
+
.layout{grid-template-columns:1fr}main{max-width:none;padding:0}
|
|
161
|
+
details.collapsible>summary::before{display:none}
|
|
162
|
+
details{break-inside:avoid}
|
|
163
|
+
details>div{display:block!important;border-top:1px solid var(--border)}
|
|
164
|
+
}
|
|
165
|
+
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}
|
|
166
|
+
</style>
|
|
167
|
+
</head>
|
|
168
|
+
<body>
|
|
169
|
+
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
|
|
170
|
+
<symbol id="i-sun" viewBox="0 0 16 16"><path d="M8 12.5a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9zM8 0a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0V.75A.75.75 0 0 1 8 0zm0 13a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 8 13zM3.05 3.05a.75.75 0 0 1 1.06 0l1.06 1.06a.75.75 0 1 1-1.06 1.06L3.05 4.11a.75.75 0 0 1 0-1.06zm7.78 7.78a.75.75 0 0 1 1.06 0l1.06 1.06a.75.75 0 1 1-1.06 1.06l-1.06-1.06a.75.75 0 0 1 0-1.06zM0 8a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8zm13 0a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5h-1.5A.75.75 0 0 1 13 8zM3.05 12.95a.75.75 0 0 1 0-1.06l1.06-1.06a.75.75 0 1 1 1.06 1.06L4.11 12.95a.75.75 0 0 1-1.06 0zm7.78-7.78a.75.75 0 0 1 0-1.06l1.06-1.06a.75.75 0 1 1 1.06 1.06l-1.06 1.06a.75.75 0 0 1-1.06 0z"/></symbol>
|
|
171
|
+
<symbol id="i-menu" viewBox="0 0 16 16"><path d="M1 2.75A.75.75 0 0 1 1.75 2h12.5a.75.75 0 0 1 0 1.5H1.75A.75.75 0 0 1 1 2.75Zm0 5A.75.75 0 0 1 1.75 7h12.5a.75.75 0 0 1 0 1.5H1.75A.75.75 0 0 1 1 7.75ZM1.75 12h12.5a.75.75 0 0 1 0 1.5H1.75a.75.75 0 0 1 0-1.5Z"/></symbol>
|
|
172
|
+
<symbol id="i-note" viewBox="0 0 16 16"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm9 3a1 1 0 1 0-2 0 1 1 0 0 0 2 0ZM7.25 6.75v3.5a.75.75 0 0 0 1.5 0v-3.5a.75.75 0 0 0-1.5 0Z"/></symbol>
|
|
173
|
+
<symbol id="i-tip" viewBox="0 0 16 16"><path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.75.75 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"/></symbol>
|
|
174
|
+
<symbol id="i-warn" viewBox="0 0 16 16"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm.53 4.75v2.5a.75.75 0 0 0 1.5 0v-2.5a.75.75 0 0 0-1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"/></symbol>
|
|
175
|
+
<symbol id="i-danger" viewBox="0 0 16 16"><path d="M9.005 2.687a1.181 1.181 0 0 0-2.01 0L1.471 11.81a1.115 1.115 0 0 0 .95 1.69H13.58a1.115 1.115 0 0 0 .95-1.69ZM8 5.5a.75.75 0 0 1 .75.75v3a.75.75 0 0 1-1.5 0v-3A.75.75 0 0 1 8 5.5Zm0 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"/></symbol>
|
|
176
|
+
</svg>
|
|
177
|
+
|
|
178
|
+
<a class="skip" href="#content">{{SKIP_LABEL}}</a>
|
|
179
|
+
<div class="progress" id="progress" aria-hidden="true"></div>
|
|
180
|
+
|
|
181
|
+
<header class="topbar">
|
|
182
|
+
<button class="icon-btn menu-btn" id="menu-toggle" aria-label="Menu">
|
|
183
|
+
<svg><use href="#i-menu"/></svg>
|
|
184
|
+
</button>
|
|
185
|
+
<span class="doc-type {{DOC_TYPE_CLASS}}">{{DOC_TYPE}}</span>
|
|
186
|
+
<span class="title">{{TITLE}}</span>
|
|
187
|
+
<span class="meta">
|
|
188
|
+
<span>{{UPDATED_LABEL}} {{LAST_UPDATED}}</span>
|
|
189
|
+
{{META_EXTRA}}
|
|
190
|
+
</span>
|
|
191
|
+
<span class="spacer"></span>
|
|
192
|
+
<button class="icon-btn" id="theme-toggle" title="{{THEME_TIP}}" aria-label="{{THEME_TIP}}">
|
|
193
|
+
<svg><use href="#i-sun"/></svg>
|
|
194
|
+
</button>
|
|
195
|
+
</header>
|
|
196
|
+
|
|
197
|
+
<div class="layout">
|
|
198
|
+
<aside class="sidebar" id="sidebar" aria-label="{{TOC_LABEL}}">
|
|
199
|
+
<input class="toc-search" id="toc-search" placeholder="{{SEARCH_PLACEHOLDER}}" type="search">
|
|
200
|
+
<div class="toc-label">{{TOC_LABEL}}</div>
|
|
201
|
+
<nav class="toc" id="toc">
|
|
202
|
+
<!-- TOC_ENTRIES -->
|
|
203
|
+
</nav>
|
|
204
|
+
</aside>
|
|
205
|
+
|
|
206
|
+
<main id="content">
|
|
207
|
+
<h1>{{TITLE}}</h1>
|
|
208
|
+
<p class="subtitle">{{SUBTITLE}}</p>
|
|
209
|
+
<!-- CONTENT_START -->
|
|
210
|
+
<!-- CONTENT_END -->
|
|
211
|
+
</main>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
215
|
+
<script>
|
|
216
|
+
if(window.mermaid){mermaid.initialize({startOnLoad:true,theme:document.documentElement.getAttribute('data-theme')==='dark'?'dark':'default',securityLevel:'loose'});}
|
|
217
|
+
const root=document.documentElement;
|
|
218
|
+
const themeBtn=document.getElementById('theme-toggle');
|
|
219
|
+
const stored=localStorage.getItem('md-theme');
|
|
220
|
+
if(stored)root.setAttribute('data-theme',stored);
|
|
221
|
+
themeBtn.addEventListener('click',()=>{
|
|
222
|
+
const cur=root.getAttribute('data-theme');
|
|
223
|
+
const next=cur==='dark'?'light':(cur==='light'?'auto':'dark');
|
|
224
|
+
root.setAttribute('data-theme',next);
|
|
225
|
+
localStorage.setItem('md-theme',next);
|
|
226
|
+
});
|
|
227
|
+
const sidebar=document.getElementById('sidebar');
|
|
228
|
+
const menuBtn=document.getElementById('menu-toggle');
|
|
229
|
+
if(menuBtn){menuBtn.addEventListener('click',()=>sidebar.classList.toggle('open'));}
|
|
230
|
+
document.querySelectorAll('.toc a').forEach(a=>a.addEventListener('click',()=>sidebar.classList.remove('open')));
|
|
231
|
+
const tocLinks=document.querySelectorAll('#toc a');
|
|
232
|
+
const tocById=new Map();
|
|
233
|
+
tocLinks.forEach(a=>tocById.set(a.dataset.target,a));
|
|
234
|
+
const targets=[...tocById.keys()].map(id=>document.getElementById(id)).filter(Boolean);
|
|
235
|
+
const setActive=(id)=>{tocLinks.forEach(a=>a.classList.remove('active'));const a=tocById.get(id);if(a)a.classList.add('active');};
|
|
236
|
+
const obs=new IntersectionObserver((entries)=>{
|
|
237
|
+
const visible=entries.filter(e=>e.isIntersecting).sort((a,b)=>a.boundingClientRect.top-b.boundingClientRect.top);
|
|
238
|
+
if(visible[0])setActive(visible[0].target.id);
|
|
239
|
+
},{rootMargin:'-70px 0px -60% 0px',threshold:0});
|
|
240
|
+
targets.forEach(t=>obs.observe(t));
|
|
241
|
+
const search=document.getElementById('toc-search');
|
|
242
|
+
search.addEventListener('input',()=>{
|
|
243
|
+
const q=search.value.trim().toLowerCase();
|
|
244
|
+
tocLinks.forEach(a=>{
|
|
245
|
+
if(!q){a.classList.remove('toc-hidden');return;}
|
|
246
|
+
a.classList.toggle('toc-hidden',!a.textContent.toLowerCase().includes(q));
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
const progress=document.getElementById('progress');
|
|
250
|
+
const onScroll=()=>{
|
|
251
|
+
const h=document.documentElement;
|
|
252
|
+
const p=(h.scrollTop)/(h.scrollHeight-h.clientHeight)*100;
|
|
253
|
+
progress.style.width=Math.max(0,Math.min(100,p))+'%';
|
|
254
|
+
};
|
|
255
|
+
document.addEventListener('scroll',onScroll,{passive:true});onScroll();
|
|
256
|
+
document.querySelectorAll('main h2[id],main h3[id]').forEach(h=>{
|
|
257
|
+
const a=document.createElement('a');
|
|
258
|
+
a.className='anchor';a.href='#'+h.id;a.textContent='#';a.setAttribute('aria-label','Copy link');
|
|
259
|
+
a.addEventListener('click',e=>{
|
|
260
|
+
e.preventDefault();
|
|
261
|
+
const url=location.origin+location.pathname+'#'+h.id;
|
|
262
|
+
history.replaceState(null,'',url);
|
|
263
|
+
try{navigator.clipboard.writeText(url);a.classList.add('ok');a.textContent='✓';
|
|
264
|
+
setTimeout(()=>{a.classList.remove('ok');a.textContent='#';},1200);
|
|
265
|
+
}catch(err){}
|
|
266
|
+
});
|
|
267
|
+
h.insertBefore(a,h.firstChild);
|
|
268
|
+
});
|
|
269
|
+
document.querySelectorAll('pre.code').forEach(pre=>{
|
|
270
|
+
const btn=document.createElement('button');
|
|
271
|
+
btn.className='copy-btn';btn.type='button';btn.textContent='{{COPY_LABEL}}';
|
|
272
|
+
btn.addEventListener('click',async()=>{
|
|
273
|
+
const code=pre.querySelector('code');
|
|
274
|
+
try{await navigator.clipboard.writeText(code?code.innerText:pre.innerText);
|
|
275
|
+
btn.textContent='{{COPIED_LABEL}}';btn.classList.add('ok');
|
|
276
|
+
setTimeout(()=>{btn.textContent='{{COPY_LABEL}}';btn.classList.remove('ok');},1400);
|
|
277
|
+
}catch(e){}
|
|
278
|
+
});
|
|
279
|
+
pre.appendChild(btn);
|
|
280
|
+
});
|
|
281
|
+
</script>
|
|
282
|
+
</body>
|
|
283
|
+
</html>
|