lantern-harness 0.0.0-stage → 0.1.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/CHANGELOG.md +143 -0
- package/LICENSE +21 -0
- package/README.md +110 -2
- package/builtin/session/README.md +21 -0
- package/builtin/session/bash-session.mjs +379 -0
- package/builtin/session/fs-observation-policy.mjs +38 -0
- package/builtin/session/lh-bash.mjs +778 -0
- package/builtin/session/lh-fs.mjs +1262 -0
- package/builtin/session/lh-nav-groups.mjs +717 -0
- package/builtin/session/lh-schedule.mjs +299 -0
- package/builtin/session/lh-spaces.mjs +423 -0
- package/builtin/session/lh-spill.mjs +138 -0
- package/builtin/session/lh-web.mjs +205 -0
- package/builtin/session/office-to-pdf.mjs +210 -0
- package/builtin/session/pwsh-session.mjs +376 -0
- package/builtin/session/sandbox-policy.mjs +116 -0
- package/builtin/session/session-runs.mjs +1352 -0
- package/builtin/session/skill-fs.mjs +148 -0
- package/builtin/session/space-context.mjs +70 -0
- package/builtin/session/space-layout.mjs +222 -0
- package/builtin/session/workspace-deps.mjs +63 -0
- package/cli/acp-server.mjs +185 -0
- package/cli/ai-testbench.mjs +5 -0
- package/cli/data-root.mjs +623 -0
- package/cli/ensure-dist.mjs +47 -0
- package/cli/headless.mjs +318 -0
- package/cli/jsonrpc-server.mjs +60 -0
- package/cli/lantern.mjs +981 -0
- package/cli/server.mjs +117 -0
- package/cli/spaces.mjs +995 -0
- package/cli/user-config.mjs +183 -0
- package/config/app.defaults.json +29 -0
- package/config/seeds//346/217/220/347/244/272/350/257/215/347/256/241/347/220/206//345/212/237/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213/346/217/220/347/244/272/350/257/215v1.0.md +155 -0
- package/config/seeds//346/217/220/347/244/272/350/257/215/347/256/241/347/220/206//351/273/230/350/256/244/346/217/220/347/244/272/350/257/215.md +1 -0
- package/config/seeds//346/265/213/350/257/225/346/250/241/346/235/277/345/272/223//345/212/237/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212//345/212/237/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212/346/250/241/346/235/277.md +65 -0
- package/config/seeds//346/265/213/350/257/225/346/250/241/346/235/277/345/272/223//345/212/237/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213//345/212/237/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213/346/250/241/346/235/277.xlsx +0 -0
- package/config/seeds//346/265/213/350/257/225/346/250/241/346/235/277/345/272/223//346/200/247/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212//346/200/247/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212/346/250/241/346/235/277.md +61 -0
- package/config/seeds//346/265/213/350/257/225/346/250/241/346/235/277/345/272/223//346/200/247/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213//346/200/247/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213/346/250/241/346/235/277.md +42 -0
- package/config/seeds//346/265/213/350/257/225/346/250/241/346/235/277/345/272/223//346/216/245/345/217/243/346/265/213/350/257/225/346/212/245/345/221/212//346/216/245/345/217/243/346/265/213/350/257/225/346/212/245/345/221/212/346/250/241/346/235/277.md +55 -0
- package/config/seeds//346/265/213/350/257/225/346/250/241/346/235/277/345/272/223//346/216/245/345/217/243/346/265/213/350/257/225/347/224/250/344/276/213//346/216/245/345/217/243/346/265/213/350/257/225/347/224/250/344/276/213/346/250/241/347/211/210.xlsx +0 -0
- package/config/seeds//346/265/213/350/257/225/346/250/241/346/235/277/345/272/223//350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/347/224/250/344/276/213//350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/347/224/250/344/276/213/346/250/241/346/235/277.md +37 -0
- package/config/seeds//347/237/245/350/257/206/345/272/223/347/256/241/347/220/206//345/212/237/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213/347/274/226/345/206/231/350/247/204/350/214/203.md +174 -0
- package/dist/500.html +250 -0
- package/dist/_astro/Base.CFN876O3.css +1 -0
- package/dist/_astro/Base.astro_astro_type_script_index_0_lang.lxEwQ1R4.js +1 -0
- package/dist/_astro/CaseGenerateApp.oU5Exw2b.js +9 -0
- package/dist/_astro/CasesLibrary.CDcqlb6I.js +1 -0
- package/dist/_astro/ChatApp.tMPYvFMU.js +258 -0
- package/dist/_astro/ClientRouter.astro_astro_type_script_index_0_lang.CO1cjG9V.js +1 -0
- package/dist/_astro/ConfirmDialog.BYz4HREM.js +1 -0
- package/dist/_astro/DocsFileBrowser.DkE26UTR.js +1 -0
- package/dist/_astro/ExcelFieldTags.B_XMwNNx.js +152 -0
- package/dist/_astro/KnowledgeLibrary.DwYJQYZT.js +1 -0
- package/dist/_astro/NamePromptDialog.DGBlDH_k.js +1 -0
- package/dist/_astro/NavLibraries.BwtNpUK6.js +1 -0
- package/dist/_astro/OverflowHint.lzmplP5j.js +1 -0
- package/dist/_astro/PluginsPage.CgIpm4kx.js +1 -0
- package/dist/_astro/PluginsPage.tLehAvrG.js +39 -0
- package/dist/_astro/PromptsLibrary.BpetRzj6.js +1 -0
- package/dist/_astro/RequirementsLibrary.Dt0Gj6Nr.js +1 -0
- package/dist/_astro/SearchableSelect.DMS2XmXc.js +1 -0
- package/dist/_astro/SelectDropdown.Dc7QaPbH.js +1 -0
- package/dist/_astro/SettingsDialog.Dc2xMiIZ.js +1 -0
- package/dist/_astro/SpaceSwitcher.CC-GMa_K.js +7 -0
- package/dist/_astro/TemplatesLibrary.uK-SjK1z.js +1 -0
- package/dist/_astro/app-config.CpjKWnzM.js +1 -0
- package/dist/_astro/client.Be_SakrB.js +9 -0
- package/dist/_astro/i18n.BWX_-rcS.js +7 -0
- package/dist/_astro/jsx-runtime.BUXbF_vF.js +1 -0
- package/dist/_astro/lib.DJNDi_Ht.js +43 -0
- package/dist/_astro/listbox.DdDDDJ5Z.js +1 -0
- package/dist/_astro/llm.BZEzE0x2.js +7 -0
- package/dist/_astro/menu.CqkL87bV.js +1 -0
- package/dist/_astro/open-docs-in-session.Bx75APfu.js +1 -0
- package/dist/_astro/page.Bx2KSkeL.js +1 -0
- package/dist/_astro/plugins.CrpY8UI9.js +1 -0
- package/dist/_astro/portal.C3KrUREL.js +5 -0
- package/dist/_astro/prefetch.GJhwodf8.js +1 -0
- package/dist/_astro/prompts-meta.F4wwkjUA.js +2 -0
- package/dist/_astro/react-dom.CqAxpHMt.js +1 -0
- package/dist/_astro/settings.Cb-d0EJe.js +2 -0
- package/dist/_astro/use-text-value.C0IDspmh.js +1 -0
- package/dist/_astro/use-tree-walker.oRTnhHS-.js +1 -0
- package/dist/_astro/useListAnim.CnbC_9MC.js +1 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/brand-mark.png +0 -0
- package/dist/cases/generate/index.html +249 -0
- package/dist/cases/index.html +1 -0
- package/dist/cases/library/index.html +249 -0
- package/dist/changelog/index.html +1 -0
- package/dist/docs/changelog/index.html +354 -0
- package/dist/docs/index.html +1 -0
- package/dist/docs/install/index.html +312 -0
- package/dist/docs/manual/index.html +631 -0
- package/dist/favicon.png +0 -0
- package/dist/icon-192.png +0 -0
- package/dist/index.html +249 -0
- package/dist/knowledge/index.html +249 -0
- package/dist/knowledge//345/212/237/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213/347/274/226/345/206/231/350/247/204/350/214/203/index.html +535 -0
- package/dist/plugins/index.html +249 -0
- package/dist/prompts/index.html +249 -0
- package/dist/raw/knowledge//345/212/237/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213/347/274/226/345/206/231/350/247/204/350/214/203.md +168 -0
- package/dist/raw/templates//345/212/237/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212//345/212/237/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212/346/250/241/346/235/277.md +58 -0
- package/dist/raw/templates//346/200/247/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212//346/200/247/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212/346/250/241/346/235/277.md +54 -0
- package/dist/raw/templates//346/200/247/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213//346/200/247/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213/346/250/241/346/235/277.md +35 -0
- package/dist/raw/templates//346/216/245/345/217/243/346/265/213/350/257/225/346/212/245/345/221/212//346/216/245/345/217/243/346/265/213/350/257/225/346/212/245/345/221/212/346/250/241/346/235/277.md +48 -0
- package/dist/raw/templates//350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/347/224/250/344/276/213//350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/347/224/250/344/276/213/346/250/241/346/235/277.md +30 -0
- package/dist/requirements/index.html +249 -0
- package/dist/templates/c//345/212/237/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212/index.html +1 -0
- package/dist/templates/c//345/212/237/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213/index.html +1 -0
- package/dist/templates/c//346/200/247/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212/index.html +1 -0
- package/dist/templates/c//346/200/247/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213/index.html +1 -0
- package/dist/templates/c//346/216/245/345/217/243/346/265/213/350/257/225/346/212/245/345/221/212/index.html +1 -0
- package/dist/templates/c//346/216/245/345/217/243/346/265/213/350/257/225/347/224/250/344/276/213/index.html +1 -0
- package/dist/templates/c//350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/347/224/250/344/276/213/index.html +1 -0
- package/dist/templates/index.html +249 -0
- package/dist/templates//345/212/237/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212//345/212/237/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212/346/250/241/346/235/277/index.html +394 -0
- package/dist/templates//346/200/247/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212//346/200/247/350/203/275/346/265/213/350/257/225/346/212/245/345/221/212/346/250/241/346/235/277/index.html +374 -0
- package/dist/templates//346/200/247/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213//346/200/247/350/203/275/346/265/213/350/257/225/347/224/250/344/276/213/346/250/241/346/235/277/index.html +336 -0
- package/dist/templates//346/216/245/345/217/243/346/265/213/350/257/225/346/212/245/345/221/212//346/216/245/345/217/243/346/265/213/350/257/225/346/212/245/345/221/212/346/250/241/346/235/277/index.html +363 -0
- package/dist/templates//350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/347/224/250/344/276/213//350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/347/224/250/344/276/213/346/250/241/346/235/277/index.html +338 -0
- package/docs//344/274/232/350/257/235/346/217/222/344/273/266.md +500 -0
- package/docs//345/205/250/345/261/200/346/217/222/344/273/266.md +1337 -0
- package/docs//345/256/211/350/243/205/346/211/213/345/206/214.md +110 -0
- package/docs//346/223/215/344/275/234/346/211/213/345/206/214.md +246 -0
- package/lh-plugins/lh-config.mjs +483 -0
- package/lh-plugins/lh-kb.mjs +206 -0
- package/lh-plugins/lh-memory.mjs +613 -0
- package/lh-plugins/lh-project-docs.mjs +875 -0
- package/lh-plugins/lh-resources.mjs +236 -0
- package/lh-plugins/session-log-deepseek.mjs +53 -0
- package/lh-plugins/session-persist.mjs +112 -0
- package/lh-plugins/session-query-sqlite.mjs +164 -0
- package/lh-plugins/storage-domain.mjs +82 -0
- package/package.json +115 -4
- package//347/251/272/351/227/264/346/261/240/.gitkeep +1 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" type="image/png" sizes="32x32" href="/favicon.png"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><title>性能测试用例(场景)模板 · 测试模板库</title><script>
|
|
2
|
+
// 尽早禁用滚动恢复,避免整页跳转/切页时停在旧滚动位置造成文字叠影
|
|
3
|
+
try {
|
|
4
|
+
if ('scrollRestoration' in history) history.scrollRestoration = 'manual';
|
|
5
|
+
} catch (e) {}
|
|
6
|
+
</script><script type="module" src="/_astro/Base.astro_astro_type_script_index_0_lang.lxEwQ1R4.js"></script><meta name="astro-view-transitions-enabled" content="true"><meta name="astro-view-transitions-fallback" content="animate"><script type="module" src="/_astro/ClientRouter.astro_astro_type_script_index_0_lang.CO1cjG9V.js"></script><script>
|
|
7
|
+
(function () {
|
|
8
|
+
try {
|
|
9
|
+
var raw = localStorage.getItem('tw.app.config.v1');
|
|
10
|
+
if (raw) {
|
|
11
|
+
var cfg = JSON.parse(raw);
|
|
12
|
+
var g = (cfg && cfg.general) || {};
|
|
13
|
+
var root = document.documentElement;
|
|
14
|
+
var size = typeof g.sessionFontSize === 'number' ? Math.round(g.sessionFontSize) : 14;
|
|
15
|
+
if (size < 12) size = 12;
|
|
16
|
+
if (size > 22) size = 22;
|
|
17
|
+
var px = size + 'px';
|
|
18
|
+
root.style.setProperty('--tw-ui-font-size', px);
|
|
19
|
+
root.style.setProperty('--tw-session-font-size', px);
|
|
20
|
+
var appearance = g.appearance;
|
|
21
|
+
var theme =
|
|
22
|
+
appearance === 'dark' || appearance === 'light'
|
|
23
|
+
? appearance
|
|
24
|
+
: window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
25
|
+
? 'dark'
|
|
26
|
+
: 'light';
|
|
27
|
+
root.dataset.theme = theme;
|
|
28
|
+
root.style.colorScheme = theme;
|
|
29
|
+
if (g.language === 'en') root.lang = 'en';
|
|
30
|
+
}
|
|
31
|
+
if (localStorage.getItem('tw.nav.collapsed') === '1') {
|
|
32
|
+
document.documentElement.dataset.navCollapsed = '1';
|
|
33
|
+
}
|
|
34
|
+
var spaceId = (
|
|
35
|
+
sessionStorage.getItem('tw.space.active-id') ||
|
|
36
|
+
localStorage.getItem('tw.space.active-id') ||
|
|
37
|
+
''
|
|
38
|
+
).trim();
|
|
39
|
+
var cfgKey = spaceId ? 'tw.app.config.v1::' + spaceId : 'tw.app.config.v1';
|
|
40
|
+
var cfgRaw = localStorage.getItem(cfgKey) || localStorage.getItem('tw.app.config.v1');
|
|
41
|
+
if (cfgRaw) {
|
|
42
|
+
var savedCfg = JSON.parse(cfgRaw) || {};
|
|
43
|
+
var plugins = savedCfg.plugins || {};
|
|
44
|
+
if (plugins.cases === false) document.documentElement.dataset.pluginOffCases = '1';
|
|
45
|
+
if (plugins.prompts === false) document.documentElement.dataset.pluginOffPrompts = '1';
|
|
46
|
+
if (plugins.knowledgeLib === false)
|
|
47
|
+
document.documentElement.dataset.pluginOffKnowledgeLib = '1';
|
|
48
|
+
if (plugins.requirements === false)
|
|
49
|
+
document.documentElement.dataset.pluginOffRequirements = '1';
|
|
50
|
+
if (plugins.templates === false)
|
|
51
|
+
document.documentElement.dataset.pluginOffTemplates = '1';
|
|
52
|
+
// 本地已有菜单布局:静态占位(固定顺序平铺)不再亮相,避免闪一下「排序前」
|
|
53
|
+
if (Array.isArray(savedCfg.navGroups) || Array.isArray(savedCfg.navOrder)) {
|
|
54
|
+
document.documentElement.dataset.navCached = '1';
|
|
55
|
+
// 兜底:脚本没跑起来(岛始终为空)时把占位放回来,别让侧栏彻底空着
|
|
56
|
+
window.setTimeout(function () {
|
|
57
|
+
var island = document.querySelector('.tw-nav-libraries astro-island');
|
|
58
|
+
if (!island || !island.querySelector('.tw-nav-item-main')) {
|
|
59
|
+
document.documentElement.removeAttribute('data-nav-cached');
|
|
60
|
+
}
|
|
61
|
+
}, 3000);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch (e) {}
|
|
65
|
+
})();
|
|
66
|
+
</script><link rel="stylesheet" href="/_astro/Base.CFN876O3.css"><script type="module" src="/_astro/page.Bx2KSkeL.js"></script><style>[data-astro-transition-scope="astro-bbhp3f5g-3"] { view-transition-name: astro-bbhp3f5g-3; }@layer astro { ::view-transition-old(astro-bbhp3f5g-3) {
|
|
67
|
+
animation-duration: 180ms;
|
|
68
|
+
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
|
|
69
|
+
animation-fill-mode: both;
|
|
70
|
+
animation-name: astroFadeOut; }::view-transition-new(astro-bbhp3f5g-3) {
|
|
71
|
+
animation-duration: 180ms;
|
|
72
|
+
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
|
|
73
|
+
animation-fill-mode: both;
|
|
74
|
+
animation-name: astroFadeIn; }[data-astro-transition=back]::view-transition-old(astro-bbhp3f5g-3) {
|
|
75
|
+
animation-duration: 180ms;
|
|
76
|
+
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
|
|
77
|
+
animation-fill-mode: both;
|
|
78
|
+
animation-name: astroFadeOut; }[data-astro-transition=back]::view-transition-new(astro-bbhp3f5g-3) {
|
|
79
|
+
animation-duration: 180ms;
|
|
80
|
+
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
|
|
81
|
+
animation-fill-mode: both;
|
|
82
|
+
animation-name: astroFadeIn; } }[data-astro-transition-fallback="old"] [data-astro-transition-scope="astro-bbhp3f5g-3"],
|
|
83
|
+
[data-astro-transition-fallback="old"][data-astro-transition-scope="astro-bbhp3f5g-3"] {
|
|
84
|
+
animation-duration: 180ms;
|
|
85
|
+
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
|
|
86
|
+
animation-fill-mode: both;
|
|
87
|
+
animation-name: astroFadeOut; }[data-astro-transition-fallback="new"] [data-astro-transition-scope="astro-bbhp3f5g-3"],
|
|
88
|
+
[data-astro-transition-fallback="new"][data-astro-transition-scope="astro-bbhp3f5g-3"] {
|
|
89
|
+
animation-duration: 180ms;
|
|
90
|
+
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
|
|
91
|
+
animation-fill-mode: both;
|
|
92
|
+
animation-name: astroFadeIn; }[data-astro-transition=back][data-astro-transition-fallback="old"] [data-astro-transition-scope="astro-bbhp3f5g-3"],
|
|
93
|
+
[data-astro-transition=back][data-astro-transition-fallback="old"][data-astro-transition-scope="astro-bbhp3f5g-3"] {
|
|
94
|
+
animation-duration: 180ms;
|
|
95
|
+
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
|
|
96
|
+
animation-fill-mode: both;
|
|
97
|
+
animation-name: astroFadeOut; }[data-astro-transition=back][data-astro-transition-fallback="new"] [data-astro-transition-scope="astro-bbhp3f5g-3"],
|
|
98
|
+
[data-astro-transition=back][data-astro-transition-fallback="new"][data-astro-transition-scope="astro-bbhp3f5g-3"] {
|
|
99
|
+
animation-duration: 180ms;
|
|
100
|
+
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
|
|
101
|
+
animation-fill-mode: both;
|
|
102
|
+
animation-name: astroFadeIn; }</style></head><body class="tw-body antialiased"><div class="tw-shell min-h-screen"><aside class="tw-nav-aside fixed inset-y-0 left-0 flex flex-col z-45" id="tw-nav-aside"><div class="tw-nav-brand"><div class="tw-nav-brand-row"><a href="/" class="tw-nav-brand-link min-w-0 flex-1"><span class="tw-nav-brand-mark" aria-hidden="true"><img class="tw-nav-brand-mark-img" src="/brand-mark.png" alt="" width="36" height="36"></span><span class="tw-nav-brand-text"><span class="tw-nav-brand-title-row"><span class="tw-nav-brand-title" data-i18n="nav.brand">Lantern Harness</span><span class="tw-nav-brand-ver" data-hint="平台版本 v0.1.0">v0.1.0</span></span></span></a></div></div><div class="tw-nav-space-slot"><div class="tw-nav-space tw-nav-space-ph" id="tw-nav-space-ph" aria-hidden="true"><div class="tw-nav-space-ph-box"><span class="tw-nav-space-ph-ico"></span><span class="tw-nav-space-ph-lines"><span class="tw-nav-space-ph-bar is-cap"></span><span class="tw-nav-space-ph-bar is-name"></span></span></div></div><style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(function(){let e=async e=>{await(await e())()};(self.Astro||(self.Astro={})).only=e,window.dispatchEvent(new Event(`astro:only`))})();</script><script>(function(){function e(t){"@babel/helpers - typeof";return e=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},e(t)}function t(t,n){if(e(t)!=`object`||!t)return t;var r=t[Symbol.toPrimitive];if(r!==void 0){var i=r.call(t,n||`default`);if(e(i)!=`object`)return i;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(n===`string`?String:Number)(t)}function n(n){var r=t(n,`string`);return e(r)==`symbol`?r:r+``}function r(e,t,r){return(t=n(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}let i=/* @__PURE__ */ new Set([`__proto__`,`constructor`,`prototype`]);{let e={0:e=>a(e),1:e=>n(e),2:e=>new RegExp(e),3:e=>new Date(e),4:e=>new Map(n(e)),5:e=>new Set(n(e)),6:e=>BigInt(e),7:e=>new URL(e),8:e=>new Uint8Array(e),9:e=>new Uint16Array(e),10:e=>new Uint32Array(e),11:e=>1/0*e},t=t=>{let[n,r]=t;return n in e?e[n](r):void 0},n=e=>e.map(t),a=e=>typeof e!=`object`||!e?e:Object.fromEntries(Object.entries(e).map(([e,n])=>[e,t(n)]));class o extends HTMLElement{constructor(...e){super(...e),r(this,`Component`,void 0),r(this,`hydrator`,void 0),r(this,`hydrate`,async()=>{var e;if(!this.hydrator||!this.isConnected)return;let t=(e=this.parentElement)==null?void 0:e.closest(`astro-island[ssr]`);if(t){t.addEventListener(`astro:hydrate`,this.hydrate,{once:!0});return}let n=this.querySelectorAll(`astro-slot`),r={},i=this.querySelectorAll(`template[data-astro-template]`);for(let e of i){let t=e.closest(this.tagName);t!=null&&t.isSameNode(this)&&(r[e.getAttribute(`data-astro-template`)||`default`]=e.innerHTML,e.remove())}for(let e of n){let t=e.closest(this.tagName);t!=null&&t.isSameNode(this)&&(r[e.getAttribute(`name`)||`default`]=e.innerHTML)}let o;try{o=this.hasAttribute(`props`)?a(JSON.parse(this.getAttribute(`props`))):{}}catch(e){let t=this.getAttribute(`component-url`)||`<unknown>`,n=this.getAttribute(`component-export`);throw n&&(t+=` (export ${n})`),console.error(`[hydrate] Error parsing props for component ${t}`,this.getAttribute(`props`),e),e}await this.hydrator(this)(this.Component,o,r,{client:this.getAttribute(`client`)}),this.removeAttribute(`ssr`),this.dispatchEvent(new CustomEvent(`astro:hydrate`))}),r(this,`unmount`,()=>{this.isConnected||this.dispatchEvent(new CustomEvent(`astro:unmount`))})}disconnectedCallback(){document.removeEventListener(`astro:after-swap`,this.unmount),document.addEventListener(`astro:after-swap`,this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute(`await-children`)||document.readyState===`interactive`||document.readyState===`complete`)this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener(`DOMContentLoaded`,e),t.disconnect(),this.childrenConnectedCallback()},t=new MutationObserver(()=>{var t;((t=this.lastChild)==null?void 0:t.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue===`astro:end`&&(this.lastChild.remove(),e())});t.observe(this,{childList:!0}),document.addEventListener(`DOMContentLoaded`,e)}}async childrenConnectedCallback(){let e=this.getAttribute(`before-hydration-url`);e&&await import(e),this.start()}getRetryImportUrl(e){let t=new URL(e,document.baseURI);return t.searchParams.set(`astro-retry`,Date.now().toString()),t.toString()}async importWithRetry(e){try{return await import(e)}catch(t){return await new Promise(e=>setTimeout(e,1e3)),import(this.getRetryImportUrl(e))}}handleHydrationError(e){let t=this.getAttribute(`component-url`),n=new CustomEvent(`astro:hydration-error`,{cancelable:!0,bubbles:!0,composed:!0,detail:{error:e,componentUrl:t}});this.dispatchEvent(n)&&console.error(`[astro-island] Error hydrating ${t}`,e)}async start(){let e=JSON.parse(this.getAttribute(`opts`)),t=this.getAttribute(`client`);if(Astro[t]===void 0)window.addEventListener(`astro:${t}`,()=>this.start(),{once:!0});else try{await Astro[t](async()=>{let e=this.getAttribute(`renderer-url`);try{let[t,{default:n}]=await Promise.all([this.importWithRetry(this.getAttribute(`component-url`)),e?this.importWithRetry(e):Promise.resolve({default:()=>()=>{}})]),r=this.getAttribute(`component-export`)||`default`;if(r.includes(`.`)){this.Component=t;for(let e of r.split(`.`)){if(i.has(e)||!this.Component||typeof this.Component!=`object`&&typeof this.Component!=`function`||!Object.hasOwn(this.Component,e))throw Error(`Invalid component export path: ${r}`);this.Component=this.Component[e]}}else{if(i.has(r))throw Error(`Invalid component export path: ${r}`);this.Component=t[r]}return this.hydrator=n,this.hydrate}catch(e){return this.handleHydrationError(e),()=>{}}},e,this)}catch(e){this.handleHydrationError(e)}}attributeChangedCallback(){this.hydrate()}}r(o,`observedAttributes`,[`props`]),customElements.get(`astro-island`)||customElements.define(`astro-island`,o)}})();</script><astro-island uid="2wH2Dz" component-url="/_astro/SpaceSwitcher.CC-GMa_K.js" component-export="default" renderer-url="/_astro/client.Be_SakrB.js" props="{"data-astro-transition-persist":[0,"astro-gyrtbwwo-1"]}" ssr client="only" opts="{"name":"SpaceSwitcher","value":"react"}" data-astro-transition-persist="astro-gyrtbwwo-1"></astro-island></div><button type="button" id="tw-nav-toggle" class="tw-nav-toggle" aria-label="收起或展开左侧导航" aria-expanded="true" aria-controls="tw-nav-aside"><svg class="tw-nav-toggle-icon is-collapse" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 6l-6 6 6 6"></path></svg><svg class="tw-nav-toggle-icon is-expand" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 6l6 6-6 6"></path></svg></button><nav class="tw-nav-list flex-1 overflow-y-auto"><a href="/" class="tw-nav-item" data-i18n-hint="nav.home" data-hint="我的工作台"><svg class="tw-nav-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="M3 10.5 12 3l9 7.5"></path><path d="M5 9.5V20h14V9.5"></path></svg><span class="tw-nav-label" data-i18n="nav.home">我的工作台</span></a><div class="tw-nav-libraries"><div class="tw-nav-libraries-ph"><a href="/cases/library/" class="tw-nav-item" data-plugin-nav="cases"><svg class="tw-nav-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="M9 11l3 3L22 4"></path><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path></svg><span class="tw-nav-label" data-i18n="nav.caseManage">用例管理</span></a><a href="/prompts/" class="tw-nav-item" data-plugin-nav="prompts"><svg class="tw-nav-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="M12 20h9"></path><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"></path></svg><span class="tw-nav-label" data-i18n="nav.prompts">提示词管理</span></a><a href="/knowledge/" class="tw-nav-item" data-plugin-nav="knowledgeLib"><svg class="tw-nav-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg><span class="tw-nav-label" data-i18n="nav.knowledge">知识库管理</span></a><a href="/requirements/" class="tw-nav-item" data-plugin-nav="requirements" data-i18n-hint="nav.requirements"><svg class="tw-nav-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="M14 3v5h5"></path><path d="M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2z"></path><path d="M8 13h8"></path><path d="M8 17h6"></path></svg><span class="tw-nav-label" data-i18n="nav.requirements">需求文档库</span></a><a href="/templates/" class="tw-nav-item is-active font-medium" data-plugin-nav="templates" data-i18n-hint="nav.templates"><svg class="tw-nav-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="M12 3 3 8l9 5 9-5-9-5z"></path><path d="m3 13 9 5 9-5"></path></svg><span class="tw-nav-label" data-i18n="nav.templates">测试模板库</span></a></div><astro-island uid="Z1lgSd4" component-url="/_astro/NavLibraries.BwtNpUK6.js" component-export="default" renderer-url="/_astro/client.Be_SakrB.js" props="{"data-astro-transition-persist":[0,"astro-iywf7oe7-2"]}" ssr client="only" opts="{"name":"NavLibraries","value":"react"}" data-astro-transition-persist="astro-iywf7oe7-2"></astro-island></div><a href="/docs/manual/" class="tw-nav-item" data-hint="帮助中心" data-i18n-hint="nav.helpCenter"><svg class="tw-nav-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="M5 4.5h9.5L19 9v10.5H5V4.5Z" stroke-linejoin="round"></path><path d="M14.5 4.5V9H19" stroke-linejoin="round"></path><path d="M8 13h8M8 16.5h6" stroke-linecap="round"></path></svg><span class="tw-nav-label" data-i18n="nav.helpCenter">帮助中心</span></a></nav><div class="tw-nav-foot"><script>(function(){let e=async e=>{await(await e())()};(self.Astro||(self.Astro={})).load=e,window.dispatchEvent(new Event(`astro:load`))})();</script><astro-island uid="Z12jkyC" prefix="r1" component-url="/_astro/SettingsDialog.Dc2xMiIZ.js" component-export="default" renderer-url="/_astro/client.Be_SakrB.js" props="{}" ssr client="load" opts="{"name":"SettingsDialog","value":true}" await-children><div class="tw-nav-foot-row"><button type="button" class="tw-nav-settings-btn" data-hint="设置"><svg class="tw-nav-ico w-4.5 h-4.5 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9c.2.6.7 1 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"></path></svg><span data-i18n="nav.settings">设置</span></button><button type="button" class="tw-nav-provider-chip is-empty" data-hint="未配置模型"><span class="tw-nav-provider-dot" aria-hidden="true"></span><span class="tw-nav-provider-name">未配置</span></button></div><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span><!--astro:end--></astro-island></div></aside><script data-astro-rerun>
|
|
103
|
+
(function () {
|
|
104
|
+
var root = document.documentElement;
|
|
105
|
+
var btn = document.getElementById('tw-nav-toggle');
|
|
106
|
+
function apply(collapsed) {
|
|
107
|
+
if (collapsed) root.dataset.navCollapsed = '1';
|
|
108
|
+
else delete root.dataset.navCollapsed;
|
|
109
|
+
try {
|
|
110
|
+
localStorage.setItem('tw.nav.collapsed', collapsed ? '1' : '0');
|
|
111
|
+
} catch (e) {}
|
|
112
|
+
if (btn) {
|
|
113
|
+
btn.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
|
114
|
+
// 气泡已移除;改为动态更新不可见标签,读屏仍能获知当前动作
|
|
115
|
+
btn.setAttribute('aria-label', collapsed ? '展开导航' : '收起导航');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
apply(root.dataset.navCollapsed === '1');
|
|
119
|
+
if (btn) {
|
|
120
|
+
btn.addEventListener('click', function () {
|
|
121
|
+
apply(root.dataset.navCollapsed !== '1');
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
var raw = localStorage.getItem('tw.app.config.v1');
|
|
127
|
+
if (!raw) return;
|
|
128
|
+
var g = JSON.parse(raw).general || {};
|
|
129
|
+
if (g.language !== 'en') return;
|
|
130
|
+
var en = {
|
|
131
|
+
'nav.brand': 'Lantern Harness',
|
|
132
|
+
'nav.home': 'My Workbench',
|
|
133
|
+
'nav.caseManage': 'Test Cases',
|
|
134
|
+
'nav.prompts': 'Prompts',
|
|
135
|
+
'nav.knowledge': 'Knowledge Mgmt',
|
|
136
|
+
'nav.plugins': 'My Plugins',
|
|
137
|
+
'nav.docs': 'Documents',
|
|
138
|
+
'nav.requirements': 'Requirements',
|
|
139
|
+
'nav.templates': 'Test Templates',
|
|
140
|
+
'nav.settings': 'Settings',
|
|
141
|
+
'nav.changelog': 'Changelog',
|
|
142
|
+
'nav.changelogShort': 'Changelog',
|
|
143
|
+
'nav.helpCenter': 'Help Center',
|
|
144
|
+
'docs.nav.title': 'Docs',
|
|
145
|
+
'docs.nav.changelog': 'Changelog',
|
|
146
|
+
'docs.nav.manual': 'User Guide',
|
|
147
|
+
'docs.nav.install': 'Install Guide',
|
|
148
|
+
'nav.repo': 'GitHub',
|
|
149
|
+
'nav.repoShort': 'Repo',
|
|
150
|
+
'nav.npm': 'npm package',
|
|
151
|
+
'nav.npmShort': 'npm',
|
|
152
|
+
'nav.site': 'Yycy Docs',
|
|
153
|
+
'nav.siteShort': 'Site',
|
|
154
|
+
'nav.board': 'Questions & suggestions',
|
|
155
|
+
'nav.boardShort': 'Suggestions',
|
|
156
|
+
};
|
|
157
|
+
document.querySelectorAll('[data-i18n]').forEach(function (el) {
|
|
158
|
+
var key = el.getAttribute('data-i18n');
|
|
159
|
+
if (key && en[key]) el.textContent = en[key];
|
|
160
|
+
});
|
|
161
|
+
document.querySelectorAll('[data-i18n-hint]').forEach(function (el) {
|
|
162
|
+
var key = el.getAttribute('data-i18n-hint');
|
|
163
|
+
if (key && en[key]) el.setAttribute('data-hint', en[key]);
|
|
164
|
+
});
|
|
165
|
+
} catch (e) {}
|
|
166
|
+
})();
|
|
167
|
+
</script><main data-astro-transition-scope="astro-bbhp3f5g-3" class="tw-main min-h-screen"><div id="tw-route-loading" class="tw-route-loading is-hidden" role="status" aria-live="polite" aria-label="页面加载中"><span class="tw-route-loading-spin" aria-hidden="true"></span></div><article class="max-w-3xl mx-auto px-8 py-8"><nav class="mb-6 text-sm"><a href="/templates/" class="text-stone-400 hover:text-amber-700 transition-colors">测试模板库</a><span class="mx-2 text-stone-300">/</span><a href="/templates/c/%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B/" class="text-stone-400 hover:text-amber-700 transition-colors">性能测试用例</a></nav><header class="mb-8 pb-6 border-b border-stone-200"><div class="flex items-center gap-2 mb-3"><span class="text-xs px-2 py-0.5 bg-amber-100 text-amber-800 rounded">性能测试用例</span><span class="text-xs px-2 py-0.5 bg-stone-100 text-stone-500 rounded">v1.0</span></div><h1 class="text-2xl font-bold text-stone-900">性能测试用例(场景)模板</h1><p class="mt-2 text-sm text-stone-500 leading-6">性能测试场景设计模板,含负载模型、加压策略与通过标准的定义方式。</p><div class="mt-4"><a href="/raw/templates/性能测试用例/性能测试用例模板.md" class="inline-flex items-center gap-1.5 text-sm px-3 py-1.5 bg-stone-900 text-white rounded-md hover:bg-stone-700 transition-colors">下载 .md 源文件</a></div></header><div class="prose prose-stone max-w-none"><h1 id="性能测试用例场景模板">性能测试用例(场景)模板</h1>
|
|
168
|
+
<h2 id="一场景设计字段">一、场景设计字段</h2>
|
|
169
|
+
<table>
|
|
170
|
+
<thead>
|
|
171
|
+
<tr>
|
|
172
|
+
<th>字段</th>
|
|
173
|
+
<th>说明</th>
|
|
174
|
+
</tr>
|
|
175
|
+
</thead>
|
|
176
|
+
<tbody>
|
|
177
|
+
<tr>
|
|
178
|
+
<td>场景编号</td>
|
|
179
|
+
<td>格式 <code>PERF-三位序号</code></td>
|
|
180
|
+
</tr>
|
|
181
|
+
<tr>
|
|
182
|
+
<td>场景名称</td>
|
|
183
|
+
<td>如「核心下单链路基准场景」</td>
|
|
184
|
+
</tr>
|
|
185
|
+
<tr>
|
|
186
|
+
<td>业务配比</td>
|
|
187
|
+
<td>各接口/事务在混合流量中的占比(源自生产监控或运营预估)</td>
|
|
188
|
+
</tr>
|
|
189
|
+
<tr>
|
|
190
|
+
<td>并发模型</td>
|
|
191
|
+
<td>虚拟用户数、思考时间(Think Time)、Ramp-up 策略</td>
|
|
192
|
+
</tr>
|
|
193
|
+
<tr>
|
|
194
|
+
<td>加压策略</td>
|
|
195
|
+
<td>基准 / 负载 / 压力 / 稳定性(长跑)/ 峰值脉冲</td>
|
|
196
|
+
</tr>
|
|
197
|
+
<tr>
|
|
198
|
+
<td>持续时间</td>
|
|
199
|
+
<td>负载场景一般 30 分钟,稳定性 4 小时以上</td>
|
|
200
|
+
</tr>
|
|
201
|
+
<tr>
|
|
202
|
+
<td>数据准备</td>
|
|
203
|
+
<td>测试数据量级与构造方式(避免缓存命中率失真)</td>
|
|
204
|
+
</tr>
|
|
205
|
+
<tr>
|
|
206
|
+
<td>监控指标</td>
|
|
207
|
+
<td>应用(TPS/RT/错误率)+ 资源(CPU/内存/GC/连接池)+ 中间件</td>
|
|
208
|
+
</tr>
|
|
209
|
+
<tr>
|
|
210
|
+
<td>通过标准</td>
|
|
211
|
+
<td>可量化的验收阈值</td>
|
|
212
|
+
</tr>
|
|
213
|
+
</tbody>
|
|
214
|
+
</table>
|
|
215
|
+
<h2 id="二场景模板">二、场景模板</h2>
|
|
216
|
+
<table>
|
|
217
|
+
<thead>
|
|
218
|
+
<tr>
|
|
219
|
+
<th>场景编号</th>
|
|
220
|
+
<th>场景名称</th>
|
|
221
|
+
<th>业务配比</th>
|
|
222
|
+
<th>并发模型</th>
|
|
223
|
+
<th>加压策略</th>
|
|
224
|
+
<th>持续时间</th>
|
|
225
|
+
<th>通过标准</th>
|
|
226
|
+
</tr>
|
|
227
|
+
</thead>
|
|
228
|
+
<tbody>
|
|
229
|
+
<tr>
|
|
230
|
+
<td>PERF-001</td>
|
|
231
|
+
<td>核心链路基准</td>
|
|
232
|
+
<td>浏览:下单:支付 = 7:2:1</td>
|
|
233
|
+
<td>100 VU,思考时间 1-3s,Ramp-up 5min</td>
|
|
234
|
+
<td>负载测试</td>
|
|
235
|
+
<td>30min</td>
|
|
236
|
+
<td>TPS ≥ 200;P95 RT ≤ 800ms;错误率 ≤ 0.1%</td>
|
|
237
|
+
</tr>
|
|
238
|
+
</tbody>
|
|
239
|
+
</table>
|
|
240
|
+
<h2 id="三通过标准的写法">三、通过标准的写法</h2>
|
|
241
|
+
<ul>
|
|
242
|
+
<li>吞吐量:<code>核心接口 TPS ≥ 目标值</code>(注明统计窗口,去掉 Ramp-up 阶段)</li>
|
|
243
|
+
<li>响应时间:用分位数而非平均值,如 <code>P95 RT ≤ 800ms,P99 RT ≤ 2s</code></li>
|
|
244
|
+
<li>错误率:<code>业务错误率 ≤ 0.1%</code>(区分业务错误与系统错误)</li>
|
|
245
|
+
<li>资源水位:<code>CPU ≤ 75%,堆内存 Old 区占用无持续增长</code>(防内存泄漏)</li>
|
|
246
|
+
</ul>
|
|
247
|
+
<h2 id="四场景选择策略">四、场景选择策略</h2>
|
|
248
|
+
<ol>
|
|
249
|
+
<li><strong>基准测试</strong>:单接口低并发,建立性能基线,用于回归对比。</li>
|
|
250
|
+
<li><strong>负载测试</strong>:预期峰值 1.2 倍压力,验证容量达标。</li>
|
|
251
|
+
<li><strong>压力测试</strong>:逐步加压直至拐点,找到系统极限与瓶颈点。</li>
|
|
252
|
+
<li><strong>稳定性测试</strong>:0.8 倍峰值长跑 4h+,暴露内存泄漏与资源耗尽。</li>
|
|
253
|
+
</ol>
|
|
254
|
+
</div></article></main></div><script>
|
|
255
|
+
(function () {
|
|
256
|
+
/**
|
|
257
|
+
* 切页滚回顶部。
|
|
258
|
+
* 只在「换页后」重置:若在 before-preparation(点击瞬间)就重置,
|
|
259
|
+
* 当前页面会先跳回顶部、再等新页到达,用户看到的就是「打开时还在滚动」。
|
|
260
|
+
* 带锚点(#)时交给浏览器定位,不强制回顶。
|
|
261
|
+
*/
|
|
262
|
+
try {
|
|
263
|
+
if ('scrollRestoration' in history) history.scrollRestoration = 'manual';
|
|
264
|
+
} catch (e) {}
|
|
265
|
+
function twScrollTop() {
|
|
266
|
+
if (location.hash) return;
|
|
267
|
+
if (window.scrollY === 0 && document.documentElement.scrollTop === 0) return;
|
|
268
|
+
try {
|
|
269
|
+
// instant:不受将来可能加入的 scroll-behavior: smooth 影响
|
|
270
|
+
window.scrollTo({ top: 0, left: 0, behavior: 'instant' });
|
|
271
|
+
} catch (e) {
|
|
272
|
+
// 旧浏览器不认 instant,会抛 TypeError,退回普通写法
|
|
273
|
+
window.scrollTo(0, 0);
|
|
274
|
+
}
|
|
275
|
+
document.documentElement.scrollTop = 0;
|
|
276
|
+
document.body.scrollTop = 0;
|
|
277
|
+
var main = document.querySelector('.tw-main');
|
|
278
|
+
if (main) main.scrollTop = 0;
|
|
279
|
+
}
|
|
280
|
+
document.addEventListener('astro:after-swap', twScrollTop);
|
|
281
|
+
document.addEventListener('astro:page-load', twScrollTop);
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* 软导航「闪白」修复。
|
|
285
|
+
* ClientRouter 交换文档时,会用新文档 <html> 的属性整体替换当前 <html>,
|
|
286
|
+
* 于是 data-theme 连同 style 里的 color-scheme 一起被清空;在主题属性
|
|
287
|
+
* 恢复之前,color-scheme 回落到 normal,画布变白 —— 深色主题下就是
|
|
288
|
+
* 切页瞬间的闪白(与具体页面无关,所有路由都受影响)。
|
|
289
|
+
* 在交换前把主题 / 语言写进新文档的 <html>,交换完成时即已带上,不留空档。
|
|
290
|
+
*/
|
|
291
|
+
document.addEventListener('astro:before-swap', function (e) {
|
|
292
|
+
var next = e.newDocument;
|
|
293
|
+
if (!next || !next.documentElement) return;
|
|
294
|
+
var cur = document.documentElement;
|
|
295
|
+
var nextRoot = next.documentElement;
|
|
296
|
+
var theme = cur.dataset.theme;
|
|
297
|
+
if (theme) {
|
|
298
|
+
nextRoot.dataset.theme = theme;
|
|
299
|
+
nextRoot.style.colorScheme = theme;
|
|
300
|
+
}
|
|
301
|
+
if (cur.lang) nextRoot.lang = cur.lang;
|
|
302
|
+
// 新文档是「解析」出来的:head 里的内联脚本在解析阶段不会执行,
|
|
303
|
+
// 于是所有运行期写在 <html> 上的状态都不会自带。主题 / 语言之外,
|
|
304
|
+
// 侧栏收起状态与空间服务可用性同样要显式搬过去,否则每次软导航都会丢
|
|
305
|
+
// (表现为侧栏自己弹开、骨架又冒出来)。
|
|
306
|
+
if (cur.dataset.navCollapsed === '1') nextRoot.dataset.navCollapsed = '1';
|
|
307
|
+
if (cur.dataset.spaceService) nextRoot.dataset.spaceService = cur.dataset.spaceService;
|
|
308
|
+
// 插件开关同理:不搬过去会先亮出被隐藏的侧栏入口,等 page-load 再收起
|
|
309
|
+
[
|
|
310
|
+
'pluginOffCases',
|
|
311
|
+
'pluginOffPrompts',
|
|
312
|
+
'pluginOffKnowledgeLib',
|
|
313
|
+
'pluginOffRequirements',
|
|
314
|
+
'pluginOffTemplates',
|
|
315
|
+
].forEach(function (key) {
|
|
316
|
+
if (cur.dataset[key]) nextRoot.dataset[key] = cur.dataset[key];
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
/** 版本日志「查看更多」:委托绑定,兼容 View Transitions 软导航 */
|
|
321
|
+
document.addEventListener('click', function (e) {
|
|
322
|
+
var t = e.target;
|
|
323
|
+
if (!(t instanceof Element)) return;
|
|
324
|
+
var btn = t.closest('.tw-changelog-toggle');
|
|
325
|
+
if (!(btn instanceof HTMLButtonElement)) return;
|
|
326
|
+
var card = btn.closest('.tw-changelog-card');
|
|
327
|
+
if (!(card instanceof HTMLElement)) return;
|
|
328
|
+
e.preventDefault();
|
|
329
|
+
var next = card.getAttribute('data-open') !== 'true';
|
|
330
|
+
card.setAttribute('data-open', next ? 'true' : 'false');
|
|
331
|
+
btn.setAttribute('aria-expanded', next ? 'true' : 'false');
|
|
332
|
+
var label = btn.querySelector('.tw-changelog-toggle-label');
|
|
333
|
+
if (label) label.textContent = next ? '收起' : '查看更多';
|
|
334
|
+
});
|
|
335
|
+
})();
|
|
336
|
+
</script></body></html>
|