File size: 32,648 Bytes
63b381a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="archify guide [[ARCHIFY_VERSION]]">
<title>Archify Scenario Guide — Choose the Right Diagram</title>
<meta name="description" content="Describe the technical question you need to answer. Archify recommends a bounded diagram recipe, evidence checklist, and copy-ready prompt.">
<meta name="theme-color" content="#eff2f1">
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 28'><polygon points='14,2 26,8 26,20 14,26 2,20 2,8' fill='%23083344' stroke='%2322d3ee' stroke-width='1.5'/><circle cx='14' cy='15' r='2.5' fill='%2322d3ee'/></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,400;0,9..144,500;1,9..144,400&family=Inter:wght@400;450;500;600&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/site-navigation.css">
<style>
:root {
/* Drafted-sheet palette — shared with the project page */
--paper:#eff2f1; --paper-raised:#f8faf9;
--ink:#15181b; --ink-soft:#363c41; --muted:#60686d; --dim:#929a9e;
--line:rgba(20,23,26,0.14); --line-hi:rgba(20,23,26,0.3);
--accent:#e14a0e; --accent-soft:rgba(225,74,14,0.08);
--plate:#0f1216;
--emerald:#047857; --rose:#be123c; --violet:#6d28d9;
--font-display:'Fraunces','Songti SC','STSong',Georgia,serif;
--font-body:'Inter',-apple-system,BlinkMacSystemFont,'PingFang SC','Hiragino Sans GB','Microsoft YaHei',sans-serif;
--font-mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
--ease-out:cubic-bezier(.22,1,.36,1);
}
*,*::before,*::after { margin:0; padding:0; box-sizing:border-box; }
html { scroll-behavior:smooth; }
body {
font-family:var(--font-body);
-webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
background:var(--paper); color:var(--ink); min-height:100vh; overflow-x:hidden;
}
body::after {
content:''; position:fixed; inset:0; z-index:1000; pointer-events:none;
opacity:.05; mix-blend-mode:multiply;
background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='140' height='140' filter='url(%23n)'/></svg>");
}
::selection { background:rgba(225,74,14,.18); color:var(--ink); }
button,textarea { font:inherit; }
button { color:inherit; }
a { color:inherit; }
:focus-visible { outline:2px solid var(--accent); outline-offset:3px; border-radius:4px; }
.grid-bg {
position:fixed; inset:0; z-index:0; pointer-events:none;
background-image:linear-gradient(rgba(20,23,26,.045) 1px,transparent 1px),linear-gradient(90deg,rgba(20,23,26,.045) 1px,transparent 1px);
background-size:56px 56px;
-webkit-mask-image:radial-gradient(ellipse 95% 62% at 50% -6%, black 12%, transparent 75%);
mask-image:radial-gradient(ellipse 95% 62% at 50% -6%, black 12%, transparent 75%);
}
.btn { display:inline-flex; align-items:center; gap:.5rem; padding:.55rem 1.15rem; border-radius:6px; font-family:var(--font-body); font-size:.8125rem; font-weight:500; letter-spacing:-.005em; cursor:pointer; text-decoration:none; transition:all .2s var(--ease-out); border:1px solid transparent; }
.btn-primary { background:var(--ink); color:var(--paper); }
.btn-primary:hover { background:#272c31; transform:translateY(-1px); }
.shell { position:relative; z-index:1; width:min(1180px,calc(100% - 48px)); margin:0 auto; }
/* HERO */
.hero { padding:10rem 0 4rem; display:grid; grid-template-columns:minmax(0,1.35fr) minmax(280px,.65fr); gap:4.5rem; align-items:end; }
.lead-label { display:flex; align-items:center; gap:.875rem; margin-bottom:1.5rem; font-family:var(--font-mono); font-size:.625rem; font-weight:500; letter-spacing:.2em; color:var(--muted); text-transform:uppercase; }
.lead-label::before { content:''; width:2.75rem; height:1px; background:var(--line-hi); }
h1 { max-width:770px; font-family:var(--font-display); font-optical-sizing:auto; font-size:clamp(2.5rem,5vw,4.25rem); font-weight:400; line-height:1.06; letter-spacing:-.028em; color:var(--ink); text-wrap:balance; }
h1 em { font-style:italic; color:var(--accent); }
.lede { max-width:680px; margin-top:1.5rem; color:var(--muted); font-size:.9375rem; line-height:1.72; text-wrap:pretty; }
.metrics { display:grid; gap:1px; background:var(--line); border:1px solid var(--line-hi); border-radius:12px; overflow:hidden; }
.metric { display:flex; justify-content:space-between; align-items:center; gap:1rem; padding:1rem 1.25rem; background:var(--paper-raised); }
.metric strong { font-family:var(--font-display); font-style:italic; font-optical-sizing:auto; font-weight:400; font-size:1.625rem; line-height:1; color:var(--ink); }
.metric span { color:var(--dim); font-family:var(--font-mono); font-size:.5625rem; letter-spacing:.14em; text-transform:uppercase; line-height:1.5; text-align:right; }
/* CHOOSER */
.chooser { padding:1.75rem 2rem 2rem; border:1px solid var(--line-hi); border-radius:14px; background:var(--paper-raised); position:relative; }
.chooser-head { display:flex; align-items:flex-start; justify-content:space-between; gap:1.25rem; margin-bottom:1.125rem; }
.chooser-head h2 { margin-bottom:.375rem; font-family:var(--font-display); font-optical-sizing:auto; font-size:1.5rem; font-weight:500; letter-spacing:-.02em; }
.chooser-head p { color:var(--muted); font-size:.8125rem; line-height:1.6; }
.kbd { flex:none; border:1px solid var(--line-hi); border-radius:5px; padding:.3rem .5rem; color:var(--dim); font-family:var(--font-mono); font-size:.5625rem; background:var(--paper); }
textarea { width:100%; min-height:104px; resize:vertical; border:1px solid var(--line-hi); border-radius:10px; padding:.9rem 1rem; background:var(--paper); color:var(--ink); outline:none; font-family:var(--font-mono); font-size:.75rem; line-height:1.7; transition:border-color .18s var(--ease-out),box-shadow .18s var(--ease-out); }
textarea::placeholder { color:var(--dim); }
textarea:focus { border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.sample-row { margin:.875rem 0 1.125rem; display:flex; gap:.45rem; flex-wrap:wrap; }
.chip { border:1px solid var(--line); border-radius:999px; background:var(--paper); color:var(--muted); padding:.45rem .75rem; font-family:var(--font-mono); font-size:.5625rem; letter-spacing:.06em; cursor:pointer; transition:all .18s var(--ease-out); }
.chip:hover { color:var(--accent); border-color:var(--accent); }
.actions { display:flex; align-items:center; gap:.625rem; }
.primary,.secondary { min-height:44px; border-radius:8px; padding:.65rem 1.15rem; border:1px solid transparent; font-size:.8125rem; font-weight:500; cursor:pointer; transition:all .2s var(--ease-out); }
.primary { background:var(--ink); color:var(--paper); border-color:var(--ink); }
.primary:hover { background:var(--accent); border-color:var(--accent); }
.secondary { background:transparent; border-color:var(--line-hi); color:var(--ink-soft); }
.secondary:hover { color:var(--ink); border-color:var(--ink); }
/* RESULT */
.result { margin-top:1.25rem; border:1px solid var(--line-hi); border-radius:14px; background:var(--paper-raised); overflow:hidden; display:none; }
.result.visible { display:block; animation:reveal .3s var(--ease-out); }
@keyframes reveal { from { opacity:0; transform:translateY(6px); } }
.result-main { padding:1.75rem 2rem; display:grid; grid-template-columns:minmax(0,1.1fr) minmax(260px,.9fr); gap:2rem; }
.result-kicker { color:var(--accent); font-family:var(--font-mono); font-size:.5625rem; font-weight:600; letter-spacing:.16em; text-transform:uppercase; }
.result h3 { margin:.5rem 0 .375rem; font-family:var(--font-display); font-optical-sizing:auto; font-size:1.625rem; font-weight:500; letter-spacing:-.02em; }
.result-question { color:var(--ink-soft); font-size:.875rem; font-weight:500; line-height:1.6; }
.result-summary { margin-top:.875rem; color:var(--muted); font-size:.8125rem; line-height:1.7; }
.proof-link { display:inline-flex; align-items:center; margin-top:1.125rem; padding:.5rem .75rem; border:1px solid rgba(4,120,87,.35); border-radius:999px; background:rgba(4,120,87,.06); color:var(--emerald); text-decoration:none; font-family:var(--font-mono); font-size:.5625rem; font-weight:600; letter-spacing:.12em; text-transform:uppercase; transition:all .18s var(--ease-out); }
.proof-link:hover { border-color:var(--emerald); background:rgba(4,120,87,.1); }
.boundary { display:grid; gap:1rem; align-content:start; }
.boundary-item small { display:block; margin-bottom:.375rem; color:var(--emerald); font-family:var(--font-mono); font-size:.5625rem; font-weight:600; letter-spacing:.14em; text-transform:uppercase; }
.boundary-item.avoid small { color:var(--rose); }
.boundary-item p { color:var(--muted); font-size:.8125rem; line-height:1.65; }
.result-grid { display:grid; grid-template-columns:minmax(230px,.7fr) minmax(0,1.3fr); border-top:1px solid var(--line); }
.checklist,.prompt-box { padding:1.5rem 2rem; }
.checklist { border-right:1px solid var(--line); }
.mini-heading { margin-bottom:.875rem; color:var(--dim); font-family:var(--font-mono); font-size:.5625rem; font-weight:500; letter-spacing:.16em; text-transform:uppercase; }
.checklist ul { list-style:none; display:grid; gap:.55rem; }
.checklist li { color:var(--muted); font-size:.75rem; line-height:1.55; display:flex; gap:.5rem; }
.checklist li::before { content:'✓'; color:var(--emerald); flex:0 0 auto; }
.presentation { display:flex; flex-wrap:wrap; gap:.4rem; margin-top:1.125rem; }
.tag { padding:.35rem .6rem; border-radius:999px; border:1px solid var(--line); background:var(--paper); color:var(--muted); font-family:var(--font-mono); font-size:.5625rem; letter-spacing:.06em; }
.prompt-bar { display:flex; align-items:center; justify-content:space-between; gap:.75rem; margin-bottom:.75rem; }
.copy { border:0; background:none; color:var(--accent); padding:0; font-family:var(--font-mono); font-size:.5625rem; font-weight:600; letter-spacing:.12em; text-transform:uppercase; cursor:pointer; }
.copy:hover { color:var(--ink); }
.prompt-text { color:var(--ink-soft); font-family:var(--font-mono); font-size:.6875rem; line-height:1.75; white-space:pre-wrap; }
.alternatives { padding:.9rem 2rem; border-top:1px solid var(--line); color:var(--dim); font-size:.75rem; }
.alternatives button { margin-left:.5rem; padding:0; border:0; background:none; color:var(--muted); cursor:pointer; font-size:.75rem; text-decoration:underline; text-underline-offset:3px; text-decoration-color:var(--line-hi); }
.alternatives button:hover { color:var(--accent); text-decoration-color:var(--accent); }
/* LIBRARY */
.library { padding:5rem 0 6rem; }
.section-head { display:flex; align-items:end; justify-content:space-between; gap:1.75rem; margin-bottom:1.75rem; }
.section-head h2 { margin-top:.5rem; font-family:var(--font-display); font-optical-sizing:auto; font-size:clamp(1.75rem,3vw,2.5rem); font-weight:400; letter-spacing:-.026em; }
.section-head p { max-width:440px; color:var(--muted); font-size:.8125rem; line-height:1.7; }
.filters { display:flex; flex-wrap:wrap; gap:.45rem; margin-bottom:1.25rem; }
.filter { border:1px solid var(--line-hi); border-radius:999px; background:transparent; color:var(--muted); padding:.5rem .85rem; font-family:var(--font-mono); font-size:.5625rem; font-weight:500; letter-spacing:.1em; text-transform:uppercase; cursor:pointer; transition:all .18s var(--ease-out); }
.filter:hover { color:var(--ink); border-color:var(--ink); }
.filter.active { color:var(--paper); background:var(--ink); border-color:var(--ink); }
.cards { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:14px; }
.card { min-width:0; min-height:206px; padding:1.375rem 1.5rem; text-align:left; border:1px solid var(--line-hi); border-radius:12px; background:var(--paper-raised); cursor:pointer; transition:transform .22s var(--ease-out),border-color .22s var(--ease-out),box-shadow .22s var(--ease-out); position:relative; font-family:inherit; }
.card:hover,.card:focus-visible { transform:translateY(-3px); border-color:var(--ink); box-shadow:0 14px 36px rgba(20,23,26,.1); outline:none; }
.card-type { color:var(--type-color,var(--accent)); font-family:var(--font-mono); font-size:.5625rem; font-weight:600; letter-spacing:.14em; text-transform:uppercase; }
.card h3 { margin:.875rem 0 .5rem; font-family:var(--font-display); font-optical-sizing:auto; font-size:1.125rem; font-weight:500; letter-spacing:-.015em; color:var(--ink); }
.card-question { color:var(--ink-soft); font-size:.75rem; line-height:1.6; }
.card-summary { margin:.75rem 0 0; color:var(--dim); font-size:.6875rem; line-height:1.6; }
.card-foot { position:absolute; left:1.5rem; right:1.5rem; bottom:1.125rem; display:flex; justify-content:space-between; gap:.75rem; color:var(--dim); font-family:var(--font-mono); font-size:.5625rem; letter-spacing:.06em; }
.card { padding-bottom:2.75rem; }
footer { border-top:1px solid var(--line); padding:1.75rem 0 2.5rem; color:var(--dim); font-family:var(--font-mono); font-size:.625rem; letter-spacing:.04em; }
footer .shell { display:flex; justify-content:space-between; gap:1rem; }
/* ENTRANCE */
@keyframes rise { from { opacity:0; transform:translateY(14px); } to { opacity:1; transform:none; } }
.hero, .chooser, .library { animation:rise .7s var(--ease-out) both; }
.chooser { animation-delay:.08s; }
.library { animation-delay:.16s; }
@media(max-width:900px) {
.hero { grid-template-columns:1fr; gap:2.25rem; padding-top:8.5rem; }
.metrics { grid-template-columns:repeat(3,1fr); }
.metric { display:grid; gap:.4rem; }
.metric span { text-align:left; }
.cards { grid-template-columns:repeat(2,minmax(0,1fr)); }
.result-main,.result-grid { grid-template-columns:1fr; }
.checklist { border-right:0; border-bottom:1px solid var(--line); }
}
@media(max-width:620px) {
.shell { width:min(100% - 28px,1180px); }
.hero { padding:7.5rem 0 2.5rem; }
h1 { font-size:2.2rem; }
.lede { font-size:.875rem; }
.metrics { grid-template-columns:1fr; }
.metric { display:flex; }
.metric span { text-align:right; }
.chooser { padding:1.25rem; }
.chooser-head .kbd { display:none; }
.sample-row { flex-wrap:nowrap; overflow:auto; padding-bottom:4px; }
.chip { white-space:nowrap; }
.result-main { padding:1.25rem; }
.checklist,.prompt-box { padding:1.125rem 1.25rem; }
.cards { grid-template-columns:1fr; }
.section-head { align-items:flex-start; flex-direction:column; }
.library { padding:3.5rem 0 4.5rem; }
footer .shell { flex-direction:column; }
}
@media (prefers-reduced-motion:reduce) {
html { scroll-behavior:auto; }
.hero, .chooser, .library, .result.visible { animation:none; }
}
</style>
</head>
<body>
<div class="grid-bg" aria-hidden="true"></div>
<nav class="site-nav" aria-label="Primary navigation">
<a class="nav-logo" href="./" aria-label="Archify home">
<span class="nav-logo-text">Archify</span>
<span class="nav-logo-path" data-en="/ guide" data-zh="/ 场景指南">/ guide</span>
</a>
<div class="nav-right">
<a class="nav-link" href="guide.html" aria-current="page" data-i18n="navGuide">Guide</a>
<a class="nav-link" href="gallery.html" data-i18n="navProof">Proof Lab</a>
<a class="nav-link" href="start.html" data-i18n="navStart">Start</a>
<a class="nav-link" href="https://github.com/tt-a1i/archify" target="_blank" rel="noopener">GitHub</a>
<button class="btn-lang" id="language" type="button" aria-label="Switch language">中文</button>
<a class="btn btn-primary nav-cta" href="start.html" data-i18n="navInstall">Install Skill</a>
</div>
</nav>
<main class="shell">
<section class="hero">
<div>
<div class="lead-label" data-i18n="eyebrow">Question-first diagramming</div>
<h1 data-i18n-html="headline">Choose the question.<br>Get the <em>right diagram.</em></h1>
<p class="lede" data-i18n="lede">Describe what your audience needs to understand. Archify recommends one bounded visual recipe—plus the evidence it must contain, when not to use it, and a prompt you can copy.</p>
</div>
<div class="metrics" aria-label="Guide metrics">
<div class="metric"><strong>[[RECIPE_COUNT]]</strong><span data-i18n-html="metricRecipes">real-world<br>recipes</span></div>
<div class="metric"><strong>5</strong><span data-i18n-html="metricModes">typed diagram<br>modes</span></div>
<div class="metric"><strong>0</strong><span data-i18n-html="metricRuntime">runtime<br>dependencies</span></div>
</div>
</section>
<section class="chooser" aria-labelledby="chooser-title">
<div class="chooser-head">
<div><h2 id="chooser-title" data-i18n="chooserTitle">What must the diagram explain?</h2><p data-i18n="chooserBody">Write a situation, not a diagram type. Specific system facts produce a stronger recommendation.</p></div>
<span class="kbd">⌘ / Ctrl + Enter</span>
</div>
<textarea id="scenario" data-i18n-placeholder="placeholder" placeholder="Example: Show an API request with JWT auth, a Redis cache miss, database fallback, and async tracing."></textarea>
<div class="sample-row" id="samples"></div>
<div class="actions">
<button class="primary" id="recommend" type="button" data-i18n="recommend">Recommend a recipe →</button>
<button class="secondary" id="clear" type="button" data-i18n="clear">Clear</button>
</div>
<div class="result" id="result" aria-live="polite"></div>
</section>
<section class="library" id="library">
<div class="section-head">
<div><div class="lead-label" data-i18n="libraryEyebrow">Recipe library</div><h2 data-i18n="libraryTitle">Eleven small, opinionated starting points.</h2></div>
<p data-i18n="libraryBody">Each recipe answers one technical question. That boundary keeps the result legible, reviewable, and honest about missing evidence.</p>
</div>
<div class="filters" id="filters" aria-label="Filter recipes"></div>
<div class="cards" id="cards"></div>
</section>
</main>
<footer><div class="shell"><span data-i18n="footerLeft">Generated from the same recipe source as the Archify CLI.</span><span>archify guide "your scenario"</span><span data-i18n="versionLabel">Scenario guide / development / v[[ARCHIFY_VERSION]]</span></div></footer>
<script id="guide-data" type="application/json">[[GUIDE_JSON]]</script>
<script src="assets/site-language.js"></script>
<script>
(function () {
'use strict';
var recipes = JSON.parse(document.getElementById('guide-data').textContent);
var types = [[DIAGRAM_TYPES_JSON]];
var colors = { architecture:'#0891b2', workflow:'#047857', sequence:'#6d28d9', dataflow:'#b45309', lifecycle:'#be123c' };
var language = ArchifySiteLanguage.read();
var activeType = 'all';
var lastRecipe = null;
var copy = {
en: {
navGuide:'Guide',navProof:'Proof Lab',navStart:'Start',navInstall:'Install Skill',versionLabel:'Scenario guide / development / v[[ARCHIFY_VERSION]]',eyebrow:'Question-first diagramming', headline:'Choose the question.<br>Get the <em>right diagram.</em>', lede:'Describe what your audience needs to understand. Archify recommends one bounded visual recipe—plus the evidence it must contain, when not to use it, and a prompt you can copy.',
metricRecipes:'real-world<br>recipes',metricModes:'typed diagram<br>modes',metricRuntime:'runtime<br>dependencies',chooserTitle:'What must the diagram explain?',chooserBody:'Write a situation, not a diagram type. Specific system facts produce a stronger recommendation.',placeholder:'Example: Show an API request with JWT auth, a Redis cache miss, database fallback, and async tracing.',recommend:'Recommend a recipe →',clear:'Clear',libraryEyebrow:'Recipe library',libraryTitle:'Eleven small, opinionated starting points.',libraryBody:'Each recipe answers one technical question. That boundary keeps the result legible, reviewable, and honest about missing evidence.',footerLeft:'Generated from the same recipe source as the Archify CLI.',all:'All recipes',recommended:'Recommended recipe',use:'Use when',avoid:'Avoid when',must:'Evidence to include',presentation:'Presentation',prompt:'Copy-ready prompt',copyPrompt:'Copy prompt',copied:'Copied',alternatives:'Other possible fits:',confidence:'confidence',open:'Open recipe',proofReady:'Verified proof',proofLink:'Open verified example ↗',
samples:[['API + cache miss','Show an API request with JWT auth, a Redis cache miss, database fallback, and async tracing.'],['Kafka + DLQ','Map Kafka topics, ordered processors, consumer groups, replay, state stores, and the dead-letter queue.'],['Incident response','Show how responders detect, triage, mitigate, escalate, communicate, and verify recovery.']]
},
zh: {
navGuide:'场景指南',navProof:'验证作品集',navStart:'快速上手',navInstall:'安装技能',versionLabel:'场景指南 / 开发版 / v[[ARCHIFY_VERSION]]',eyebrow:'先问题,后图表',headline:'先选对问题,<br>再得到<em>对的图。</em>',lede:'描述受众真正需要理解的内容。Archify 会推荐一个有边界的视觉配方,同时给出证据清单、禁用条件和可复制提示词。',
metricRecipes:'个真实场景<br>配方',metricModes:'种类型化<br>图表模式',metricRuntime:'个运行时<br>依赖',chooserTitle:'这张图必须解释什么?',chooserBody:'写清场景,不要只写图表类型。系统事实越具体,推荐越可靠。',placeholder:'例如:展示带 JWT 鉴权、Redis 缓存未命中、数据库回退和异步追踪的 API 请求。',recommend:'推荐配方 →',clear:'清空',libraryEyebrow:'配方库',libraryTitle:'十一个小而专的起点。',libraryBody:'每个配方只回答一个技术问题。清晰的边界让图更易读、可评审,也不会掩盖证据缺口。',footerLeft:'网页与 Archify CLI 使用同一份配方数据生成。',all:'全部配方',recommended:'推荐配方',use:'适合',avoid:'不要这样用',must:'必须包含的证据',presentation:'表现建议',prompt:'可直接复制的提示词',copyPrompt:'复制提示词',copied:'已复制',alternatives:'其他可能:',confidence:'置信度',open:'打开配方',proofReady:'已验证成品',proofLink:'打开验证成品 ↗',
samples:[['API + 缓存未命中','展示带 JWT 鉴权、Redis 缓存未命中、数据库回退和异步追踪的 API 请求。'],['Kafka + 死信','梳理 Kafka Topic、有序处理器、消费者组、重放、状态存储和死信队列。'],['事故处置','展示响应者如何发现、分诊、缓解、升级、沟通并验证恢复。']]
}
};
function t(key) { return copy[language][key]; }
function local(recipe) { return Object.assign({}, recipe, recipe[language]); }
function normalize(value) { return String(value || '').normalize('NFKC').toLowerCase().replace(/[\\s_]+/g,' ').trim(); }
function rank(query) {
var text = normalize(query);
return recipes.map(function (recipe,index) {
var score = 0, matched = [];
if (text === recipe.id || text === recipe.id.replace(/-/g,' ')) { score = 100; matched = [recipe.id]; }
else recipe.signals.forEach(function (signal) { if (text.includes(normalize(signal[0]))) { score += signal[1]; matched.push(signal[0]); } });
return { recipe:recipe, score:score, matched:matched, index:index };
}).sort(function (a,b) { return b.score - a.score || a.index - b.index; });
}
function recommendation(query) {
var ranked = rank(query), winner = ranked[0].score > 0 ? ranked[0] : { recipe:recipes[0], score:0, matched:[] };
return { recipe:winner.recipe, confidence:winner.score >= 14 ? 'high' : winner.score >= 7 ? 'medium' : 'low', alternatives:ranked.filter(function (entry) { return entry.recipe.id !== winner.recipe.id && entry.score > 0; }).slice(0,2) };
}
function escapeHtml(value) { var node = document.createElement('span'); node.textContent = String(value); return node.innerHTML; }
function renderSamples() {
document.getElementById('samples').innerHTML = t('samples').map(function (sample) { return '<button class="chip" type="button" data-query="'+escapeHtml(sample[1])+'">'+escapeHtml(sample[0])+'</button>'; }).join('');
}
function renderFilters() {
var labels = [[DIAGRAM_TYPE_LABELS_JSON]];
document.getElementById('filters').innerHTML = ['all'].concat(types).map(function (type) { return '<button class="filter '+(activeType === type ? 'active':'')+'" type="button" data-filter="'+type+'">'+escapeHtml(type === 'all' ? t('all') : labels[language][type])+'</button>'; }).join('');
}
function renderCards() {
var visible = recipes.filter(function (recipe) { return activeType === 'all' || recipe.type === activeType; });
document.getElementById('cards').innerHTML = visible.map(function (raw) {
var recipe = local(raw);
return '<button class="card" type="button" data-recipe="'+recipe.id+'" style="--type-color:'+colors[recipe.type]+'"><span class="card-type">'+escapeHtml(recipe.type)+'</span><h3>'+escapeHtml(recipe.title)+'</h3><p class="card-question">'+escapeHtml(recipe.question)+'</p><p class="card-summary">'+escapeHtml(recipe.summary)+'</p><span class="card-foot"><span>'+escapeHtml(recipe.presentation.preset)+' · '+escapeHtml(recipe.presentation.motion)+'</span><span>'+escapeHtml(recipe.proof ? t('proofReady') : t('open'))+' ↗</span></span></button>';
}).join('');
}
function renderResult(rawRecipe, confidence, alternatives) {
var recipe = local(rawRecipe);
lastRecipe = rawRecipe;
var alt = alternatives || [];
var html = '<div class="result-main"><div><span class="result-kicker">'+escapeHtml(t('recommended'))+' · '+escapeHtml(confidence)+' '+escapeHtml(t('confidence'))+'</span><h3>'+escapeHtml(recipe.title)+'</h3><p class="result-question">'+escapeHtml(recipe.question)+'</p><p class="result-summary">'+escapeHtml(recipe.summary)+'</p>'+(recipe.proof ? '<a class="proof-link" href="gallery.html#proof-'+encodeURIComponent(recipe.proof)+'">'+escapeHtml(t('proofLink'))+'</a>' : '')+'</div><div class="boundary"><div class="boundary-item"><small>'+escapeHtml(t('use'))+'</small><p>'+escapeHtml(recipe.useWhen)+'</p></div><div class="boundary-item avoid"><small>'+escapeHtml(t('avoid'))+'</small><p>'+escapeHtml(recipe.avoidWhen)+'</p></div></div></div>';
html += '<div class="result-grid"><div class="checklist"><div class="mini-heading">'+escapeHtml(t('must'))+'</div><ul>'+recipe.include.map(function (item) { return '<li>'+escapeHtml(item)+'</li>'; }).join('')+'</ul><div class="presentation"><span class="tag">'+escapeHtml(recipe.type)+'</span><span class="tag">'+escapeHtml(recipe.presentation.preset)+'</span><span class="tag">'+escapeHtml(recipe.presentation.motion)+'</span><span class="tag">views '+escapeHtml(recipe.presentation.views)+'</span></div></div>';
html += '<div class="prompt-box"><div class="prompt-bar"><div class="mini-heading" style="margin:0">'+escapeHtml(t('prompt'))+'</div><button class="copy" id="copy-prompt" type="button">'+escapeHtml(t('copyPrompt'))+'</button></div><p class="prompt-text">'+escapeHtml(recipe.prompt)+'</p></div></div>';
if (alt.length) html += '<div class="alternatives">'+escapeHtml(t('alternatives'))+alt.map(function (entry) { var item=local(entry.recipe); return '<button type="button" data-alt="'+item.id+'">'+escapeHtml(item.title)+' ['+item.type+']</button>'; }).join('')+'</div>';
var result = document.getElementById('result');
result.innerHTML = html;
result.classList.add('visible');
}
function runRecommendation() {
var query = document.getElementById('scenario').value.trim();
if (!query) { document.getElementById('scenario').focus(); return; }
var picked = recommendation(query);
renderResult(picked.recipe,picked.confidence,picked.alternatives);
}
function applyLanguage(next) {
language = ArchifySiteLanguage.write(next);
document.documentElement.lang = language === 'zh' ? 'zh-CN' : 'en';
document.getElementById('language').textContent = language === 'zh' ? 'EN' : '中文';
document.getElementById('language').setAttribute('aria-label', language === 'zh' ? 'Switch to English' : '切换到中文');
document.querySelectorAll('[data-en][data-zh]').forEach(function (node) { node.textContent=node.getAttribute(language === 'zh' ? 'data-zh' : 'data-en'); });
document.querySelectorAll('[data-i18n]').forEach(function (node) { node.textContent=t(node.dataset.i18n); });
document.querySelectorAll('[data-i18n-html]').forEach(function (node) { node.innerHTML=t(node.dataset.i18nHtml); });
document.querySelectorAll('[data-i18n-placeholder]').forEach(function (node) { node.placeholder=t(node.dataset.i18nPlaceholder); });
renderSamples(); renderFilters(); renderCards();
if (lastRecipe) renderResult(lastRecipe,'selected',[]);
}
async function copyPrompt() {
if (!lastRecipe) return;
var text = local(lastRecipe).prompt, button = document.getElementById('copy-prompt');
try { await navigator.clipboard.writeText(text); } catch (_) {
var area=document.createElement('textarea'); area.value=text; document.body.appendChild(area); area.select(); document.execCommand('copy'); area.remove();
}
button.textContent=t('copied'); setTimeout(function () { if (button.isConnected) button.textContent=t('copyPrompt'); },1200);
}
document.getElementById('recommend').addEventListener('click',runRecommendation);
document.getElementById('clear').addEventListener('click',function () { document.getElementById('scenario').value=''; document.getElementById('result').classList.remove('visible'); lastRecipe=null; });
document.getElementById('language').addEventListener('click',function () { applyLanguage(language === 'en' ? 'zh':'en'); });
document.getElementById('scenario').addEventListener('keydown',function (event) { if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') runRecommendation(); });
document.getElementById('samples').addEventListener('click',function (event) { var chip=event.target.closest('[data-query]'); if (!chip) return; document.getElementById('scenario').value=chip.dataset.query; runRecommendation(); });
document.getElementById('filters').addEventListener('click',function (event) { var filter=event.target.closest('[data-filter]'); if (!filter) return; activeType=filter.dataset.filter; renderFilters(); renderCards(); });
document.getElementById('cards').addEventListener('click',function (event) { var card=event.target.closest('[data-recipe]'); if (!card) return; var recipe=recipes.find(function (item) { return item.id === card.dataset.recipe; }); renderResult(recipe,'selected',[]); document.getElementById('result').scrollIntoView({behavior:'smooth',block:'center'}); });
document.getElementById('result').addEventListener('click',function (event) { if (event.target.id === 'copy-prompt') copyPrompt(); var alt=event.target.closest('[data-alt]'); if (alt) renderResult(recipes.find(function (item) { return item.id === alt.dataset.alt; }),'selected',[]); });
applyLanguage(language);
}());
</script>
</body>
</html>
|