改进首页布局,增加视频预览;简化demo清单逻辑。
Browse files- .gitattributes +1 -0
- backend/access_log.py +5 -7
- backend/api/static.py +2 -0
- client/src/analysis.html +1 -1
- client/src/attribution.html +1 -1
- client/src/chat.html +1 -1
- client/src/content/images/attribute-dark.png +2 -2
- client/src/content/images/attribute.png +2 -2
- client/src/content/images/chat-dark.png +2 -2
- client/src/content/images/chat.png +2 -2
- client/src/content/images/dag-cn.png +2 -2
- client/src/content/images/dag-dark-cn.png +2 -2
- client/src/content/images/dag-dark-en.png +2 -2
- client/src/content/images/dag-dark.mov +3 -0
- client/src/content/images/dag-en.png +2 -2
- client/src/content/images/dag.mov +3 -0
- client/src/content/images/highlight-dark.png +2 -2
- client/src/content/images/highlight.png +2 -2
- client/src/css/home.scss +96 -35
- client/src/gen_attribute.html +1 -1
- client/src/index.html +4 -4
- client/src/scripts/genAttributeDemoManifestPlugin.js +38 -24
- client/src/scripts/injectPageMetaIntoHtml.js +5 -1
- client/src/ts/demos/genAttributeBundledDemoManifest.generated.ts +4 -0
- client/src/ts/demos/genAttributeBundledDemos.ts +6 -34
- client/src/ts/gen_attribute.ts +4 -6
- client/src/ts/home.ts +17 -2
- client/src/ts/lang/translations.ts +0 -1
- client/src/ts/types/html.d.ts +5 -0
- client/src/webpack.config.js +4 -0
.gitattributes
CHANGED
|
@@ -10,6 +10,7 @@
|
|
| 10 |
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
*.model filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 13 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
*.npz filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 10 |
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.mov filter=lfs diff=lfs merge=lfs -text
|
| 14 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 15 |
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 16 |
*.npz filter=lfs diff=lfs merge=lfs -text
|
backend/access_log.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
"""服务访问日志"""
|
| 2 |
from datetime import datetime
|
| 3 |
from typing import Optional
|
|
|
|
| 4 |
|
| 5 |
from flask import request
|
| 6 |
import threading
|
|
@@ -45,15 +46,12 @@ def _log_request(event_type: str, details: str = "", client_ip: str = None):
|
|
| 45 |
|
| 46 |
|
| 47 |
def log_page_load(path: str):
|
| 48 |
-
"""记录页面访问(含 ?ref= 参数)"""
|
| 49 |
-
details = f"path='{path}'"
|
| 50 |
try:
|
| 51 |
-
|
| 52 |
-
if
|
| 53 |
-
details += f", ref='{ref}'"
|
| 54 |
except RuntimeError:
|
| 55 |
-
|
| 56 |
-
_log_request("📄 页面访问",
|
| 57 |
|
| 58 |
|
| 59 |
def log_demo_file(path: str):
|
|
|
|
| 1 |
"""服务访问日志"""
|
| 2 |
from datetime import datetime
|
| 3 |
from typing import Optional
|
| 4 |
+
from urllib.parse import unquote
|
| 5 |
|
| 6 |
from flask import request
|
| 7 |
import threading
|
|
|
|
| 46 |
|
| 47 |
|
| 48 |
def log_page_load(path: str):
|
|
|
|
|
|
|
| 49 |
try:
|
| 50 |
+
qs = request.query_string.decode("utf-8", errors="replace")
|
| 51 |
+
combined = f"{path}?{unquote(qs)}" if qs else path
|
|
|
|
| 52 |
except RuntimeError:
|
| 53 |
+
combined = path
|
| 54 |
+
_log_request("📄 页面访问", f"path={combined!r}")
|
| 55 |
|
| 56 |
|
| 57 |
def log_demo_file(path: str):
|
backend/api/static.py
CHANGED
|
@@ -41,6 +41,8 @@ def register_static_routes(app):
|
|
| 41 |
"""serves all files from ./client/dist/ to ``/client/<path:path>``"""
|
| 42 |
if path.endswith('.html'):
|
| 43 |
log_page_load(path)
|
|
|
|
|
|
|
| 44 |
return _read_static_file('client/dist', path)
|
| 45 |
|
| 46 |
@app.route('/demo/<path:path>')
|
|
|
|
| 41 |
"""serves all files from ./client/dist/ to ``/client/<path:path>``"""
|
| 42 |
if path.endswith('.html'):
|
| 43 |
log_page_load(path)
|
| 44 |
+
if path.endswith('.json'):
|
| 45 |
+
log_demo_file(path)
|
| 46 |
return _read_static_file('client/dist', path)
|
| 47 |
|
| 48 |
@app.route('/demo/<path:path>')
|
client/src/analysis.html
CHANGED
|
@@ -18,7 +18,7 @@
|
|
| 18 |
<header class="app-page-toolbar app-page-toolbar--bleed">
|
| 19 |
<h1 class="page-toolbar-title"><span class="title-main-line"><span data-page-title data-i18n></span><span class="title-tagline" data-page-subtitle data-i18n></span></span></h1>
|
| 20 |
<div class="app-page-toolbar-actions">
|
| 21 |
-
<a href="index.html" class="home-link" title="
|
| 22 |
<a href="compare.html?showTextRender=1&demos=/quick-start-1.json,/quick-start-2.json" target="_blank" class="compare-link" style="display: none;" title="Compare analysis results" data-i18n="text,title">Compare results</a>
|
| 23 |
<div class="settings-menu-wrapper">
|
| 24 |
<button id="settings_btn" class="settings-btn" title="Settings" data-i18n="title">
|
|
|
|
| 18 |
<header class="app-page-toolbar app-page-toolbar--bleed">
|
| 19 |
<h1 class="page-toolbar-title"><span class="title-main-line"><span data-page-title data-i18n></span><span class="title-tagline" data-page-subtitle data-i18n></span></span></h1>
|
| 20 |
<div class="app-page-toolbar-actions">
|
| 21 |
+
<a href="index.html" class="home-link" title="Info Lens" data-i18n="text,title">Info Lens</a>
|
| 22 |
<a href="compare.html?showTextRender=1&demos=/quick-start-1.json,/quick-start-2.json" target="_blank" class="compare-link" style="display: none;" title="Compare analysis results" data-i18n="text,title">Compare results</a>
|
| 23 |
<div class="settings-menu-wrapper">
|
| 24 |
<button id="settings_btn" class="settings-btn" title="Settings" data-i18n="title">
|
client/src/attribution.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
| 16 |
<header class="app-page-toolbar app-page-toolbar--bleed">
|
| 17 |
<h1 class="page-toolbar-title"><span class="title-main-line"><span data-page-title data-i18n></span><span class="title-tagline" data-page-subtitle data-i18n></span></span></h1>
|
| 18 |
<div class="app-page-toolbar-actions">
|
| 19 |
-
<a href="index.html" class="home-link" title="
|
| 20 |
<div class="settings-menu-wrapper">
|
| 21 |
<button id="settings_btn" class="settings-btn" title="Settings" data-i18n="title">
|
| 22 |
<span class="settings-icon">⚙️</span>
|
|
|
|
| 16 |
<header class="app-page-toolbar app-page-toolbar--bleed">
|
| 17 |
<h1 class="page-toolbar-title"><span class="title-main-line"><span data-page-title data-i18n></span><span class="title-tagline" data-page-subtitle data-i18n></span></span></h1>
|
| 18 |
<div class="app-page-toolbar-actions">
|
| 19 |
+
<a href="index.html" class="home-link" title="Info Lens" data-i18n="text,title">Info Lens</a>
|
| 20 |
<div class="settings-menu-wrapper">
|
| 21 |
<button id="settings_btn" class="settings-btn" title="Settings" data-i18n="title">
|
| 22 |
<span class="settings-icon">⚙️</span>
|
client/src/chat.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
| 16 |
<header class="app-page-toolbar app-page-toolbar--bleed">
|
| 17 |
<h1 class="page-toolbar-title"><span class="title-main-line"><span data-page-title data-i18n></span><span class="title-tagline" data-page-subtitle data-i18n></span></span></h1>
|
| 18 |
<div class="app-page-toolbar-actions">
|
| 19 |
-
<a href="index.html" class="home-link" title="
|
| 20 |
<div class="settings-menu-wrapper">
|
| 21 |
<button id="settings_btn" class="settings-btn" title="Settings" data-i18n="title">
|
| 22 |
<span class="settings-icon">⚙️</span>
|
|
|
|
| 16 |
<header class="app-page-toolbar app-page-toolbar--bleed">
|
| 17 |
<h1 class="page-toolbar-title"><span class="title-main-line"><span data-page-title data-i18n></span><span class="title-tagline" data-page-subtitle data-i18n></span></span></h1>
|
| 18 |
<div class="app-page-toolbar-actions">
|
| 19 |
+
<a href="index.html" class="home-link" title="Info Lens" data-i18n="text,title">Info Lens</a>
|
| 20 |
<div class="settings-menu-wrapper">
|
| 21 |
<button id="settings_btn" class="settings-btn" title="Settings" data-i18n="title">
|
| 22 |
<span class="settings-icon">⚙️</span>
|
client/src/content/images/attribute-dark.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
client/src/content/images/attribute.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
client/src/content/images/chat-dark.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
client/src/content/images/chat.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
client/src/content/images/dag-cn.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
client/src/content/images/dag-dark-cn.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
client/src/content/images/dag-dark-en.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
client/src/content/images/dag-dark.mov
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ed07cfaa00a91cfc7ed3ddc0edfa19747e864127053af2bf9bce24dcdfa39c65
|
| 3 |
+
size 132450
|
client/src/content/images/dag-en.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
client/src/content/images/dag.mov
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:67b81278d3c4a0e311bd4c7d2ebea552e91b1a0b4fbcc3f85568490a0098d768
|
| 3 |
+
size 164323
|
client/src/content/images/highlight-dark.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
client/src/content/images/highlight.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
client/src/css/home.scss
CHANGED
|
@@ -1,6 +1,23 @@
|
|
| 1 |
$breakpoint-mobile: 767px;
|
| 2 |
|
| 3 |
-
// 导航页
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
body.nav-landing-page {
|
| 5 |
--nav-landing-title-size: 22px;
|
| 6 |
--nav-landing-title-weight: 600;
|
|
@@ -11,8 +28,6 @@ body.nav-landing-page {
|
|
| 11 |
--nav-landing-card-bg: #eeeeee;
|
| 12 |
--nav-landing-card-bg-hover: #e0e0e0;
|
| 13 |
|
| 14 |
-
// 覆盖子页/响应式里对 body{height:100vh} 的设定:固定视口高 + flex 纵向居中时,
|
| 15 |
-
// 内容总高超过一屏会向上溢出,顶栏(设置按钮)会落在首屏外。
|
| 16 |
position: relative;
|
| 17 |
min-height: 100vh;
|
| 18 |
height: auto;
|
|
@@ -25,7 +40,6 @@ body.nav-landing-page {
|
|
| 25 |
background: var(--app-bg, #f5f5f5);
|
| 26 |
}
|
| 27 |
|
| 28 |
-
// 相对 body 右上角定位,随页面滚动;不参与 flex 流,不单独占一行
|
| 29 |
.nav-landing-settings {
|
| 30 |
position: absolute;
|
| 31 |
top: max(12px, env(safe-area-inset-top));
|
|
@@ -43,13 +57,11 @@ html[data-theme='dark'] body.nav-landing-page {
|
|
| 43 |
|
| 44 |
.nav-landing-main {
|
| 45 |
width: 100%;
|
| 46 |
-
// 与双列断点一致:窄/中屏仍由 width:100% 撑满视口(受 padding 限制)
|
| 47 |
max-width: 44rem;
|
| 48 |
box-sizing: border-box;
|
| 49 |
padding: 2rem 1.25rem;
|
| 50 |
}
|
| 51 |
|
| 52 |
-
// 宽屏:放大主区域,卡片与预览块随之变宽变高
|
| 53 |
@media (min-width: $breakpoint-mobile + 1) {
|
| 54 |
.nav-landing-main {
|
| 55 |
max-width: 64rem;
|
|
@@ -86,30 +98,92 @@ html[data-theme='dark'] body.nav-landing-page {
|
|
| 86 |
letter-spacing: 0.02em;
|
| 87 |
}
|
| 88 |
|
|
|
|
| 89 |
.nav-landing-grid {
|
| 90 |
display: grid;
|
| 91 |
-
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
}
|
| 94 |
|
| 95 |
-
|
|
|
|
| 96 |
.nav-landing-grid {
|
| 97 |
-
grid-template-columns: 1fr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
}
|
| 99 |
}
|
| 100 |
|
|
|
|
| 101 |
.nav-landing-card {
|
| 102 |
display: flex;
|
| 103 |
flex-direction: column;
|
| 104 |
-
gap:
|
| 105 |
-
padding:
|
| 106 |
-
border-radius:
|
| 107 |
text-decoration: none;
|
| 108 |
color: inherit;
|
| 109 |
text-align: left;
|
| 110 |
background: var(--nav-landing-card-bg, #e6e6e6);
|
| 111 |
border: 1px solid var(--border-color, #ddd);
|
| 112 |
transition: background 0.15s ease, border-color 0.15s ease;
|
|
|
|
| 113 |
|
| 114 |
&:hover {
|
| 115 |
background: var(--nav-landing-card-bg-hover, #dcdcdc);
|
|
@@ -120,14 +194,13 @@ html[data-theme='dark'] .nav-landing-card {
|
|
| 120 |
border-color: var(--border-color, #333);
|
| 121 |
}
|
| 122 |
|
| 123 |
-
// 与卡片同色,不单独铺底
|
| 124 |
.nav-landing-card-text {
|
| 125 |
padding: 0;
|
| 126 |
}
|
| 127 |
|
| 128 |
.nav-landing-card-title {
|
| 129 |
display: block;
|
| 130 |
-
font-size:
|
| 131 |
font-weight: 600;
|
| 132 |
line-height: var(--nav-landing-line-height);
|
| 133 |
}
|
|
@@ -135,7 +208,7 @@ html[data-theme='dark'] .nav-landing-card {
|
|
| 135 |
.nav-landing-card-subtitle {
|
| 136 |
display: block;
|
| 137 |
text-align: right;
|
| 138 |
-
font-size:
|
| 139 |
font-weight: 400;
|
| 140 |
line-height: var(--nav-landing-line-height);
|
| 141 |
color: var(--text-muted, #666);
|
|
@@ -145,16 +218,14 @@ html[data-theme='dark'] .nav-landing-card {
|
|
| 145 |
flex-shrink: 0;
|
| 146 |
width: 100%;
|
| 147 |
aspect-ratio: 4 / 3;
|
| 148 |
-
border-radius:
|
| 149 |
box-sizing: border-box;
|
| 150 |
background: var(--bg-hover, #f0f0f0);
|
| 151 |
background-size: cover;
|
| 152 |
-
// 裁切时保留左上内容(右侧、下侧被裁)
|
| 153 |
background-position: left top;
|
| 154 |
background-repeat: no-repeat;
|
| 155 |
}
|
| 156 |
|
| 157 |
-
// content/images:日/夜各一套;LLM Causal Flow 另按 html lang 分中/英,经 webpack 产出 URL
|
| 158 |
.nav-landing-card[data-nav-page='analysis'] .nav-landing-card-shot {
|
| 159 |
background-image: url('../content/images/highlight.png');
|
| 160 |
background-color: var(--bg-hover, #f0f0f0);
|
|
@@ -170,15 +241,15 @@ html[data-theme='dark'] .nav-landing-card {
|
|
| 170 |
background-color: var(--bg-hover, #f0f0f0);
|
| 171 |
}
|
| 172 |
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
|
|
|
| 176 |
background-color: var(--bg-hover, #f0f0f0);
|
| 177 |
}
|
| 178 |
|
| 179 |
-
html[
|
| 180 |
-
background-
|
| 181 |
-
background-color: var(--bg-hover, #f0f0f0);
|
| 182 |
}
|
| 183 |
|
| 184 |
html[data-theme='dark'] .nav-landing-card[data-nav-page='analysis'] .nav-landing-card-shot {
|
|
@@ -195,13 +266,3 @@ html[data-theme='dark'] .nav-landing-card[data-nav-page='attribution'] .nav-land
|
|
| 195 |
background-image: url('../content/images/attribute-dark.png');
|
| 196 |
background-color: var(--bg-hover, #2a2a2a);
|
| 197 |
}
|
| 198 |
-
|
| 199 |
-
html[data-theme='dark'] .nav-landing-card[data-nav-page='genAttribute'] .nav-landing-card-shot {
|
| 200 |
-
background-image: url('../content/images/dag-dark-en.png');
|
| 201 |
-
background-color: var(--bg-hover, #2a2a2a);
|
| 202 |
-
}
|
| 203 |
-
|
| 204 |
-
html[data-theme='dark'][lang='zh-CN'] .nav-landing-card[data-nav-page='genAttribute'] .nav-landing-card-shot {
|
| 205 |
-
background-image: url('../content/images/dag-dark-cn.png');
|
| 206 |
-
background-color: var(--bg-hover, #2a2a2a);
|
| 207 |
-
}
|
|
|
|
| 1 |
$breakpoint-mobile: 767px;
|
| 2 |
|
| 3 |
+
// ----- 导航首页卡片尺寸(只改此块)-----
|
| 4 |
+
$nav-grid-gap: 0.75rem;
|
| 5 |
+
$nav-card-gap: 0.65rem;
|
| 6 |
+
$nav-card-pad-block: 0.75rem;
|
| 7 |
+
$nav-card-pad-inline: 0.85rem;
|
| 8 |
+
$nav-card-radius: 8px;
|
| 9 |
+
$nav-card-shot-radius: 6px;
|
| 10 |
+
$nav-title-size: 0.95rem;
|
| 11 |
+
$nav-subtitle-size: 0.82rem;
|
| 12 |
+
|
| 13 |
+
$nav-featured-width-narrow: 100%; // 单列竖排时大卡铺满轨宽
|
| 14 |
+
$nav-featured-width-wide: 60%; // 宽屏首行内宽度,对齐旧版双列中单列占比
|
| 15 |
+
|
| 16 |
+
// 小卡一键标度;只占格宽时用 track,只缩内边距/字时用 inner
|
| 17 |
+
$nav-compact-track-fraction: 0.9;
|
| 18 |
+
$nav-compact-inner-scale: 0.9;
|
| 19 |
+
// -----
|
| 20 |
+
|
| 21 |
body.nav-landing-page {
|
| 22 |
--nav-landing-title-size: 22px;
|
| 23 |
--nav-landing-title-weight: 600;
|
|
|
|
| 28 |
--nav-landing-card-bg: #eeeeee;
|
| 29 |
--nav-landing-card-bg-hover: #e0e0e0;
|
| 30 |
|
|
|
|
|
|
|
| 31 |
position: relative;
|
| 32 |
min-height: 100vh;
|
| 33 |
height: auto;
|
|
|
|
| 40 |
background: var(--app-bg, #f5f5f5);
|
| 41 |
}
|
| 42 |
|
|
|
|
| 43 |
.nav-landing-settings {
|
| 44 |
position: absolute;
|
| 45 |
top: max(12px, env(safe-area-inset-top));
|
|
|
|
| 57 |
|
| 58 |
.nav-landing-main {
|
| 59 |
width: 100%;
|
|
|
|
| 60 |
max-width: 44rem;
|
| 61 |
box-sizing: border-box;
|
| 62 |
padding: 2rem 1.25rem;
|
| 63 |
}
|
| 64 |
|
|
|
|
| 65 |
@media (min-width: $breakpoint-mobile + 1) {
|
| 66 |
.nav-landing-main {
|
| 67 |
max-width: 64rem;
|
|
|
|
| 98 |
letter-spacing: 0.02em;
|
| 99 |
}
|
| 100 |
|
| 101 |
+
// 首行一张 featured,末行三张 compact;单列时竖排同序
|
| 102 |
.nav-landing-grid {
|
| 103 |
display: grid;
|
| 104 |
+
gap: $nav-grid-gap;
|
| 105 |
+
justify-items: center;
|
| 106 |
+
grid-template-columns: 1fr;
|
| 107 |
+
grid-template-areas:
|
| 108 |
+
'hero'
|
| 109 |
+
'c1'
|
| 110 |
+
'c2'
|
| 111 |
+
'c3';
|
| 112 |
}
|
| 113 |
|
| 114 |
+
// 宽屏:首行大卡跨三列,末行三列各放一张小卡
|
| 115 |
+
@media (min-width: $breakpoint-mobile + 1) {
|
| 116 |
.nav-landing-grid {
|
| 117 |
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
| 118 |
+
grid-template-areas:
|
| 119 |
+
'hero hero hero'
|
| 120 |
+
'c1 c2 c3';
|
| 121 |
+
}
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
// index.html 顺序:nth-child(1) featured,(2)(3)(4) 为三张 compact
|
| 125 |
+
.nav-landing-card--featured {
|
| 126 |
+
grid-area: hero;
|
| 127 |
+
width: $nav-featured-width-narrow;
|
| 128 |
+
justify-self: stretch;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
@media (min-width: $breakpoint-mobile + 1) {
|
| 132 |
+
.nav-landing-card--featured {
|
| 133 |
+
width: $nav-featured-width-wide;
|
| 134 |
+
justify-self: center; // 在跨三列的首行里水平居中
|
| 135 |
+
}
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
// 三张 compact 对应第二行 c1–c3(与 grid-template-areas 一致)
|
| 139 |
+
.nav-landing-card.nav-landing-card--compact:nth-child(2) {
|
| 140 |
+
grid-area: c1;
|
| 141 |
+
width: #{$nav-compact-track-fraction * 100%};
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
.nav-landing-card.nav-landing-card--compact:nth-child(3) {
|
| 145 |
+
grid-area: c2;
|
| 146 |
+
width: #{$nav-compact-track-fraction * 100%};
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
.nav-landing-card.nav-landing-card--compact:nth-child(4) {
|
| 150 |
+
grid-area: c3;
|
| 151 |
+
width: #{$nav-compact-track-fraction * 100%};
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
// 双类链:特异性高于单独 .nav-landing-card,避免基类盖掉小卡的 padding/gap
|
| 155 |
+
.nav-landing-card.nav-landing-card--compact {
|
| 156 |
+
padding: #{$nav-card-pad-block * $nav-compact-inner-scale} #{$nav-card-pad-inline * $nav-compact-inner-scale};
|
| 157 |
+
gap: #{$nav-card-gap * $nav-compact-inner-scale};
|
| 158 |
+
border-radius: #{$nav-card-radius * $nav-compact-inner-scale};
|
| 159 |
+
|
| 160 |
+
.nav-landing-card-title {
|
| 161 |
+
font-size: #{$nav-title-size * $nav-compact-inner-scale};
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
.nav-landing-card-subtitle {
|
| 165 |
+
font-size: #{$nav-subtitle-size * $nav-compact-inner-scale};
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
.nav-landing-card-shot {
|
| 169 |
+
border-radius: #{$nav-card-shot-radius * $nav-compact-inner-scale};
|
| 170 |
}
|
| 171 |
}
|
| 172 |
|
| 173 |
+
// 大卡与小卡共用基类;小卡另加 .nav-landing-card--compact 覆写尺寸
|
| 174 |
.nav-landing-card {
|
| 175 |
display: flex;
|
| 176 |
flex-direction: column;
|
| 177 |
+
gap: $nav-card-gap;
|
| 178 |
+
padding: $nav-card-pad-block $nav-card-pad-inline;
|
| 179 |
+
border-radius: $nav-card-radius;
|
| 180 |
text-decoration: none;
|
| 181 |
color: inherit;
|
| 182 |
text-align: left;
|
| 183 |
background: var(--nav-landing-card-bg, #e6e6e6);
|
| 184 |
border: 1px solid var(--border-color, #ddd);
|
| 185 |
transition: background 0.15s ease, border-color 0.15s ease;
|
| 186 |
+
box-sizing: border-box;
|
| 187 |
|
| 188 |
&:hover {
|
| 189 |
background: var(--nav-landing-card-bg-hover, #dcdcdc);
|
|
|
|
| 194 |
border-color: var(--border-color, #333);
|
| 195 |
}
|
| 196 |
|
|
|
|
| 197 |
.nav-landing-card-text {
|
| 198 |
padding: 0;
|
| 199 |
}
|
| 200 |
|
| 201 |
.nav-landing-card-title {
|
| 202 |
display: block;
|
| 203 |
+
font-size: $nav-title-size;
|
| 204 |
font-weight: 600;
|
| 205 |
line-height: var(--nav-landing-line-height);
|
| 206 |
}
|
|
|
|
| 208 |
.nav-landing-card-subtitle {
|
| 209 |
display: block;
|
| 210 |
text-align: right;
|
| 211 |
+
font-size: $nav-subtitle-size;
|
| 212 |
font-weight: 400;
|
| 213 |
line-height: var(--nav-landing-line-height);
|
| 214 |
color: var(--text-muted, #666);
|
|
|
|
| 218 |
flex-shrink: 0;
|
| 219 |
width: 100%;
|
| 220 |
aspect-ratio: 4 / 3;
|
| 221 |
+
border-radius: $nav-card-shot-radius;
|
| 222 |
box-sizing: border-box;
|
| 223 |
background: var(--bg-hover, #f0f0f0);
|
| 224 |
background-size: cover;
|
|
|
|
| 225 |
background-position: left top;
|
| 226 |
background-repeat: no-repeat;
|
| 227 |
}
|
| 228 |
|
|
|
|
| 229 |
.nav-landing-card[data-nav-page='analysis'] .nav-landing-card-shot {
|
| 230 |
background-image: url('../content/images/highlight.png');
|
| 231 |
background-color: var(--bg-hover, #f0f0f0);
|
|
|
|
| 241 |
background-color: var(--bg-hover, #f0f0f0);
|
| 242 |
}
|
| 243 |
|
| 244 |
+
.nav-landing-card[data-nav-page='genAttribute'] video.nav-landing-card-shot {
|
| 245 |
+
object-fit: cover;
|
| 246 |
+
object-position: left top;
|
| 247 |
+
display: block;
|
| 248 |
background-color: var(--bg-hover, #f0f0f0);
|
| 249 |
}
|
| 250 |
|
| 251 |
+
html[data-theme='dark'] .nav-landing-card[data-nav-page='genAttribute'] video.nav-landing-card-shot {
|
| 252 |
+
background-color: var(--bg-hover, #2a2a2a);
|
|
|
|
| 253 |
}
|
| 254 |
|
| 255 |
html[data-theme='dark'] .nav-landing-card[data-nav-page='analysis'] .nav-landing-card-shot {
|
|
|
|
| 266 |
background-image: url('../content/images/attribute-dark.png');
|
| 267 |
background-color: var(--bg-hover, #2a2a2a);
|
| 268 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client/src/gen_attribute.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
| 16 |
<header class="app-page-toolbar app-page-toolbar--bleed">
|
| 17 |
<h1 class="page-toolbar-title"><span class="title-main-line"><span data-page-title data-i18n></span><span class="title-tagline" data-page-subtitle data-i18n></span></span></h1>
|
| 18 |
<div class="app-page-toolbar-actions">
|
| 19 |
-
<a href="index.html" class="home-link" title="
|
| 20 |
<div class="settings-menu-wrapper">
|
| 21 |
<button id="settings_btn" class="settings-btn" title="Settings" data-i18n="title">
|
| 22 |
<span class="settings-icon">⚙️</span>
|
|
|
|
| 16 |
<header class="app-page-toolbar app-page-toolbar--bleed">
|
| 17 |
<h1 class="page-toolbar-title"><span class="title-main-line"><span data-page-title data-i18n></span><span class="title-tagline" data-page-subtitle data-i18n></span></span></h1>
|
| 18 |
<div class="app-page-toolbar-actions">
|
| 19 |
+
<a href="index.html" class="home-link" title="Info Lens" data-i18n="text,title">Info Lens</a>
|
| 20 |
<div class="settings-menu-wrapper">
|
| 21 |
<button id="settings_btn" class="settings-btn" title="Settings" data-i18n="title">
|
| 22 |
<span class="settings-icon">⚙️</span>
|
client/src/index.html
CHANGED
|
@@ -28,10 +28,10 @@
|
|
| 28 |
<p class="nav-landing-subtitle" data-page-subtitle data-i18n></p>
|
| 29 |
</header>
|
| 30 |
<nav class="nav-landing-grid" aria-label="App pages">
|
| 31 |
-
<a class="nav-landing-card" href="gen_attribute.html" target="_blank" rel="noopener" data-nav-page="genAttribute" data-i18n="title"></a>
|
| 32 |
-
<a class="nav-landing-card" href="analysis.html" target="_blank" rel="noopener" data-nav-page="analysis" data-i18n="title"></a>
|
| 33 |
-
<a class="nav-landing-card" href="attribution.html" target="_blank" rel="noopener" data-nav-page="attribution" data-i18n="title"></a>
|
| 34 |
-
<a class="nav-landing-card" href="chat.html" target="_blank" rel="noopener" data-nav-page="chat" data-i18n="title"></a>
|
| 35 |
</nav>
|
| 36 |
<p class="nav-landing-hint" data-page-formula data-i18n></p>
|
| 37 |
</main>
|
|
|
|
| 28 |
<p class="nav-landing-subtitle" data-page-subtitle data-i18n></p>
|
| 29 |
</header>
|
| 30 |
<nav class="nav-landing-grid" aria-label="App pages">
|
| 31 |
+
<a class="nav-landing-card nav-landing-card--featured" href="gen_attribute.html" target="_blank" rel="noopener" data-nav-page="genAttribute" data-i18n="title"></a>
|
| 32 |
+
<a class="nav-landing-card nav-landing-card--compact" href="analysis.html" target="_blank" rel="noopener" data-nav-page="analysis" data-i18n="title"></a>
|
| 33 |
+
<a class="nav-landing-card nav-landing-card--compact" href="attribution.html" target="_blank" rel="noopener" data-nav-page="attribution" data-i18n="title"></a>
|
| 34 |
+
<a class="nav-landing-card nav-landing-card--compact" href="chat.html" target="_blank" rel="noopener" data-nav-page="chat" data-i18n="title"></a>
|
| 35 |
</nav>
|
| 36 |
<p class="nav-landing-hint" data-page-formula data-i18n></p>
|
| 37 |
</main>
|
client/src/scripts/genAttributeDemoManifestPlugin.js
CHANGED
|
@@ -1,38 +1,52 @@
|
|
| 1 |
/**
|
| 2 |
-
* 构建
|
| 3 |
-
*
|
| 4 |
*/
|
| 5 |
const path = require('path');
|
| 6 |
const fs = require('fs');
|
| 7 |
-
const webpack = require('webpack');
|
| 8 |
|
| 9 |
const REL_DIR = 'demos/gen_attribute';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
class GenAttributeDemoManifestPlugin {
|
| 12 |
apply(compiler) {
|
| 13 |
const srcDir = path.join(__dirname, '..', REL_DIR);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
compiler.hooks.thisCompilation.tap('GenAttributeDemoManifestPlugin', (compilation) => {
|
| 15 |
-
compilation.
|
| 16 |
-
{
|
| 17 |
-
name: 'GenAttributeDemoManifestPlugin',
|
| 18 |
-
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
|
| 19 |
-
},
|
| 20 |
-
() => {
|
| 21 |
-
let slugs = [];
|
| 22 |
-
if (fs.existsSync(srcDir)) {
|
| 23 |
-
const files = fs.readdirSync(srcDir).filter((f) => f.endsWith('.json'));
|
| 24 |
-
slugs = files
|
| 25 |
-
.map((f) => f.replace(/\.json$/i, ''))
|
| 26 |
-
.filter((s) => s.length > 0);
|
| 27 |
-
slugs.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
|
| 28 |
-
}
|
| 29 |
-
const json = JSON.stringify({ slugs });
|
| 30 |
-
compilation.emitAsset(
|
| 31 |
-
path.posix.join(REL_DIR, 'manifest.json'),
|
| 32 |
-
new webpack.sources.RawSource(json)
|
| 33 |
-
);
|
| 34 |
-
}
|
| 35 |
-
);
|
| 36 |
});
|
| 37 |
}
|
| 38 |
}
|
|
|
|
| 1 |
/**
|
| 2 |
+
* 构建前扫描 `demos/gen_attribute/*.json`,写入 `ts/demos/genAttributeBundledDemoManifest.generated.ts`,供 bundle 内联 slug 列表。
|
| 3 |
+
* 并为该目录注册 contextDependencies,便于 watch 下增减 demo JSON 时触发重编。
|
| 4 |
*/
|
| 5 |
const path = require('path');
|
| 6 |
const fs = require('fs');
|
|
|
|
| 7 |
|
| 8 |
const REL_DIR = 'demos/gen_attribute';
|
| 9 |
+
const GENERATED_BASENAME = 'genAttributeBundledDemoManifest.generated.ts';
|
| 10 |
+
|
| 11 |
+
function collectSlugs(srcDir) {
|
| 12 |
+
if (!fs.existsSync(srcDir)) return [];
|
| 13 |
+
const files = fs.readdirSync(srcDir).filter((f) => f.endsWith('.json'));
|
| 14 |
+
const slugs = files
|
| 15 |
+
.map((f) => f.replace(/\.json$/i, ''))
|
| 16 |
+
.filter((s) => s.length > 0);
|
| 17 |
+
// UTF-16 码元序,与 locale 无关,避免不同构建机生成顺序不一致
|
| 18 |
+
slugs.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
| 19 |
+
return slugs;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
function writeGeneratedModule(srcDir, outPath) {
|
| 23 |
+
const slugs = collectSlugs(srcDir);
|
| 24 |
+
const content =
|
| 25 |
+
'/**\n' +
|
| 26 |
+
' * Generated by GenAttributeDemoManifestPlugin — do not edit.\n' +
|
| 27 |
+
' */\n' +
|
| 28 |
+
`export const GEN_ATTRIBUTE_BUNDLED_DEMO_SLUGS: readonly string[] = ${JSON.stringify(slugs)};\n`;
|
| 29 |
+
if (fs.existsSync(outPath) && fs.readFileSync(outPath, 'utf8') === content) return;
|
| 30 |
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
| 31 |
+
fs.writeFileSync(outPath, content, 'utf8');
|
| 32 |
+
}
|
| 33 |
|
| 34 |
class GenAttributeDemoManifestPlugin {
|
| 35 |
apply(compiler) {
|
| 36 |
const srcDir = path.join(__dirname, '..', REL_DIR);
|
| 37 |
+
const outPath = path.join(__dirname, '..', 'ts', 'demos', GENERATED_BASENAME);
|
| 38 |
+
|
| 39 |
+
compiler.hooks.beforeCompile.tapAsync('GenAttributeDemoManifestPlugin', (_params, callback) => {
|
| 40 |
+
try {
|
| 41 |
+
writeGeneratedModule(srcDir, outPath);
|
| 42 |
+
callback();
|
| 43 |
+
} catch (e) {
|
| 44 |
+
callback(e);
|
| 45 |
+
}
|
| 46 |
+
});
|
| 47 |
+
|
| 48 |
compiler.hooks.thisCompilation.tap('GenAttributeDemoManifestPlugin', (compilation) => {
|
| 49 |
+
compilation.contextDependencies.add(srcDir);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
});
|
| 51 |
}
|
| 52 |
}
|
client/src/scripts/injectPageMetaIntoHtml.js
CHANGED
|
@@ -103,12 +103,16 @@ function injectPageMeta(html, pageKey, doc) {
|
|
| 103 |
} else {
|
| 104 |
openTag = openTag.replace(/>$/, ` title="${escapeHtmlText(navTitle)}">`);
|
| 105 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
const inner =
|
| 107 |
`<div class="nav-landing-card-text">` +
|
| 108 |
`<span class="nav-landing-card-title" data-i18n>${escapeHtmlText(navMeta.title)}</span>` +
|
| 109 |
`<span class="nav-landing-card-subtitle" data-i18n>${escapeHtmlText(navMeta.subtitle)}</span>` +
|
| 110 |
`</div>` +
|
| 111 |
-
|
| 112 |
html = html.replace(re, `${openTag}${inner}${m[3]}`);
|
| 113 |
}
|
| 114 |
}
|
|
|
|
| 103 |
} else {
|
| 104 |
openTag = openTag.replace(/>$/, ` title="${escapeHtmlText(navTitle)}">`);
|
| 105 |
}
|
| 106 |
+
const shot =
|
| 107 |
+
navKey === 'genAttribute'
|
| 108 |
+
? `<video class="nav-landing-card-shot" muted loop playsinline autoplay preload="metadata" aria-hidden="true"></video>`
|
| 109 |
+
: `<div class="nav-landing-card-shot" aria-hidden="true"></div>`;
|
| 110 |
const inner =
|
| 111 |
`<div class="nav-landing-card-text">` +
|
| 112 |
`<span class="nav-landing-card-title" data-i18n>${escapeHtmlText(navMeta.title)}</span>` +
|
| 113 |
`<span class="nav-landing-card-subtitle" data-i18n>${escapeHtmlText(navMeta.subtitle)}</span>` +
|
| 114 |
`</div>` +
|
| 115 |
+
shot;
|
| 116 |
html = html.replace(re, `${openTag}${inner}${m[3]}`);
|
| 117 |
}
|
| 118 |
}
|
client/src/ts/demos/genAttributeBundledDemoManifest.generated.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Generated by GenAttributeDemoManifestPlugin — do not edit.
|
| 3 |
+
*/
|
| 4 |
+
export const GEN_ATTRIBUTE_BUNDLED_DEMO_SLUGS: readonly string[] = ["Write a sonnet about love","写一首绝句,主题是春天","总结","翻译"];
|
client/src/ts/demos/genAttributeBundledDemos.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
| 1 |
/**
|
| 2 |
-
* Gen Attribute 打包 demo:JSON
|
| 3 |
-
* 列表来自同目录 `manifest.json`(构建时由 webpack 插件生成)。
|
| 4 |
*/
|
| 5 |
|
| 6 |
import type { GenAttrCachedRun } from '../storage/genAttributeRunCache';
|
| 7 |
import { isKnownPersistedCompletionReason } from '../utils/generationEndReasonLabel';
|
|
|
|
| 8 |
|
| 9 |
const BASE = 'demos/gen_attribute/';
|
| 10 |
|
|
@@ -43,39 +43,11 @@ function isValidGenAttrCachedRunPayload(v: unknown): v is GenAttrCachedRun {
|
|
| 43 |
const payloadCache = new Map<string, GenAttrCachedRun>();
|
| 44 |
const payloadInflight = new Map<string, Promise<GenAttrCachedRun | undefined>>();
|
| 45 |
|
| 46 |
-
type BundledDemoListEntry = { id: string; label: string };
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
/**
|
| 52 |
-
* manifest 构建期固定;本会话内只网络请求一次,并发首次调用会去重。
|
| 53 |
-
*/
|
| 54 |
-
export async function fetchBundledGenAttributeDemoList(): Promise<readonly BundledDemoListEntry[]> {
|
| 55 |
-
if (manifestListCache) return manifestListCache;
|
| 56 |
-
if (!manifestListInflight) {
|
| 57 |
-
manifestListInflight = fetch(new URL('manifest.json', baseUrl()))
|
| 58 |
-
.then((r) => {
|
| 59 |
-
if (!r.ok) throw new Error(`manifest: ${r.status}`);
|
| 60 |
-
return r.json() as Promise<unknown>;
|
| 61 |
-
})
|
| 62 |
-
.then((j) => {
|
| 63 |
-
const slugs =
|
| 64 |
-
j != null &&
|
| 65 |
-
typeof j === 'object' &&
|
| 66 |
-
'slugs' in j &&
|
| 67 |
-
Array.isArray((j as { slugs: unknown }).slugs)
|
| 68 |
-
? (j as { slugs: unknown[] }).slugs.filter((x): x is string => typeof x === 'string')
|
| 69 |
-
: [];
|
| 70 |
-
const list = slugs.map((slug) => ({ id: slug, label: slug }));
|
| 71 |
-
manifestListCache = list;
|
| 72 |
-
return list;
|
| 73 |
-
})
|
| 74 |
-
.finally(() => {
|
| 75 |
-
manifestListInflight = undefined;
|
| 76 |
-
});
|
| 77 |
-
}
|
| 78 |
-
return manifestListInflight;
|
| 79 |
}
|
| 80 |
|
| 81 |
/**
|
|
|
|
| 1 |
/**
|
| 2 |
+
* Gen Attribute 打包 demo:大 JSON 在 `dist/demos/gen_attribute/`,运行时 fetch;slug 列表构建期内联自 generated 模块。
|
|
|
|
| 3 |
*/
|
| 4 |
|
| 5 |
import type { GenAttrCachedRun } from '../storage/genAttributeRunCache';
|
| 6 |
import { isKnownPersistedCompletionReason } from '../utils/generationEndReasonLabel';
|
| 7 |
+
import { GEN_ATTRIBUTE_BUNDLED_DEMO_SLUGS } from './genAttributeBundledDemoManifest.generated';
|
| 8 |
|
| 9 |
const BASE = 'demos/gen_attribute/';
|
| 10 |
|
|
|
|
| 43 |
const payloadCache = new Map<string, GenAttrCachedRun>();
|
| 44 |
const payloadInflight = new Map<string, Promise<GenAttrCachedRun | undefined>>();
|
| 45 |
|
| 46 |
+
export type BundledDemoListEntry = { id: string; label: string };
|
| 47 |
|
| 48 |
+
/** 构建期固定的 bundled demo 列表(与当前 JS 同版本)。 */
|
| 49 |
+
export function getBundledGenAttributeDemoList(): readonly BundledDemoListEntry[] {
|
| 50 |
+
return GEN_ATTRIBUTE_BUNDLED_DEMO_SLUGS.map((slug) => ({ id: slug, label: slug }));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
}
|
| 52 |
|
| 53 |
/**
|
client/src/ts/gen_attribute.ts
CHANGED
|
@@ -46,7 +46,7 @@ import {
|
|
| 46 |
} from './utils/contentUrl';
|
| 47 |
import {
|
| 48 |
fetchBundledGenAttributeDemoBySlug,
|
| 49 |
-
|
| 50 |
isGenAttrRunPayloadValidForUi,
|
| 51 |
} from './demos/genAttributeBundledDemos';
|
| 52 |
import { extractErrorMessage } from './utils/errorUtils';
|
|
@@ -865,8 +865,8 @@ async function restoreGenAttrFromDemoSlug(slug: string): Promise<void> {
|
|
| 865 |
const genAttrCachedHistoryBtn = document.getElementById('gen_attr_cached_history_btn');
|
| 866 |
let genAttrBundledDemoEntries: Array<{ id: string; label: string }> = [];
|
| 867 |
|
| 868 |
-
|
| 869 |
-
genAttrBundledDemoEntries = [...(
|
| 870 |
}
|
| 871 |
|
| 872 |
const genCachedHistory = initCachedHistoryQueryDropdown({
|
|
@@ -898,9 +898,7 @@ initQueryHistoryDropdown({
|
|
| 898 |
applyHistoryOnHover: true,
|
| 899 |
});
|
| 900 |
|
| 901 |
-
|
| 902 |
-
console.warn('[gen_attribute] bundled demo manifest prefetch failed', e);
|
| 903 |
-
});
|
| 904 |
|
| 905 |
// --- 进度与指标 ---
|
| 906 |
function showProgress(current: number, total: number): void {
|
|
|
|
| 46 |
} from './utils/contentUrl';
|
| 47 |
import {
|
| 48 |
fetchBundledGenAttributeDemoBySlug,
|
| 49 |
+
getBundledGenAttributeDemoList,
|
| 50 |
isGenAttrRunPayloadValidForUi,
|
| 51 |
} from './demos/genAttributeBundledDemos';
|
| 52 |
import { extractErrorMessage } from './utils/errorUtils';
|
|
|
|
| 865 |
const genAttrCachedHistoryBtn = document.getElementById('gen_attr_cached_history_btn');
|
| 866 |
let genAttrBundledDemoEntries: Array<{ id: string; label: string }> = [];
|
| 867 |
|
| 868 |
+
function refreshGenAttrBundledDemoEntriesList(): void {
|
| 869 |
+
genAttrBundledDemoEntries = [...getBundledGenAttributeDemoList()];
|
| 870 |
}
|
| 871 |
|
| 872 |
const genCachedHistory = initCachedHistoryQueryDropdown({
|
|
|
|
| 898 |
applyHistoryOnHover: true,
|
| 899 |
});
|
| 900 |
|
| 901 |
+
refreshGenAttrBundledDemoEntriesList();
|
|
|
|
|
|
|
| 902 |
|
| 903 |
// --- 进度与指标 ---
|
| 904 |
function showProgress(current: number, total: number): void {
|
client/src/ts/home.ts
CHANGED
|
@@ -2,10 +2,12 @@
|
|
| 2 |
* 极简导航首页:主题与语言与 analysis 等页通过 localStorage 一致。
|
| 3 |
*/
|
| 4 |
import './utils/d3-polyfill';
|
|
|
|
|
|
|
| 5 |
import '../css/start.scss';
|
| 6 |
import '../css/home.scss';
|
| 7 |
|
| 8 |
-
import { initThemeManager } from './ui/theme';
|
| 9 |
import { initLanguageManager } from './ui/language';
|
| 10 |
import { getCurrentLanguage, initI18n, tr } from './lang/i18n-lite';
|
| 11 |
import { AdminManager } from './utils/adminManager';
|
|
@@ -28,6 +30,19 @@ function applyGenAttributeNavCardHref(): void {
|
|
| 28 |
a.setAttribute('href', `gen_attribute.html?demo=${encodeURIComponent(slug)}`);
|
| 29 |
}
|
| 30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
applyGenAttributeNavCardHref();
|
| 32 |
|
| 33 |
const apiPrefix = URLHandler.parameters['api'] || '';
|
|
@@ -35,7 +50,7 @@ const { api } = initializeCommonApp(apiPrefix);
|
|
| 35 |
const adminManager = AdminManager.getInstance();
|
| 36 |
api.setAdminToken(adminManager.isInAdminMode() ? adminManager.getAdminToken() : null);
|
| 37 |
|
| 38 |
-
const themeManager = initThemeManager({}, '#theme_dropdown');
|
| 39 |
const languageManager = initLanguageManager({}, '#language_dropdown');
|
| 40 |
|
| 41 |
void new SettingsMenuManager(
|
|
|
|
| 2 |
* 极简导航首页:主题与语言与 analysis 等页通过 localStorage 一致。
|
| 3 |
*/
|
| 4 |
import './utils/d3-polyfill';
|
| 5 |
+
import dagPreviewLight from '../content/images/dag.mov';
|
| 6 |
+
import dagPreviewDark from '../content/images/dag-dark.mov';
|
| 7 |
import '../css/start.scss';
|
| 8 |
import '../css/home.scss';
|
| 9 |
|
| 10 |
+
import { initThemeManager, type Theme } from './ui/theme';
|
| 11 |
import { initLanguageManager } from './ui/language';
|
| 12 |
import { getCurrentLanguage, initI18n, tr } from './lang/i18n-lite';
|
| 13 |
import { AdminManager } from './utils/adminManager';
|
|
|
|
| 30 |
a.setAttribute('href', `gen_attribute.html?demo=${encodeURIComponent(slug)}`);
|
| 31 |
}
|
| 32 |
|
| 33 |
+
const DAG_PREVIEW_BY_THEME: Record<Theme, string> = {
|
| 34 |
+
light: dagPreviewLight,
|
| 35 |
+
dark: dagPreviewDark,
|
| 36 |
+
};
|
| 37 |
+
|
| 38 |
+
function syncGenAttributeCardPreviewVideo(theme: Theme): void {
|
| 39 |
+
const v = document.querySelector<HTMLVideoElement>(
|
| 40 |
+
'a.nav-landing-card[data-nav-page="genAttribute"] video.nav-landing-card-shot'
|
| 41 |
+
);
|
| 42 |
+
if (!v) return;
|
| 43 |
+
v.src = DAG_PREVIEW_BY_THEME[theme];
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
applyGenAttributeNavCardHref();
|
| 47 |
|
| 48 |
const apiPrefix = URLHandler.parameters['api'] || '';
|
|
|
|
| 50 |
const adminManager = AdminManager.getInstance();
|
| 51 |
api.setAdminToken(adminManager.isInAdminMode() ? adminManager.getAdminToken() : null);
|
| 52 |
|
| 53 |
+
const themeManager = initThemeManager({ onThemeChange: syncGenAttributeCardPreviewVideo }, '#theme_dropdown');
|
| 54 |
const languageManager = initLanguageManager({}, '#language_dropdown');
|
| 55 |
|
| 56 |
void new SettingsMenuManager(
|
client/src/ts/lang/translations.ts
CHANGED
|
@@ -215,7 +215,6 @@ export const translations: Translations = {
|
|
| 215 |
'This action cannot be undone.': '此操作不可撤销。',
|
| 216 |
|
| 217 |
// ========== Demo 对比页面 ==========
|
| 218 |
-
'InfoLens Home': '返回首页',
|
| 219 |
'Import Result': '导入结果',
|
| 220 |
'Diff Mode': '差分模式',
|
| 221 |
'Move left': '左移',
|
|
|
|
| 215 |
'This action cannot be undone.': '此操作不可撤销。',
|
| 216 |
|
| 217 |
// ========== Demo 对比页面 ==========
|
|
|
|
| 218 |
'Import Result': '导入结果',
|
| 219 |
'Diff Mode': '差分模式',
|
| 220 |
'Move left': '左移',
|
client/src/ts/types/html.d.ts
CHANGED
|
@@ -6,3 +6,8 @@ declare module '*.html' {
|
|
| 6 |
const content: string;
|
| 7 |
export default content;
|
| 8 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
const content: string;
|
| 7 |
export default content;
|
| 8 |
}
|
| 9 |
+
|
| 10 |
+
declare module '*.mov' {
|
| 11 |
+
const url: string;
|
| 12 |
+
export default url;
|
| 13 |
+
}
|
client/src/webpack.config.js
CHANGED
|
@@ -73,6 +73,10 @@ module.exports = {
|
|
| 73 |
}
|
| 74 |
}
|
| 75 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
{
|
| 77 |
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
| 78 |
type: 'asset',
|
|
|
|
| 73 |
}
|
| 74 |
}
|
| 75 |
},
|
| 76 |
+
{
|
| 77 |
+
test: /\.mov$/,
|
| 78 |
+
type: 'asset/resource'
|
| 79 |
+
},
|
| 80 |
{
|
| 81 |
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
| 82 |
type: 'asset',
|