chore(demo): add loader and minimal-mode preview pages
This commit is contained in:
parent
dfc763c7c5
commit
249eb663bb
708
static/demo/loaders-preview.html
Normal file
708
static/demo/loaders-preview.html
Normal file
@ -0,0 +1,708 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>加载动画预览</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #1a1a1a;
|
||||
color: #e0e0e0;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.loader-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 40px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.loader-item {
|
||||
background: #2a2a2a;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.loader-title {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loader-wrapper {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 统一的颜色变量 */
|
||||
:root {
|
||||
--loader-color: #888;
|
||||
}
|
||||
|
||||
/* ========== 0. 方格网格动画 (已有) ========== */
|
||||
@keyframes loader_5191 {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.loader-0 {
|
||||
position: relative;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.loader-0-square {
|
||||
background: var(--loader-color);
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -1.5px;
|
||||
margin-left: -1.5px;
|
||||
border-radius: 0.5px;
|
||||
}
|
||||
|
||||
.loader-0 #sq1 {
|
||||
margin-top: -7.5px;
|
||||
margin-left: -7.5px;
|
||||
animation: loader_5191 675ms ease-in-out 0s infinite alternate;
|
||||
}
|
||||
|
||||
.loader-0 #sq2 {
|
||||
margin-top: -7.5px;
|
||||
animation: loader_5191 675ms ease-in-out 75ms infinite alternate;
|
||||
}
|
||||
|
||||
.loader-0 #sq3 {
|
||||
margin-top: -7.5px;
|
||||
margin-left: 4.5px;
|
||||
animation: loader_5191 675ms ease-in-out 150ms infinite;
|
||||
}
|
||||
|
||||
.loader-0 #sq4 {
|
||||
margin-left: -7.5px;
|
||||
animation: loader_5191 675ms ease-in-out 225ms infinite;
|
||||
}
|
||||
|
||||
.loader-0 #sq5 {
|
||||
animation: loader_5191 675ms ease-in-out 300ms infinite;
|
||||
}
|
||||
|
||||
.loader-0 #sq6 {
|
||||
margin-left: 4.5px;
|
||||
animation: loader_5191 675ms ease-in-out 375ms infinite;
|
||||
}
|
||||
|
||||
.loader-0 #sq7 {
|
||||
margin-top: 4.5px;
|
||||
margin-left: -7.5px;
|
||||
animation: loader_5191 675ms ease-in-out 450ms infinite;
|
||||
}
|
||||
|
||||
.loader-0 #sq8 {
|
||||
margin-top: 4.5px;
|
||||
animation: loader_5191 675ms ease-in-out 525ms infinite;
|
||||
}
|
||||
|
||||
.loader-0 #sq9 {
|
||||
margin-top: 4.5px;
|
||||
margin-left: 4.5px;
|
||||
animation: loader_5191 675ms ease-in-out 600ms infinite;
|
||||
}
|
||||
|
||||
/* ========== 1. 方块旋转动画 ========== */
|
||||
@keyframes square-animation {
|
||||
0% { left: 0; top: 0; }
|
||||
10.5% { left: 0; top: 0; }
|
||||
12.5% { left: 16px; top: 0; }
|
||||
23% { left: 16px; top: 0; }
|
||||
25% { left: 32px; top: 0; }
|
||||
35.5% { left: 32px; top: 0; }
|
||||
37.5% { left: 32px; top: 16px; }
|
||||
48% { left: 32px; top: 16px; }
|
||||
50% { left: 16px; top: 16px; }
|
||||
60.5% { left: 16px; top: 16px; }
|
||||
62.5% { left: 16px; top: 32px; }
|
||||
73% { left: 16px; top: 32px; }
|
||||
75% { left: 0; top: 32px; }
|
||||
85.5% { left: 0; top: 32px; }
|
||||
87.5% { left: 0; top: 16px; }
|
||||
98% { left: 0; top: 16px; }
|
||||
100% { left: 0; top: 0; }
|
||||
}
|
||||
|
||||
.loader-1 {
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.loader-1-square {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin: 1px;
|
||||
background: var(--loader-color);
|
||||
animation: square-animation 10s ease-in-out infinite both;
|
||||
}
|
||||
|
||||
.loader-1-square:nth-of-type(1) { animation-delay: -1.4285714286s; }
|
||||
.loader-1-square:nth-of-type(2) { animation-delay: -2.8571428571s; }
|
||||
.loader-1-square:nth-of-type(3) { animation-delay: -4.2857142857s; }
|
||||
.loader-1-square:nth-of-type(4) { animation-delay: -5.7142857143s; }
|
||||
.loader-1-square:nth-of-type(5) { animation-delay: -7.1428571429s; }
|
||||
.loader-1-square:nth-of-type(6) { animation-delay: -8.5714285714s; }
|
||||
.loader-1-square:nth-of-type(7) { animation-delay: -10s; }
|
||||
|
||||
/* ========== 2. 双方块追逐 ========== */
|
||||
.loader-2 {
|
||||
--size: 15px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loader-2::after,
|
||||
.loader-2::before {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
top: 50%;
|
||||
animation: loader-2-up 2.4s cubic-bezier(0, 0, 0.24, 1.21) infinite;
|
||||
left: 50%;
|
||||
background: var(--loader-color);
|
||||
}
|
||||
|
||||
.loader-2::after {
|
||||
top: calc(50% - var(--size));
|
||||
left: calc(50% - var(--size));
|
||||
animation: loader-2-down 2.4s cubic-bezier(0, 0, 0.24, 1.21) infinite;
|
||||
}
|
||||
|
||||
@keyframes loader-2-down {
|
||||
0%, 100% { transform: none; }
|
||||
25% { transform: translateX(100%); }
|
||||
50% { transform: translateX(100%) translateY(100%); }
|
||||
75% { transform: translateY(100%); }
|
||||
}
|
||||
|
||||
@keyframes loader-2-up {
|
||||
0%, 100% { transform: none; }
|
||||
25% { transform: translateX(-100%); }
|
||||
50% { transform: translateX(-100%) translateY(-100%); }
|
||||
75% { transform: translateY(-100%); }
|
||||
}
|
||||
|
||||
/* ========== 3. 弹跳方块 ========== */
|
||||
.loader-3 {
|
||||
width: 60px;
|
||||
height: 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loader-3-square {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
background-color: var(--loader-color);
|
||||
left: 15%;
|
||||
transform-origin: 50%;
|
||||
animation: loader-3-bounce .5s alternate infinite ease;
|
||||
}
|
||||
|
||||
@keyframes loader-3-bounce {
|
||||
0% {
|
||||
top: 30px;
|
||||
height: 5px;
|
||||
transform: scaleX(1.7);
|
||||
}
|
||||
40% {
|
||||
height: 10px;
|
||||
transform: scaleX(1);
|
||||
}
|
||||
100% {
|
||||
top: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
.loader-3-square:nth-child(2) {
|
||||
left: 45%;
|
||||
animation-delay: .2s;
|
||||
}
|
||||
|
||||
.loader-3-square:nth-child(3) {
|
||||
left: auto;
|
||||
right: 15%;
|
||||
animation-delay: .3s;
|
||||
}
|
||||
|
||||
/* ========== 4. 五方块序列 ========== */
|
||||
.loader-4 {
|
||||
--square: 13px;
|
||||
--offset: 15px;
|
||||
--duration: 2.4s;
|
||||
--delay: 0.2s;
|
||||
width: calc(3 * var(--offset) + var(--square));
|
||||
height: calc(2 * var(--offset) + var(--square));
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loader-4 div {
|
||||
display: inline-block;
|
||||
background: var(--loader-color);
|
||||
border: none;
|
||||
width: var(--square);
|
||||
height: var(--square);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.loader-4 #sq1 {
|
||||
left: calc(0 * var(--offset));
|
||||
top: calc(0 * var(--offset));
|
||||
animation: loader-4-square1 var(--duration) var(--delay) ease-in-out infinite;
|
||||
}
|
||||
|
||||
.loader-4 #sq2 {
|
||||
left: calc(0 * var(--offset));
|
||||
top: calc(1 * var(--offset));
|
||||
animation: loader-4-square2 var(--duration) var(--delay) ease-in-out infinite;
|
||||
}
|
||||
|
||||
.loader-4 #sq3 {
|
||||
left: calc(1 * var(--offset));
|
||||
top: calc(1 * var(--offset));
|
||||
animation: loader-4-square3 var(--duration) var(--delay) ease-in-out infinite;
|
||||
}
|
||||
|
||||
.loader-4 #sq4 {
|
||||
left: calc(2 * var(--offset));
|
||||
top: calc(1 * var(--offset));
|
||||
animation: loader-4-square4 var(--duration) var(--delay) ease-in-out infinite;
|
||||
}
|
||||
|
||||
.loader-4 #sq5 {
|
||||
left: calc(3 * var(--offset));
|
||||
top: calc(1 * var(--offset));
|
||||
animation: loader-4-square5 var(--duration) var(--delay) ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes loader-4-square1 {
|
||||
0% { left: calc(0 * var(--offset)); top: calc(0 * var(--offset)); }
|
||||
8.333% { left: calc(0 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
100% { left: calc(0 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
}
|
||||
|
||||
@keyframes loader-4-square2 {
|
||||
0% { left: calc(0 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
8.333% { left: calc(0 * var(--offset)); top: calc(2 * var(--offset)); }
|
||||
16.67% { left: calc(1 * var(--offset)); top: calc(2 * var(--offset)); }
|
||||
25.00% { left: calc(1 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
83.33% { left: calc(1 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
91.67% { left: calc(1 * var(--offset)); top: calc(0 * var(--offset)); }
|
||||
100% { left: calc(0 * var(--offset)); top: calc(0 * var(--offset)); }
|
||||
}
|
||||
|
||||
@keyframes loader-4-square3 {
|
||||
0%, 100% { left: calc(1 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
16.67% { left: calc(1 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
25.00% { left: calc(1 * var(--offset)); top: calc(0 * var(--offset)); }
|
||||
33.33% { left: calc(2 * var(--offset)); top: calc(0 * var(--offset)); }
|
||||
41.67% { left: calc(2 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
66.67% { left: calc(2 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
75.00% { left: calc(2 * var(--offset)); top: calc(2 * var(--offset)); }
|
||||
83.33% { left: calc(1 * var(--offset)); top: calc(2 * var(--offset)); }
|
||||
91.67% { left: calc(1 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
}
|
||||
|
||||
@keyframes loader-4-square4 {
|
||||
0% { left: calc(2 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
33.33% { left: calc(2 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
41.67% { left: calc(2 * var(--offset)); top: calc(2 * var(--offset)); }
|
||||
50.00% { left: calc(3 * var(--offset)); top: calc(2 * var(--offset)); }
|
||||
58.33% { left: calc(3 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
100% { left: calc(3 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
}
|
||||
|
||||
@keyframes loader-4-square5 {
|
||||
0% { left: calc(3 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
50.00% { left: calc(3 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
58.33% { left: calc(3 * var(--offset)); top: calc(0 * var(--offset)); }
|
||||
66.67% { left: calc(2 * var(--offset)); top: calc(0 * var(--offset)); }
|
||||
75.00% { left: calc(2 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
100% { left: calc(2 * var(--offset)); top: calc(1 * var(--offset)); }
|
||||
}
|
||||
|
||||
/* ========== 5. 九宫格动画 ========== */
|
||||
.loader-5 {
|
||||
position: relative;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.loader-5-box {
|
||||
float: left;
|
||||
position: relative;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.loader-5-box:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--loader-color);
|
||||
}
|
||||
|
||||
.loader-5-box:nth-child(3n) {
|
||||
margin-right: 0;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.loader-5-box:nth-child(1):before,
|
||||
.loader-5-box:nth-child(4):before {
|
||||
margin-left: 13px;
|
||||
}
|
||||
|
||||
.loader-5-box:nth-child(3):before {
|
||||
margin-top: 26px;
|
||||
}
|
||||
|
||||
.loader-5-box:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.loader-5-box:nth-child(1) { animation: loader-5-moveBox-1 4s infinite; }
|
||||
.loader-5-box:nth-child(2) { animation: loader-5-moveBox-2 4s infinite; }
|
||||
.loader-5-box:nth-child(3) { animation: loader-5-moveBox-3 4s infinite; }
|
||||
.loader-5-box:nth-child(4) { animation: loader-5-moveBox-4 4s infinite; }
|
||||
.loader-5-box:nth-child(5) { animation: loader-5-moveBox-5 4s infinite; }
|
||||
.loader-5-box:nth-child(6) { animation: loader-5-moveBox-6 4s infinite; }
|
||||
.loader-5-box:nth-child(7) { animation: loader-5-moveBox-7 4s infinite; }
|
||||
.loader-5-box:nth-child(8) { animation: loader-5-moveBox-8 4s infinite; }
|
||||
.loader-5-box:nth-child(9) { animation: loader-5-moveBox-9 4s infinite; }
|
||||
|
||||
@keyframes loader-5-moveBox-1 {
|
||||
9.09% { transform: translate(-13px, 0); }
|
||||
18.18% { transform: translate(0px, 0); }
|
||||
27.27% { transform: translate(0px, 0); }
|
||||
36.36% { transform: translate(13px, 0); }
|
||||
45.45% { transform: translate(13px, 13px); }
|
||||
54.55% { transform: translate(13px, 13px); }
|
||||
63.64% { transform: translate(13px, 13px); }
|
||||
72.73% { transform: translate(13px, 0px); }
|
||||
81.82% { transform: translate(0px, 0px); }
|
||||
90.91% { transform: translate(-13px, 0px); }
|
||||
100% { transform: translate(0px, 0px); }
|
||||
}
|
||||
|
||||
@keyframes loader-5-moveBox-2 {
|
||||
9.09% { transform: translate(0, 0); }
|
||||
18.18% { transform: translate(13px, 0); }
|
||||
27.27% { transform: translate(0px, 0); }
|
||||
36.36% { transform: translate(13px, 0); }
|
||||
45.45% { transform: translate(13px, 13px); }
|
||||
54.55% { transform: translate(13px, 13px); }
|
||||
63.64% { transform: translate(13px, 13px); }
|
||||
72.73% { transform: translate(13px, 13px); }
|
||||
81.82% { transform: translate(0px, 13px); }
|
||||
90.91% { transform: translate(0px, 13px); }
|
||||
100% { transform: translate(0px, 0px); }
|
||||
}
|
||||
|
||||
@keyframes loader-5-moveBox-3 {
|
||||
9.09% { transform: translate(-13px, 0); }
|
||||
18.18% { transform: translate(-13px, 0); }
|
||||
27.27% { transform: translate(0px, 0); }
|
||||
36.36% { transform: translate(-13px, 0); }
|
||||
45.45% { transform: translate(-13px, 0); }
|
||||
54.55% { transform: translate(-13px, 0); }
|
||||
63.64% { transform: translate(-13px, 0); }
|
||||
72.73% { transform: translate(-13px, 0); }
|
||||
81.82% { transform: translate(-13px, -13px); }
|
||||
90.91% { transform: translate(0px, -13px); }
|
||||
100% { transform: translate(0px, 0px); }
|
||||
}
|
||||
|
||||
@keyframes loader-5-moveBox-4 {
|
||||
9.09% { transform: translate(-13px, 0); }
|
||||
18.18% { transform: translate(-13px, 0); }
|
||||
27.27% { transform: translate(-13px, -13px); }
|
||||
36.36% { transform: translate(0px, -13px); }
|
||||
45.45% { transform: translate(0px, 0px); }
|
||||
54.55% { transform: translate(0px, -13px); }
|
||||
63.64% { transform: translate(0px, -13px); }
|
||||
72.73% { transform: translate(0px, -13px); }
|
||||
81.82% { transform: translate(-13px, -13px); }
|
||||
90.91% { transform: translate(-13px, 0px); }
|
||||
100% { transform: translate(0px, 0px); }
|
||||
}
|
||||
|
||||
@keyframes loader-5-moveBox-5 {
|
||||
9.09% { transform: translate(0, 0); }
|
||||
18.18% { transform: translate(0, 0); }
|
||||
27.27% { transform: translate(0, 0); }
|
||||
36.36% { transform: translate(13px, 0); }
|
||||
45.45% { transform: translate(13px, 0); }
|
||||
54.55% { transform: translate(13px, 0); }
|
||||
63.64% { transform: translate(13px, 0); }
|
||||
72.73% { transform: translate(13px, 0); }
|
||||
81.82% { transform: translate(13px, -13px); }
|
||||
90.91% { transform: translate(0px, -13px); }
|
||||
100% { transform: translate(0px, 0px); }
|
||||
}
|
||||
|
||||
@keyframes loader-5-moveBox-6 {
|
||||
9.09% { transform: translate(0, 0); }
|
||||
18.18% { transform: translate(-13px, 0); }
|
||||
27.27% { transform: translate(-13px, 0); }
|
||||
36.36% { transform: translate(0px, 0); }
|
||||
45.45% { transform: translate(0px, 0); }
|
||||
54.55% { transform: translate(0px, 0); }
|
||||
63.64% { transform: translate(0px, 0); }
|
||||
72.73% { transform: translate(0px, 13px); }
|
||||
81.82% { transform: translate(-13px, 13px); }
|
||||
90.91% { transform: translate(-13px, 0px); }
|
||||
100% { transform: translate(0px, 0px); }
|
||||
}
|
||||
|
||||
@keyframes loader-5-moveBox-7 {
|
||||
9.09% { transform: translate(13px, 0); }
|
||||
18.18% { transform: translate(13px, 0); }
|
||||
27.27% { transform: translate(13px, 0); }
|
||||
36.36% { transform: translate(0px, 0); }
|
||||
45.45% { transform: translate(0px, -13px); }
|
||||
54.55% { transform: translate(13px, -13px); }
|
||||
63.64% { transform: translate(0px, -13px); }
|
||||
72.73% { transform: translate(0px, -13px); }
|
||||
81.82% { transform: translate(0px, 0px); }
|
||||
90.91% { transform: translate(13px, 0px); }
|
||||
100% { transform: translate(0px, 0px); }
|
||||
}
|
||||
|
||||
@keyframes loader-5-moveBox-8 {
|
||||
9.09% { transform: translate(0, 0); }
|
||||
18.18% { transform: translate(-13px, 0); }
|
||||
27.27% { transform: translate(-13px, -13px); }
|
||||
36.36% { transform: translate(0px, -13px); }
|
||||
45.45% { transform: translate(0px, -13px); }
|
||||
54.55% { transform: translate(0px, -13px); }
|
||||
63.64% { transform: translate(0px, -13px); }
|
||||
72.73% { transform: translate(0px, -13px); }
|
||||
81.82% { transform: translate(13px, -13px); }
|
||||
90.91% { transform: translate(13px, 0px); }
|
||||
100% { transform: translate(0px, 0px); }
|
||||
}
|
||||
|
||||
@keyframes loader-5-moveBox-9 {
|
||||
9.09% { transform: translate(-13px, 0); }
|
||||
18.18% { transform: translate(-13px, 0); }
|
||||
27.27% { transform: translate(0px, 0); }
|
||||
36.36% { transform: translate(-13px, 0); }
|
||||
45.45% { transform: translate(0px, 0); }
|
||||
54.55% { transform: translate(0px, 0); }
|
||||
63.64% { transform: translate(-13px, 0); }
|
||||
72.73% { transform: translate(-13px, 0); }
|
||||
81.82% { transform: translate(-26px, 0); }
|
||||
90.91% { transform: translate(-13px, 0); }
|
||||
100% { transform: translate(0px, 0); }
|
||||
}
|
||||
|
||||
/* ========== 6. 波纹方块 ========== */
|
||||
.loader-6 {
|
||||
--cell-size: 10px;
|
||||
--cell-spacing: 1px;
|
||||
--cells: 3;
|
||||
--total-size: calc(var(--cells) * (var(--cell-size) + 2 * var(--cell-spacing)));
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: var(--total-size);
|
||||
height: var(--total-size);
|
||||
}
|
||||
|
||||
.loader-6-cell {
|
||||
flex: 0 0 var(--cell-size);
|
||||
margin: var(--cell-spacing);
|
||||
background-color: transparent;
|
||||
box-sizing: border-box;
|
||||
animation: 1.5s loader-6-ripple ease infinite;
|
||||
}
|
||||
|
||||
.loader-6-cell.d-1 { animation-delay: 100ms; }
|
||||
.loader-6-cell.d-2 { animation-delay: 200ms; }
|
||||
.loader-6-cell.d-3 { animation-delay: 300ms; }
|
||||
.loader-6-cell.d-4 { animation-delay: 400ms; }
|
||||
|
||||
.loader-6-cell:nth-child(1),
|
||||
.loader-6-cell:nth-child(2),
|
||||
.loader-6-cell:nth-child(3),
|
||||
.loader-6-cell:nth-child(4),
|
||||
.loader-6-cell:nth-child(5),
|
||||
.loader-6-cell:nth-child(6),
|
||||
.loader-6-cell:nth-child(7),
|
||||
.loader-6-cell:nth-child(8),
|
||||
.loader-6-cell:nth-child(9) {
|
||||
--cell-color: var(--loader-color);
|
||||
}
|
||||
|
||||
@keyframes loader-6-ripple {
|
||||
0% { background-color: transparent; }
|
||||
30% { background-color: var(--cell-color); }
|
||||
60% { background-color: transparent; }
|
||||
100% { background-color: transparent; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>加载动画预览 - 统一风格</h1>
|
||||
|
||||
<div class="loader-grid">
|
||||
<!-- 0. 方格网格 (已有) -->
|
||||
<div class="loader-item">
|
||||
<div class="loader-wrapper">
|
||||
<div class="loader-0">
|
||||
<div class="loader-0-square" id="sq1"></div>
|
||||
<div class="loader-0-square" id="sq2"></div>
|
||||
<div class="loader-0-square" id="sq3"></div>
|
||||
<div class="loader-0-square" id="sq4"></div>
|
||||
<div class="loader-0-square" id="sq5"></div>
|
||||
<div class="loader-0-square" id="sq6"></div>
|
||||
<div class="loader-0-square" id="sq7"></div>
|
||||
<div class="loader-0-square" id="sq8"></div>
|
||||
<div class="loader-0-square" id="sq9"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loader-title">0. 方格网格 (已有)</div>
|
||||
</div>
|
||||
|
||||
<!-- 1. 方块旋转 -->
|
||||
<div class="loader-item">
|
||||
<div class="loader-wrapper">
|
||||
<div class="loader-1">
|
||||
<div class="loader-1-square"></div>
|
||||
<div class="loader-1-square"></div>
|
||||
<div class="loader-1-square"></div>
|
||||
<div class="loader-1-square"></div>
|
||||
<div class="loader-1-square"></div>
|
||||
<div class="loader-1-square"></div>
|
||||
<div class="loader-1-square"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loader-title">1. 方块旋转</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. 双方块追逐 -->
|
||||
<div class="loader-item">
|
||||
<div class="loader-wrapper">
|
||||
<div class="loader-2"></div>
|
||||
</div>
|
||||
<div class="loader-title">2. 双方块追逐</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. 弹跳方块 -->
|
||||
<div class="loader-item">
|
||||
<div class="loader-wrapper">
|
||||
<div class="loader-3">
|
||||
<div class="loader-3-square"></div>
|
||||
<div class="loader-3-square"></div>
|
||||
<div class="loader-3-square"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loader-title">3. 弹跳方块</div>
|
||||
</div>
|
||||
|
||||
<!-- 4. 五方块序列 -->
|
||||
<div class="loader-item">
|
||||
<div class="loader-wrapper">
|
||||
<div class="loader-4">
|
||||
<div id="sq1"></div>
|
||||
<div id="sq2"></div>
|
||||
<div id="sq3"></div>
|
||||
<div id="sq4"></div>
|
||||
<div id="sq5"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loader-title">4. 五方块序列</div>
|
||||
</div>
|
||||
|
||||
<!-- 5. 九宫格动画 -->
|
||||
<div class="loader-item">
|
||||
<div class="loader-wrapper">
|
||||
<div class="loader-5">
|
||||
<div class="loader-5-box"></div>
|
||||
<div class="loader-5-box"></div>
|
||||
<div class="loader-5-box"></div>
|
||||
<div class="loader-5-box"></div>
|
||||
<div class="loader-5-box"></div>
|
||||
<div class="loader-5-box"></div>
|
||||
<div class="loader-5-box"></div>
|
||||
<div class="loader-5-box"></div>
|
||||
<div class="loader-5-box"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loader-title">5. 九宫格动画</div>
|
||||
</div>
|
||||
|
||||
<!-- 6. 波纹方块 -->
|
||||
<div class="loader-item">
|
||||
<div class="loader-wrapper">
|
||||
<div class="loader-6">
|
||||
<div class="loader-6-cell d-0"></div>
|
||||
<div class="loader-6-cell d-1"></div>
|
||||
<div class="loader-6-cell d-2"></div>
|
||||
<div class="loader-6-cell d-1"></div>
|
||||
<div class="loader-6-cell d-2"></div>
|
||||
<div class="loader-6-cell d-2"></div>
|
||||
<div class="loader-6-cell d-3"></div>
|
||||
<div class="loader-6-cell d-3"></div>
|
||||
<div class="loader-6-cell d-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loader-title">6. 波纹方块</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
123
static/demo/loaders-v2.html
Normal file
123
static/demo/loaders-v2.html
Normal file
@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>加载动画预览 V2</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #1a1a1a; color: #e0e0e0; padding: 40px; }
|
||||
.container { max-width: 1400px; margin: 0 auto; }
|
||||
h1 { text-align: center; margin-bottom: 40px; color: #fff; }
|
||||
.loader-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 30px; }
|
||||
.loader-item { background: #2a2a2a; border-radius: 12px; padding: 30px; display: flex; flex-direction: column; align-items: center; gap: 20px; }
|
||||
.loader-title { font-size: 13px; color: #999; text-align: center; }
|
||||
.loader-wrapper { width: 100%; height: 60px; display: flex; align-items: center; justify-content: center; position: relative; }
|
||||
:root { --loader-color: #888; }
|
||||
|
||||
/* 0. 已有 */
|
||||
@keyframes loader_5191 { from { opacity: 0; } to { opacity: 1; } }
|
||||
.loader-0 { position: relative; width: 16px; height: 16px; }
|
||||
.loader-0-square { background: var(--loader-color); width: 3px; height: 3px; position: absolute; top: 50%; left: 50%; margin-top: -1.5px; margin-left: -1.5px; border-radius: 0.5px; }
|
||||
.loader-0 #sq1 { margin-top: -7.5px; margin-left: -7.5px; animation: loader_5191 675ms ease-in-out 0s infinite alternate; }
|
||||
.loader-0 #sq2 { margin-top: -7.5px; animation: loader_5191 675ms ease-in-out 75ms infinite alternate; }
|
||||
.loader-0 #sq3 { margin-top: -7.5px; margin-left: 4.5px; animation: loader_5191 675ms ease-in-out 150ms infinite; }
|
||||
.loader-0 #sq4 { margin-left: -7.5px; animation: loader_5191 675ms ease-in-out 225ms infinite; }
|
||||
.loader-0 #sq5 { animation: loader_5191 675ms ease-in-out 300ms infinite; }
|
||||
.loader-0 #sq6 { margin-left: 4.5px; animation: loader_5191 675ms ease-in-out 375ms infinite; }
|
||||
.loader-0 #sq7 { margin-top: 4.5px; margin-left: -7.5px; animation: loader_5191 675ms ease-in-out 450ms infinite; }
|
||||
.loader-0 #sq8 { margin-top: 4.5px; animation: loader_5191 675ms ease-in-out 525ms infinite; }
|
||||
.loader-0 #sq9 { margin-top: 4.5px; margin-left: 4.5px; animation: loader_5191 675ms ease-in-out 600ms infinite; }
|
||||
|
||||
/* 1. 方块旋转 */
|
||||
@keyframes sq-anim-1 { 0% { left: 0; top: 0; } 10.5% { left: 0; top: 0; } 12.5% { left: 8px; top: 0; } 23% { left: 8px; top: 0; } 25% { left: 16px; top: 0; } 35.5% { left: 16px; top: 0; } 37.5% { left: 16px; top: 8px; } 48% { left: 16px; top: 8px; } 50% { left: 8px; top: 8px; } 60.5% { left: 8px; top: 8px; } 62.5% { left: 8px; top: 16px; } 73% { left: 8px; top: 16px; } 75% { left: 0; top: 16px; } 85.5% { left: 0; top: 16px; } 87.5% { left: 0; top: 8px; } 98% { left: 0; top: 8px; } 100% { left: 0; top: 0; } }
|
||||
.loader-1 { position: relative; width: 24px; height: 24px; transform: rotate(45deg); }
|
||||
.loader-1-square { position: absolute; top: 0; left: 0; width: 3px; height: 3px; margin: 0.5px; background: var(--loader-color); animation: sq-anim-1 10s ease-in-out infinite both; }
|
||||
.loader-1-square:nth-of-type(1) { animation-delay: -1.43s; }
|
||||
.loader-1-square:nth-of-type(2) { animation-delay: -2.86s; }
|
||||
.loader-1-square:nth-of-type(3) { animation-delay: -4.29s; }
|
||||
.loader-1-square:nth-of-type(4) { animation-delay: -5.71s; }
|
||||
.loader-1-square:nth-of-type(5) { animation-delay: -7.14s; }
|
||||
.loader-1-square:nth-of-type(6) { animation-delay: -8.57s; }
|
||||
.loader-1-square:nth-of-type(7) { animation-delay: -10s; }
|
||||
|
||||
/* 2. 双方块 */
|
||||
.loader-2 { --size: 6px; width: 16px; height: 16px; position: relative; }
|
||||
.loader-2::after, .loader-2::before { box-sizing: border-box; position: absolute; content: ""; width: var(--size); height: var(--size); top: 50%; animation: l2-up 2.4s cubic-bezier(0, 0, 0.24, 1.21) infinite; left: 50%; background: var(--loader-color); }
|
||||
.loader-2::after { top: calc(50% - var(--size)); left: calc(50% - var(--size)); animation: l2-down 2.4s cubic-bezier(0, 0, 0.24, 1.21) infinite; }
|
||||
@keyframes l2-down { 0%, 100% { transform: none; } 25% { transform: translateX(100%); } 50% { transform: translateX(100%) translateY(100%); } 75% { transform: translateY(100%); } }
|
||||
@keyframes l2-up { 0%, 100% { transform: none; } 25% { transform: translateX(-100%); } 50% { transform: translateX(-100%) translateY(-100%); } 75% { transform: translateY(-100%); } }
|
||||
|
||||
/* 3. 弹跳 */
|
||||
.loader-3 { width: 40px; height: 20px; position: relative; }
|
||||
.loader-3-square { width: 3px; height: 3px; position: absolute; background-color: var(--loader-color); left: 15%; transform-origin: 50%; animation: l3-bounce .5s alternate infinite ease; }
|
||||
@keyframes l3-bounce { 0% { top: 20px; height: 2px; transform: scaleX(1.7); } 40% { height: 3px; transform: scaleX(1); } 100% { top: 0%; } }
|
||||
.loader-3-square:nth-child(2) { left: 45%; animation-delay: .2s; }
|
||||
.loader-3-square:nth-child(3) { left: auto; right: 15%; animation-delay: .3s; }
|
||||
|
||||
/* 4. 五方块 */
|
||||
.loader-4 { --sq: 3px; --off: 7.5px; width: calc(3 * var(--off) + var(--sq)); height: calc(2 * var(--off) + var(--sq)); position: relative; }
|
||||
.loader-4 div { display: inline-block; background: var(--loader-color); width: var(--sq); height: var(--sq); position: absolute; }
|
||||
.loader-4 #s1 { left: 0; top: 0; animation: l4-s1 2.4s 0.2s ease-in-out infinite; }
|
||||
.loader-4 #s2 { left: 0; top: var(--off); animation: l4-s2 2.4s 0.2s ease-in-out infinite; }
|
||||
.loader-4 #s3 { left: var(--off); top: var(--off); animation: l4-s3 2.4s 0.2s ease-in-out infinite; }
|
||||
.loader-4 #s4 { left: calc(2 * var(--off)); top: var(--off); animation: l4-s4 2.4s 0.2s ease-in-out infinite; }
|
||||
.loader-4 #s5 { left: calc(3 * var(--off)); top: var(--off); animation: l4-s5 2.4s 0.2s ease-in-out infinite; }
|
||||
@keyframes l4-s1 { 0% { left: 0; top: 0; } 8.33% { left: 0; top: var(--off); } 100% { left: 0; top: var(--off); } }
|
||||
@keyframes l4-s2 { 0% { left: 0; top: var(--off); } 8.33% { left: 0; top: calc(2 * var(--off)); } 16.67% { left: var(--off); top: calc(2 * var(--off)); } 25% { left: var(--off); top: var(--off); } 83.33% { left: var(--off); top: var(--off); } 91.67% { left: var(--off); top: 0; } 100% { left: 0; top: 0; } }
|
||||
@keyframes l4-s3 { 0%, 100% { left: var(--off); top: var(--off); } 16.67% { left: var(--off); top: var(--off); } 25% { left: var(--off); top: 0; } 33.33% { left: calc(2 * var(--off)); top: 0; } 41.67% { left: calc(2 * var(--off)); top: var(--off); } 66.67% { left: calc(2 * var(--off)); top: var(--off); } 75% { left: calc(2 * var(--off)); top: calc(2 * var(--off)); } 83.33% { left: var(--off); top: calc(2 * var(--off)); } 91.67% { left: var(--off); top: var(--off); } }
|
||||
@keyframes l4-s4 { 0% { left: calc(2 * var(--off)); top: var(--off); } 33.33% { left: calc(2 * var(--off)); top: var(--off); } 41.67% { left: calc(2 * var(--off)); top: calc(2 * var(--off)); } 50% { left: calc(3 * var(--off)); top: calc(2 * var(--off)); } 58.33% { left: calc(3 * var(--off)); top: var(--off); } 100% { left: calc(3 * var(--off)); top: var(--off); } }
|
||||
@keyframes l4-s5 { 0% { left: calc(3 * var(--off)); top: var(--off); } 50% { left: calc(3 * var(--off)); top: var(--off); } 58.33% { left: calc(3 * var(--off)); top: 0; } 66.67% { left: calc(2 * var(--off)); top: 0; } 75% { left: calc(2 * var(--off)); top: var(--off); } 100% { left: calc(2 * var(--off)); top: var(--off); } }
|
||||
|
||||
/* 5. 九宫格 */
|
||||
.loader-5 { position: relative; width: 20px; height: 20px; }
|
||||
.loader-5-box { float: left; position: relative; width: 3px; height: 3px; margin-right: 4px; }
|
||||
.loader-5-box:before { content: ""; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: var(--loader-color); }
|
||||
.loader-5-box:nth-child(3n) { margin-right: 0; margin-bottom: 4px; }
|
||||
.loader-5-box:nth-child(1):before, .loader-5-box:nth-child(4):before { margin-left: 7px; }
|
||||
.loader-5-box:nth-child(3):before { margin-top: 14px; }
|
||||
.loader-5-box:last-child { margin-bottom: 0; }
|
||||
.loader-5-box:nth-child(1) { animation: l5-m1 4s infinite; }
|
||||
.loader-5-box:nth-child(2) { animation: l5-m2 4s infinite; }
|
||||
.loader-5-box:nth-child(3) { animation: l5-m3 4s infinite; }
|
||||
.loader-5-box:nth-child(4) { animation: l5-m4 4s infinite; }
|
||||
.loader-5-box:nth-child(5) { animation: l5-m5 4s infinite; }
|
||||
.loader-5-box:nth-child(6) { animation: l5-m6 4s infinite; }
|
||||
.loader-5-box:nth-child(7) { animation: l5-m7 4s infinite; }
|
||||
.loader-5-box:nth-child(8) { animation: l5-m8 4s infinite; }
|
||||
.loader-5-box:nth-child(9) { animation: l5-m9 4s infinite; }
|
||||
@keyframes l5-m1 { 9% { transform: translate(-7px, 0); } 18% { transform: translate(0, 0); } 27% { transform: translate(0, 0); } 36% { transform: translate(7px, 0); } 45% { transform: translate(7px, 7px); } 54% { transform: translate(7px, 7px); } 63% { transform: translate(7px, 7px); } 72% { transform: translate(7px, 0); } 81% { transform: translate(0, 0); } 90% { transform: translate(-7px, 0); } 100% { transform: translate(0, 0); } }
|
||||
@keyframes l5-m2 { 9% { transform: translate(0, 0); } 18% { transform: translate(7px, 0); } 27% { transform: translate(0, 0); } 36% { transform: translate(7px, 0); } 45% { transform: translate(7px, 7px); } 54% { transform: translate(7px, 7px); } 63% { transform: translate(7px, 7px); } 72% { transform: translate(7px, 7px); } 81% { transform: translate(0, 7px); } 90% { transform: translate(0, 7px); } 100% { transform: translate(0, 0); } }
|
||||
@keyframes l5-m3 { 9% { transform: translate(-7px, 0); } 18% { transform: translate(-7px, 0); } 27% { transform: translate(0, 0); } 36% { transform: translate(-7px, 0); } 45% { transform: translate(-7px, 0); } 54% { transform: translate(-7px, 0); } 63% { transform: translate(-7px, 0); } 72% { transform: translate(-7px, 0); } 81% { transform: translate(-7px, -7px); } 90% { transform: translate(0, -7px); } 100% { transform: translate(0, 0); } }
|
||||
@keyframes l5-m4 { 9% { transform: translate(-7px, 0); } 18% { transform: translate(-7px, 0); } 27% { transform: translate(-7px, -7px); } 36% { transform: translate(0, -7px); } 45% { transform: translate(0, 0); } 54% { transform: translate(0, -7px); } 63% { transform: translate(0, -7px); } 72% { transform: translate(0, -7px); } 81% { transform: translate(-7px, -7px); } 90% { transform: translate(-7px, 0); } 100% { transform: translate(0, 0); } }
|
||||
@keyframes l5-m5 { 9% { transform: translate(0, 0); } 18% { transform: translate(0, 0); } 27% { transform: translate(0, 0); } 36% { transform: translate(7px, 0); } 45% { transform: translate(7px, 0); } 54% { transform: translate(7px, 0); } 63% { transform: translate(7px, 0); } 72% { transform: translate(7px, 0); } 81% { transform: translate(7px, -7px); } 90% { transform: translate(0, -7px); } 100% { transform: translate(0, 0); } }
|
||||
@keyframes l5-m6 { 9% { transform: translate(0, 0); } 18% { transform: translate(-7px, 0); } 27% { transform: translate(-7px, 0); } 36% { transform: translate(0, 0); } 45% { transform: translate(0, 0); } 54% { transform: translate(0, 0); } 63% { transform: translate(0, 0); } 72% { transform: translate(0, 7px); } 81% { transform: translate(-7px, 7px); } 90% { transform: translate(-7px, 0); } 100% { transform: translate(0, 0); } }
|
||||
@keyframes l5-m7 { 9% { transform: translate(7px, 0); } 18% { transform: translate(7px, 0); } 27% { transform: translate(7px, 0); } 36% { transform: translate(0, 0); } 45% { transform: translate(0, -7px); } 54% { transform: translate(7px, -7px); } 63% { transform: translate(0, -7px); } 72% { transform: translate(0, -7px); } 81% { transform: translate(0, 0); } 90% { transform: translate(7px, 0); } 100% { transform: translate(0, 0); } }
|
||||
@keyframes l5-m8 { 9% { transform: translate(0, 0); } 18% { transform: translate(-7px, 0); } 27% { transform: translate(-7px, -7px); } 36% { transform: translate(0, -7px); } 45% { transform: translate(0, -7px); } 54% { transform: translate(0, -7px); } 63% { transform: translate(0, -7px); } 72% { transform: translate(0, -7px); } 81% { transform: translate(7px, -7px); } 90% { transform: translate(7px, 0); } 100% { transform: translate(0, 0); } }
|
||||
@keyframes l5-m9 { 9% { transform: translate(-7px, 0); } 18% { transform: translate(-7px, 0); } 27% { transform: translate(0, 0); } 36% { transform: translate(-7px, 0); } 45% { transform: translate(0, 0); } 54% { transform: translate(0, 0); } 63% { transform: translate(-7px, 0); } 72% { transform: translate(-7px, 0); } 81% { transform: translate(-14px, 0); } 90% { transform: translate(-7px, 0); } 100% { transform: translate(0, 0); } }
|
||||
|
||||
/* 6. 波纹 */
|
||||
.loader-6 { --cell-size: 3px; --cell-spacing: 2px; --cells: 3; --total-size: calc(var(--cells) * (var(--cell-size) + 2 * var(--cell-spacing))); display: flex; flex-wrap: wrap; width: var(--total-size); height: var(--total-size); }
|
||||
.loader-6-cell { flex: 0 0 var(--cell-size); margin: var(--cell-spacing); background-color: transparent; box-sizing: border-box; animation: 1.5s l6-ripple ease infinite; }
|
||||
.loader-6-cell.d-1 { animation-delay: 100ms; }
|
||||
.loader-6-cell.d-2 { animation-delay: 200ms; }
|
||||
.loader-6-cell.d-3 { animation-delay: 300ms; }
|
||||
.loader-6-cell.d-4 { animation-delay: 400ms; }
|
||||
.loader-6-cell:nth-child(1), .loader-6-cell:nth-child(2), .loader-6-cell:nth-child(3), .loader-6-cell:nth-child(4), .loader-6-cell:nth-child(5), .loader-6-cell:nth-child(6), .loader-6-cell:nth-child(7), .loader-6-cell:nth-child(8), .loader-6-cell:nth-child(9) { --cell-color: var(--loader-color); }
|
||||
@keyframes l6-ripple { 0% { background-color: transparent; } 30% { background-color: var(--cell-color); } 60% { background-color: transparent; } 100% { background-color: transparent; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>加载动画预览 V2 - 统一风格</h1>
|
||||
<div class="loader-grid">
|
||||
<div class="loader-item"><div class="loader-wrapper"><div class="loader-0"><div class="loader-0-square" id="sq1"></div><div class="loader-0-square" id="sq2"></div><div class="loader-0-square" id="sq3"></div><div class="loader-0-square" id="sq4"></div><div class="loader-0-square" id="sq5"></div><div class="loader-0-square" id="sq6"></div><div class="loader-0-square" id="sq7"></div><div class="loader-0-square" id="sq8"></div><div class="loader-0-square" id="sq9"></div></div></div><div class="loader-title">0. 方格网格 (已有)</div></div>
|
||||
<div class="loader-item"><div class="loader-wrapper"><div class="loader-1"><div class="loader-1-square"></div><div class="loader-1-square"></div><div class="loader-1-square"></div><div class="loader-1-square"></div><div class="loader-1-square"></div><div class="loader-1-square"></div><div class="loader-1-square"></div></div></div><div class="loader-title">1. 方块旋转</div></div>
|
||||
<div class="loader-item"><div class="loader-wrapper"><div class="loader-2"></div></div><div class="loader-title">2. 双方块追逐</div></div>
|
||||
<div class="loader-item"><div class="loader-wrapper"><div class="loader-3"><div class="loader-3-square"></div><div class="loader-3-square"></div><div class="loader-3-square"></div></div></div><div class="loader-title">3. 弹跳方块</div></div>
|
||||
<div class="loader-item"><div class="loader-wrapper"><div class="loader-4"><div id="s1"></div><div id="s2"></div><div id="s3"></div><div id="s4"></div><div id="s5"></div></div></div><div class="loader-title">4. 五方块序列</div></div>
|
||||
<div class="loader-item"><div class="loader-wrapper"><div class="loader-5"><div class="loader-5-box"></div><div class="loader-5-box"></div><div class="loader-5-box"></div><div class="loader-5-box"></div><div class="loader-5-box"></div><div class="loader-5-box"></div><div class="loader-5-box"></div><div class="loader-5-box"></div><div class="loader-5-box"></div></div></div><div class="loader-title">5. 九宫格动画</div></div>
|
||||
<div class="loader-item"><div class="loader-wrapper"><div class="loader-6"><div class="loader-6-cell d-0"></div><div class="loader-6-cell d-1"></div><div class="loader-6-cell d-2"></div><div class="loader-6-cell d-1"></div><div class="loader-6-cell d-2"></div><div class="loader-6-cell d-2"></div><div class="loader-6-cell d-3"></div><div class="loader-6-cell d-3"></div><div class="loader-6-cell d-4"></div></div></div><div class="loader-title">6. 波纹方块</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
195
static/demo/minimal-mode-v2/index.html
Normal file
195
static/demo/minimal-mode-v2/index.html
Normal file
@ -0,0 +1,195 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>极简模式 V2 - 无缝展开</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="chat-area">
|
||||
<!-- 用户消息 -->
|
||||
<div class="message-block user-message">
|
||||
<div class="message-header">
|
||||
<svg class="icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
<span>用户</span>
|
||||
</div>
|
||||
<div class="message-text">
|
||||
帮我搜索一下最新的AI技术发展趋势
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI 消息 -->
|
||||
<div class="message-block assistant-message">
|
||||
<div class="message-header">
|
||||
<svg class="icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||||
</svg>
|
||||
<span>AI Assistant</span>
|
||||
</div>
|
||||
|
||||
<!-- 等待动画 -->
|
||||
<div class="generating-placeholder" id="generatingPlaceholder">
|
||||
<span class="letter">思</span>
|
||||
<span class="letter">考</span>
|
||||
<span class="letter">中</span>
|
||||
<span class="letter">.</span>
|
||||
<span class="letter">.</span>
|
||||
<span class="letter">.</span>
|
||||
</div>
|
||||
|
||||
<!-- 摘要/第一步内容(可展开的) -->
|
||||
<div class="summary-wrapper" id="summaryWrapper" style="display: none;">
|
||||
<div class="summary-timeline">
|
||||
<svg class="summary-icon" id="summaryIcon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"></path>
|
||||
<path d="M12 6v6l4 2"></path>
|
||||
</svg>
|
||||
<div class="summary-line" id="summaryLine"></div>
|
||||
</div>
|
||||
<div class="summary-content" id="summaryContent">
|
||||
<div class="summary-text collapsed" id="summaryText">
|
||||
我需要搜索最新的AI技术发展趋势。首先,我应该使用搜索工具来查找相关信息...
|
||||
</div>
|
||||
<div class="summary-text-full" id="summaryTextFull" style="display: none;">
|
||||
我需要搜索最新的AI技术发展趋势。首先,我应该使用搜索工具来查找相关信息。搜索关键词应该包括"AI技术"、"人工智能"、"2026"、"发展趋势"等。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 后续步骤 -->
|
||||
<div class="steps-container" id="stepsContainer" style="display: none;">
|
||||
<!-- 步骤1: 搜索网页 -->
|
||||
<div class="step-item" data-step="1" style="display: none;">
|
||||
<div class="step-timeline">
|
||||
<svg class="step-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<path d="m21 21-4.35-4.35"></path>
|
||||
</svg>
|
||||
<div class="step-line"></div>
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<div class="step-header">搜索网页</div>
|
||||
<div class="step-body">
|
||||
<div class="tool-intent">搜索:AI技术发展趋势 2026</div>
|
||||
<div class="tool-result">
|
||||
<div class="result-item">
|
||||
<div class="result-title">2026年AI技术十大趋势</div>
|
||||
<div class="result-url">https://example.com/ai-trends-2026</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<div class="result-title">人工智能最新突破</div>
|
||||
<div class="result-url">https://example.com/ai-breakthrough</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 步骤2: 思考 -->
|
||||
<div class="step-item" data-step="2" style="display: none;">
|
||||
<div class="step-timeline">
|
||||
<svg class="step-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"></path>
|
||||
<path d="M12 6v6l4 2"></path>
|
||||
</svg>
|
||||
<div class="step-line"></div>
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<div class="step-body">
|
||||
<div class="thinking-content" id="thinkingContent2">
|
||||
搜索结果显示了一些有价值的信息。现在我需要执行一个命令来获取更详细的数据分析。我将使用curl命令来获取第一个链接的内容,然后进行分析。这将帮助我提供更准确和详细的答案。让我执行这个命令...
|
||||
<button class="show-more-btn" onclick="expandThinking(2)">显示更多</button>
|
||||
</div>
|
||||
<div class="thinking-content-full" id="thinkingContentFull2" style="display: none;">
|
||||
搜索结果显示了一些有价值的信息。现在我需要执行一个命令来获取更详细的数据分析。我将使用curl命令来获取第一个链接的内容,然后进行分析。这将帮助我提供更准确和详细的答案。让我执行这个命令...
|
||||
|
||||
在执行之前,我需要确保命令的安全性和有效性。curl命令是一个常用的网络请求工具,可以帮助我获取网页内容。我会添加适当的参数来确保请求的稳定性,比如设置超时时间、用户代理等。
|
||||
|
||||
同时,我还需要考虑如何解析返回的HTML内容,提取出关键信息。可能需要使用一些文本处理工具,如grep、sed或awk来过滤和格式化数据。
|
||||
<button class="show-less-btn" onclick="collapseThinking(2)">收起</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 步骤3: 执行命令 -->
|
||||
<div class="step-item" data-step="3" style="display: none;">
|
||||
<div class="step-timeline">
|
||||
<svg class="step-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="4 17 10 11 4 5"></polyline>
|
||||
<line x1="12" y1="19" x2="20" y2="19"></line>
|
||||
</svg>
|
||||
<div class="step-line"></div>
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<div class="step-header">执行命令</div>
|
||||
<div class="step-body">
|
||||
<div class="tool-intent">获取网页内容并分析</div>
|
||||
<div class="tool-result">
|
||||
<div class="command-block">
|
||||
<div class="command-header">
|
||||
<span class="command-label">bash</span>
|
||||
</div>
|
||||
<pre class="command-code">curl -s "https://example.com/ai-trends-2026" | grep -i "trend"</pre>
|
||||
<div class="command-output">
|
||||
<div class="output-line">Trend 1: 多模态AI模型的普及</div>
|
||||
<div class="output-line">Trend 2: AI安全性和可解释性</div>
|
||||
<div class="output-line">Trend 3: 边缘AI计算</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 步骤4: 思考 -->
|
||||
<div class="step-item" data-step="4" style="display: none;">
|
||||
<div class="step-timeline">
|
||||
<svg class="step-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"></path>
|
||||
<path d="M12 6v6l4 2"></path>
|
||||
</svg>
|
||||
<div class="step-line"></div>
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<div class="step-body">
|
||||
<div class="thinking-content">
|
||||
很好,我已经收集到了足够的信息。现在我可以整理这些内容,为用户提供一个清晰、全面的答案。我会按照趋势的重要性和影响力来组织内容。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最终输出 -->
|
||||
<div class="text-output" id="textOutput" style="display: none;">
|
||||
<div class="text-content">
|
||||
<p>根据我的搜索和分析,2026年AI技术的主要发展趋势包括:</p>
|
||||
<ol>
|
||||
<li><strong>多模态AI模型的普及</strong>:能够同时处理文本、图像、音频等多种数据类型的AI模型将成为主流。</li>
|
||||
<li><strong>AI安全性和可解释性</strong>:随着AI应用的广泛部署,确保AI系统的安全性和决策的可解释性变得越来越重要。</li>
|
||||
<li><strong>边缘AI计算</strong>:将AI计算能力部署到边缘设备,实现更快的响应速度和更好的隐私保护。</li>
|
||||
</ol>
|
||||
<p>这些趋势将深刻影响各行各业的数字化转型进程。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 控制面板 -->
|
||||
<div class="control-panel">
|
||||
<button onclick="startDemo()" class="control-btn">开始演示</button>
|
||||
<button onclick="resetDemo()" class="control-btn">重置</button>
|
||||
<button onclick="toggleExpand()" class="control-btn">切换展开/折叠</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
413
static/demo/minimal-mode-v2/script.js
Normal file
413
static/demo/minimal-mode-v2/script.js
Normal file
@ -0,0 +1,413 @@
|
||||
// 全局状态
|
||||
let currentStep = 0;
|
||||
let isExpanded = false;
|
||||
let demoRunning = false;
|
||||
|
||||
// 步骤配置
|
||||
const steps = [
|
||||
{
|
||||
type: 'thinking',
|
||||
icon: 'brain',
|
||||
summary: '我需要搜索最新的AI技术发展趋势。首先,我应该使用搜索工具来查找相关信息...',
|
||||
fullText: '我需要搜索最新的AI技术发展趋势。首先,我应该使用搜索工具来查找相关信息。搜索关键词应该包括"AI技术"、"人工智能"、"2026"、"发展趋势"等。',
|
||||
duration: 2000
|
||||
},
|
||||
{
|
||||
type: 'tool',
|
||||
icon: 'search',
|
||||
summary: '搜索:AI技术发展趋势 2026',
|
||||
duration: 2500
|
||||
},
|
||||
{
|
||||
type: 'thinking',
|
||||
icon: 'brain',
|
||||
summary: '搜索结果显示了一些有价值的信息。现在我需要执行一个命令来获取更详细的数据分析...',
|
||||
fullText: '搜索结果显示了一些有价值的信息。现在我需要执行一个命令来获取更详细的数据分析。我将使用curl命令来获取第一个链接的内容,然后进行分析。这将帮助我提供更准确和详细的答案。让我执行这个命令...\n\n在执行之前,我需要确保命令的安全性和有效性。curl命令是一个常用的网络请求工具,可以帮助我获取网页内容。我会添加适当的参数来确保请求的稳定性,比如设置超时时间、用户代理等。\n\n同时,我还需要考虑如何解析返回的HTML内容,提取出关键信息。可能需要使用一些文本处理工具,如grep、sed或awk来过滤和格式化数据。',
|
||||
duration: 2000
|
||||
},
|
||||
{
|
||||
type: 'tool',
|
||||
icon: 'terminal',
|
||||
summary: '执行命令:获取网页内容并分析',
|
||||
duration: 2500
|
||||
},
|
||||
{
|
||||
type: 'thinking',
|
||||
icon: 'brain',
|
||||
summary: '很好,我已经收集到了足够的信息。现在我可以整理这些内容,为用户提供一个清晰、全面的答案...',
|
||||
fullText: '很好,我已经收集到了足够的信息。现在我可以整理这些内容,为用户提供一个清晰、全面的答案。我会按照趋势的重要性和影响力来组织内容。',
|
||||
duration: 2000
|
||||
}
|
||||
];
|
||||
|
||||
// 最终输出文本
|
||||
const finalOutputText = `根据我的搜索和分析,2026年AI技术的主要发展趋势包括:
|
||||
|
||||
1. **多模态AI模型的普及**:能够同时处理文本、图像、音频等多种数据类型的AI模型将成为主流。
|
||||
|
||||
2. **AI安全性和可解释性**:随着AI应用的广泛部署,确保AI系统的安全性和决策的可解释性变得越来越重要。
|
||||
|
||||
3. **边缘AI计算**:将AI计算能力部署到边缘设备,实现更快的响应速度和更好的隐私保护。
|
||||
|
||||
这些趋势将深刻影响各行各业的数字化转型进程。`;
|
||||
|
||||
// 开始演示
|
||||
async function startDemo() {
|
||||
if (demoRunning) return;
|
||||
|
||||
demoRunning = true;
|
||||
resetDemo();
|
||||
|
||||
// 显示等待动画
|
||||
const placeholder = document.getElementById('generatingPlaceholder');
|
||||
placeholder.style.display = 'flex';
|
||||
|
||||
await sleep(1500);
|
||||
|
||||
// 隐藏等待动画,显示摘要包装器
|
||||
placeholder.style.display = 'none';
|
||||
const summaryWrapper = document.getElementById('summaryWrapper');
|
||||
summaryWrapper.style.display = 'flex';
|
||||
summaryWrapper.classList.add('running');
|
||||
|
||||
// 执行每个步骤
|
||||
for (let i = 0; i < steps.length; i++) {
|
||||
currentStep = i;
|
||||
await executeStep(i);
|
||||
}
|
||||
|
||||
// 完成后移除运行状态
|
||||
await sleep(500);
|
||||
summaryWrapper.classList.remove('running');
|
||||
|
||||
// 如果展开状态,显示最后一步
|
||||
if (isExpanded) {
|
||||
await showDetailStep(currentStep, true);
|
||||
} else {
|
||||
// 折叠状态下,摘要显示第一步内容
|
||||
updateSummary(steps[0]);
|
||||
}
|
||||
|
||||
await sleep(800);
|
||||
|
||||
// 显示最终输出(流式)
|
||||
const textOutput = document.getElementById('textOutput');
|
||||
textOutput.style.display = 'block';
|
||||
setTimeout(() => {
|
||||
textOutput.classList.add('show');
|
||||
}, 50);
|
||||
|
||||
// 流式输出最终文本
|
||||
await streamText(document.querySelector('.text-content'), finalOutputText, 20);
|
||||
|
||||
demoRunning = false;
|
||||
}
|
||||
|
||||
// 执行单个步骤
|
||||
async function executeStep(stepIndex) {
|
||||
const step = steps[stepIndex];
|
||||
|
||||
// 更新摘要(仅在折叠状态下)
|
||||
if (!isExpanded) {
|
||||
// 流式输出摘要文本
|
||||
await streamSummary(step);
|
||||
}
|
||||
|
||||
// 如果展开状态,显示详细内容
|
||||
if (isExpanded) {
|
||||
await showDetailStep(stepIndex, true);
|
||||
|
||||
// 如果是思考类型,流式输出完整内容
|
||||
if (step.type === 'thinking' && step.fullText) {
|
||||
const stepBody = document.querySelector(`.step-item[data-step="${stepIndex + 1}"] .step-body`);
|
||||
if (stepBody) {
|
||||
stepBody.textContent = '';
|
||||
await streamText(stepBody, step.fullText, 30);
|
||||
// 更新竖线高度
|
||||
updateLineHeight(stepIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 显示下一个步骤的竖线(如果有下一个步骤)
|
||||
if (stepIndex < steps.length - 1) {
|
||||
if (isExpanded) {
|
||||
// 展开状态:显示当前步骤的竖线
|
||||
const currentStepItem = document.querySelector(`.step-item[data-step="${stepIndex + 1}"]`);
|
||||
if (currentStepItem) {
|
||||
const line = currentStepItem.querySelector('.step-line');
|
||||
if (line) {
|
||||
updateLineHeight(stepIndex + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 折叠状态:显示摘要的竖线(暂时不需要,因为只在展开时才有步骤)
|
||||
}
|
||||
}
|
||||
|
||||
await sleep(step.duration);
|
||||
}
|
||||
|
||||
// 流式输出摘要文本
|
||||
async function streamSummary(step) {
|
||||
const summaryIcon = document.getElementById('summaryIcon');
|
||||
const summaryText = document.getElementById('summaryText');
|
||||
|
||||
// 更新图标
|
||||
summaryIcon.innerHTML = getIconSVG(step.icon);
|
||||
|
||||
// 流式输出文本
|
||||
summaryText.textContent = '';
|
||||
await streamText(summaryText, step.summary, 30);
|
||||
}
|
||||
|
||||
// 流式输出文本
|
||||
async function streamText(element, text, speed = 30) {
|
||||
element.textContent = '';
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
element.textContent += text[i];
|
||||
await sleep(speed);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新摘要
|
||||
function updateSummary(step) {
|
||||
const summaryIcon = document.getElementById('summaryIcon');
|
||||
const summaryText = document.getElementById('summaryText');
|
||||
|
||||
// 更新图标
|
||||
summaryIcon.innerHTML = getIconSVG(step.icon);
|
||||
|
||||
// 更新文本
|
||||
summaryText.textContent = step.summary;
|
||||
}
|
||||
|
||||
// 显示详细步骤
|
||||
async function showDetailStep(stepIndex, isNewStep = false) {
|
||||
const stepItem = document.querySelector(`.step-item[data-step="${stepIndex + 1}"]`);
|
||||
if (!stepItem) return;
|
||||
|
||||
// 添加动画类
|
||||
if (isNewStep) {
|
||||
stepItem.classList.add('animated');
|
||||
} else {
|
||||
stepItem.classList.add('instant');
|
||||
}
|
||||
|
||||
// 显示步骤
|
||||
stepItem.style.display = 'flex';
|
||||
|
||||
// 等待动画
|
||||
await sleep(isNewStep ? 100 : 10);
|
||||
|
||||
// 如果有下一个步骤,显示当前步骤的竖线
|
||||
if (stepIndex < steps.length - 1) {
|
||||
updateLineHeight(stepIndex + 1);
|
||||
}
|
||||
|
||||
await sleep(isNewStep ? 250 : 10);
|
||||
}
|
||||
|
||||
// 更新竖线高度
|
||||
function updateLineHeight(stepIndex) {
|
||||
const stepItem = document.querySelector(`.step-item[data-step="${stepIndex}"]`);
|
||||
if (!stepItem) return;
|
||||
|
||||
const line = stepItem.querySelector('.step-line');
|
||||
const stepContent = stepItem.querySelector('.step-content');
|
||||
if (!line || !stepContent) return;
|
||||
|
||||
// 获取内容的实际高度
|
||||
const contentHeight = stepContent.offsetHeight;
|
||||
|
||||
// 如果是最后一个步骤,不需要竖线
|
||||
if (stepIndex >= steps.length) {
|
||||
line.style.height = '0';
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算竖线高度:内容高度 + 间距
|
||||
const gap = 16;
|
||||
const lineHeight = Math.max(contentHeight + gap, 40);
|
||||
line.style.height = `${lineHeight}px`;
|
||||
}
|
||||
|
||||
// 切换展开/折叠
|
||||
async function toggleExpand() {
|
||||
const summaryWrapper = document.getElementById('summaryWrapper');
|
||||
if (summaryWrapper.style.display === 'none') return;
|
||||
|
||||
isExpanded = !isExpanded;
|
||||
|
||||
if (isExpanded) {
|
||||
// 展开:显示完整文本,隐藏摘要文本
|
||||
summaryWrapper.classList.add('expanded');
|
||||
|
||||
const summaryText = document.getElementById('summaryText');
|
||||
const summaryTextFull = document.getElementById('summaryTextFull');
|
||||
const summaryLine = document.getElementById('summaryLine');
|
||||
const stepsContainer = document.getElementById('stepsContainer');
|
||||
|
||||
// 切换文本显示
|
||||
summaryText.style.display = 'none';
|
||||
summaryTextFull.style.display = 'block';
|
||||
|
||||
// 显示连接线
|
||||
await sleep(50);
|
||||
summaryLine.style.height = '20px';
|
||||
|
||||
// 显示步骤容器
|
||||
stepsContainer.style.display = 'block';
|
||||
await sleep(50);
|
||||
stepsContainer.classList.add('show');
|
||||
|
||||
// 快速显示已完成的步骤
|
||||
for (let i = 0; i <= currentStep; i++) {
|
||||
const isCurrentStep = i === currentStep && demoRunning;
|
||||
await showDetailStep(i, isCurrentStep);
|
||||
if (!isCurrentStep) {
|
||||
await sleep(30); // 已有内容快速出现
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 折叠:显示摘要文本,隐藏完整文本
|
||||
summaryWrapper.classList.remove('expanded');
|
||||
|
||||
const summaryText = document.getElementById('summaryText');
|
||||
const summaryTextFull = document.getElementById('summaryTextFull');
|
||||
const summaryLine = document.getElementById('summaryLine');
|
||||
const stepsContainer = document.getElementById('stepsContainer');
|
||||
|
||||
// 隐藏步骤容器
|
||||
stepsContainer.classList.remove('show');
|
||||
await sleep(250);
|
||||
stepsContainer.style.display = 'none';
|
||||
|
||||
// 隐藏连接线
|
||||
summaryLine.style.height = '0';
|
||||
|
||||
// 切换文本显示
|
||||
summaryText.style.display = 'block';
|
||||
summaryTextFull.style.display = 'none';
|
||||
|
||||
// 更新摘要为第一步内容(不是当前步骤)
|
||||
updateSummary(steps[0]);
|
||||
|
||||
// 重置所有步骤显示
|
||||
document.querySelectorAll('.step-item').forEach(item => {
|
||||
item.style.display = 'none';
|
||||
item.classList.remove('instant', 'animated');
|
||||
const line = item.querySelector('.step-line');
|
||||
if (line) {
|
||||
line.style.height = '0';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 展开思考内容
|
||||
function expandThinking(stepIndex) {
|
||||
const thinkingContent = document.getElementById(`thinkingContent${stepIndex}`);
|
||||
const thinkingContentFull = document.getElementById(`thinkingContentFull${stepIndex}`);
|
||||
|
||||
thinkingContent.style.display = 'none';
|
||||
thinkingContentFull.style.display = 'block';
|
||||
|
||||
// 重新计算并更新竖线高度
|
||||
setTimeout(() => {
|
||||
updateLineHeight(stepIndex);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
// 收起思考内容
|
||||
function collapseThinking(stepIndex) {
|
||||
const thinkingContent = document.getElementById(`thinkingContent${stepIndex}`);
|
||||
const thinkingContentFull = document.getElementById(`thinkingContentFull${stepIndex}`);
|
||||
|
||||
thinkingContent.style.display = 'block';
|
||||
thinkingContentFull.style.display = 'none';
|
||||
|
||||
// 重新计算并更新竖线高度
|
||||
setTimeout(() => {
|
||||
updateLineHeight(stepIndex);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
// 重置演示
|
||||
function resetDemo() {
|
||||
currentStep = 0;
|
||||
isExpanded = false;
|
||||
demoRunning = false;
|
||||
|
||||
// 隐藏所有元素
|
||||
document.getElementById('generatingPlaceholder').style.display = 'none';
|
||||
|
||||
const summaryWrapper = document.getElementById('summaryWrapper');
|
||||
summaryWrapper.style.display = 'none';
|
||||
summaryWrapper.classList.remove('running', 'expanded');
|
||||
|
||||
const summaryText = document.getElementById('summaryText');
|
||||
const summaryTextFull = document.getElementById('summaryTextFull');
|
||||
summaryText.style.display = 'block';
|
||||
summaryTextFull.style.display = 'none';
|
||||
|
||||
const summaryLine = document.getElementById('summaryLine');
|
||||
summaryLine.style.height = '0';
|
||||
|
||||
const stepsContainer = document.getElementById('stepsContainer');
|
||||
stepsContainer.style.display = 'none';
|
||||
stepsContainer.classList.remove('show');
|
||||
|
||||
const textOutput = document.getElementById('textOutput');
|
||||
textOutput.style.display = 'none';
|
||||
textOutput.classList.remove('show');
|
||||
const textContent = textOutput.querySelector('.text-content');
|
||||
if (textContent) {
|
||||
textContent.innerHTML = `<p>根据我的搜索和分析,2026年AI技术的主要发展趋势包括:</p>
|
||||
<ol>
|
||||
<li><strong>多模态AI模型的普及</strong>:能够同时处理文本、图像、音频等多种数据类型的AI模型将成为主流。</li>
|
||||
<li><strong>AI安全性和可解释性</strong>:随着AI应用的广泛部署,确保AI系统的安全性和决策的可解释性变得越来越重要。</li>
|
||||
<li><strong>边缘AI计算</strong>:将AI计算能力部署到边缘设备,实现更快的响应速度和更好的隐私保护。</li>
|
||||
</ol>
|
||||
<p>这些趋势将深刻影响各行各业的数字化转型进程。</p>`;
|
||||
}
|
||||
|
||||
// 重置所有步骤
|
||||
document.querySelectorAll('.step-item').forEach(item => {
|
||||
item.style.display = 'none';
|
||||
item.classList.remove('instant', 'animated');
|
||||
const line = item.querySelector('.step-line');
|
||||
if (line) {
|
||||
line.style.height = '0';
|
||||
}
|
||||
});
|
||||
|
||||
// 重置思考内容
|
||||
document.querySelectorAll('.thinking-content').forEach(item => {
|
||||
item.style.display = 'block';
|
||||
});
|
||||
document.querySelectorAll('.thinking-content-full').forEach(item => {
|
||||
item.style.display = 'none';
|
||||
});
|
||||
|
||||
// 重置摘要文本
|
||||
updateSummary(steps[0]);
|
||||
}
|
||||
|
||||
// 获取图标SVG
|
||||
function getIconSVG(iconType) {
|
||||
const icons = {
|
||||
brain: '<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"></path><path d="M12 6v6l4 2"></path>',
|
||||
search: '<circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.35-4.35"></path>',
|
||||
terminal: '<polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line>'
|
||||
};
|
||||
return icons[iconType] || icons.brain;
|
||||
}
|
||||
|
||||
// 工具函数:延迟
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// 点击摘要包装器切换展开/折叠
|
||||
document.getElementById('summaryWrapper').addEventListener('click', toggleExpand);
|
||||
448
static/demo/minimal-mode-v2/style.css
Normal file
448
static/demo/minimal-mode-v2/style.css
Normal file
@ -0,0 +1,448 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif;
|
||||
background: #f8f9fa;
|
||||
color: #1a1a1a;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.chat-area {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 32px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
/* 消息块 */
|
||||
.message-block {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.message-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 用户消息 */
|
||||
.user-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.user-message .message-text {
|
||||
background: rgba(0, 0, 0, 0.03);
|
||||
padding: 14px 18px;
|
||||
border-radius: 16px;
|
||||
max-width: 60%;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* AI消息 */
|
||||
.assistant-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* 等待动画 */
|
||||
.generating-placeholder {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
padding: 12px 0;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.generating-placeholder .letter {
|
||||
display: inline-block;
|
||||
animation: wave 1.6s ease-in-out infinite;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.generating-placeholder .letter:nth-child(1) { animation-delay: 0s; }
|
||||
.generating-placeholder .letter:nth-child(2) { animation-delay: 0.1s; }
|
||||
.generating-placeholder .letter:nth-child(3) { animation-delay: 0.2s; }
|
||||
.generating-placeholder .letter:nth-child(4) { animation-delay: 0.3s; }
|
||||
.generating-placeholder .letter:nth-child(5) { animation-delay: 0.4s; }
|
||||
.generating-placeholder .letter:nth-child(6) { animation-delay: 0.5s; }
|
||||
|
||||
@keyframes wave {
|
||||
0%, 100% {
|
||||
opacity: 0.4;
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 摘要包装器 */
|
||||
.summary-wrapper {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.summary-wrapper:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* 摘要时间线 */
|
||||
.summary-timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.summary-icon {
|
||||
color: #999;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.summary-line {
|
||||
width: 2px;
|
||||
height: 0;
|
||||
background: #e0e0e0;
|
||||
margin-top: 6px;
|
||||
transition: height 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.summary-wrapper.expanded .summary-line {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* 摘要内容 */
|
||||
.summary-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.summary-text {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
line-height: 1.6;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.summary-text.collapsed {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.summary-text.collapsed::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 40px;
|
||||
background: linear-gradient(to right, transparent, white);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.summary-text-full {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* 运行中的发光效果(字体颜色变化) */
|
||||
.summary-wrapper.running .summary-text,
|
||||
.summary-wrapper.running .summary-text-full {
|
||||
animation: textGlow 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes textGlow {
|
||||
0%, 100% {
|
||||
color: #999;
|
||||
}
|
||||
50% {
|
||||
color: #6464ff;
|
||||
}
|
||||
}
|
||||
|
||||
/* 步骤容器 */
|
||||
.steps-container {
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
max-height 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.steps-container.show {
|
||||
opacity: 1;
|
||||
max-height: 3000px;
|
||||
}
|
||||
|
||||
/* 步骤项 */
|
||||
.step-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
transition: opacity 0.25s ease, transform 0.25s ease;
|
||||
}
|
||||
|
||||
.step-item.instant {
|
||||
animation: fadeInStep 0.15s ease forwards;
|
||||
}
|
||||
|
||||
.step-item.animated {
|
||||
animation: fadeInStep 0.35s ease forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeInStep {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 步骤时间线 */
|
||||
.step-timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
color: #666;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.step-line {
|
||||
width: 2px;
|
||||
background: #e0e0e0;
|
||||
margin-top: 6px;
|
||||
transition: height 0.2s ease;
|
||||
}
|
||||
|
||||
.step-item:last-child .step-line {
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
/* 步骤内容 */
|
||||
.step-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.step-header {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #999;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.step-body {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 思考内容 */
|
||||
.thinking-content {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
max-height: 100px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.thinking-content-full {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.show-more-btn,
|
||||
.show-less-btn {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
padding: 4px 12px;
|
||||
background: transparent;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.show-more-btn:hover,
|
||||
.show-less-btn:hover {
|
||||
background: #f5f5f5;
|
||||
border-color: #999;
|
||||
}
|
||||
|
||||
/* 工具内容 */
|
||||
.tool-intent {
|
||||
font-weight: 500;
|
||||
color: #444;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.tool-result {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.result-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.result-url {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 命令块 */
|
||||
.command-block {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.command-header {
|
||||
background: #e9ecef;
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.command-code {
|
||||
padding: 12px;
|
||||
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.command-output {
|
||||
padding: 12px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.output-line {
|
||||
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
/* 最终输出 */
|
||||
.text-output {
|
||||
margin-top: 16px;
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
transition: all 0.4s ease;
|
||||
}
|
||||
|
||||
.text-output.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.text-content {
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.text-content p {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.text-content ol {
|
||||
margin-left: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.text-content li {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.text-content strong {
|
||||
font-weight: 600;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
/* 控制面板 */
|
||||
.control-panel {
|
||||
margin-top: 32px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 10px 20px;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: #0056b3;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.control-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
191
static/demo/minimal-mode/index.html
Normal file
191
static/demo/minimal-mode/index.html
Normal file
@ -0,0 +1,191 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>极简模式 Demo</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="chat-area">
|
||||
<!-- 用户消息 -->
|
||||
<div class="message-block user-message">
|
||||
<div class="message-header">
|
||||
<svg class="icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
<span>用户</span>
|
||||
</div>
|
||||
<div class="message-text">
|
||||
帮我搜索一下最新的AI技术发展趋势
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI 消息 -->
|
||||
<div class="message-block assistant-message">
|
||||
<div class="message-header">
|
||||
<svg class="icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||||
</svg>
|
||||
<span>AI Assistant</span>
|
||||
</div>
|
||||
|
||||
<!-- 等待动画 -->
|
||||
<div class="generating-placeholder" id="generatingPlaceholder">
|
||||
<span class="letter">思</span>
|
||||
<span class="letter">考</span>
|
||||
<span class="letter">中</span>
|
||||
<span class="letter">.</span>
|
||||
<span class="letter">.</span>
|
||||
<span class="letter">.</span>
|
||||
</div>
|
||||
|
||||
<!-- 摘要行 -->
|
||||
<div class="summary-line" id="summaryLine" style="display: none;">
|
||||
<svg class="summary-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
<span class="summary-text" id="summaryText">正在思考如何搜索...</span>
|
||||
<div class="shimmer-overlay"></div>
|
||||
</div>
|
||||
|
||||
<!-- 展开的详细内容 -->
|
||||
<div class="detail-view" id="detailView" style="display: none;">
|
||||
<div class="timeline-container">
|
||||
<!-- 左侧时间线 -->
|
||||
<div class="timeline">
|
||||
<!-- 第一个步骤不显示图标,使用摘要行的图标 -->
|
||||
<div class="timeline-item timeline-item-first" data-step="0">
|
||||
<div class="timeline-line"></div>
|
||||
</div>
|
||||
<div class="timeline-item" data-step="1" style="display: none;">
|
||||
<svg class="timeline-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<path d="m21 21-4.35-4.35"></path>
|
||||
</svg>
|
||||
<div class="timeline-line"></div>
|
||||
</div>
|
||||
<div class="timeline-item" data-step="2" style="display: none;">
|
||||
<svg class="timeline-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"></path>
|
||||
<path d="M12 6v6l4 2"></path>
|
||||
</svg>
|
||||
<div class="timeline-line"></div>
|
||||
</div>
|
||||
<div class="timeline-item" data-step="3" style="display: none;">
|
||||
<svg class="timeline-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="4 17 10 11 4 5"></polyline>
|
||||
<line x1="12" y1="19" x2="20" y2="19"></line>
|
||||
</svg>
|
||||
<div class="timeline-line"></div>
|
||||
</div>
|
||||
<div class="timeline-item" data-step="4" style="display: none;">
|
||||
<svg class="timeline-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"></path>
|
||||
<path d="M12 6v6l4 2"></path>
|
||||
</svg>
|
||||
<div class="timeline-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容 -->
|
||||
<div class="content-area">
|
||||
<div class="content-item" data-step="0">
|
||||
<div class="content-body">
|
||||
<div class="thinking-content" id="thinkingContent0">
|
||||
我需要搜索最新的AI技术发展趋势。首先,我应该使用搜索工具来查找相关信息。搜索关键词应该包括"AI技术"、"人工智能"、"2026"、"发展趋势"等。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-item" data-step="1" style="display: none;">
|
||||
<div class="content-header">搜索网页</div>
|
||||
<div class="content-body">
|
||||
<div class="tool-intent">搜索:AI技术发展趋势 2026</div>
|
||||
<div class="tool-result">
|
||||
<div class="result-item">
|
||||
<div class="result-title">2026年AI技术十大趋势</div>
|
||||
<div class="result-url">https://example.com/ai-trends-2026</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<div class="result-title">人工智能最新突破</div>
|
||||
<div class="result-url">https://example.com/ai-breakthrough</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-item" data-step="2" style="display: none;">
|
||||
<div class="content-body">
|
||||
<div class="thinking-content" id="thinkingContent2">
|
||||
搜索结果显示了一些有价值的信息。现在我需要执行一个命令来获取更详细的数据分析。我将使用curl命令来获取第一个链接的内容,然后进行分析。这将帮助我提供更准确和详细的答案。让我执行这个命令...
|
||||
<button class="show-more-btn" onclick="expandThinking(2)">显示更多</button>
|
||||
</div>
|
||||
<div class="thinking-content-full" id="thinkingContentFull2" style="display: none;">
|
||||
搜索结果显示了一些有价值的信息。现在我需要执行一个命令来获取更详细的数据分析。我将使用curl命令来获取第一个链接的内容,然后进行分析。这将帮助我提供更准确和详细的答案。让我执行这个命令...
|
||||
|
||||
在执行之前,我需要确保命令的安全性和有效性。curl命令是一个常用的网络请求工具,可以帮助我获取网页内容。我会添加适当的参数来确保请求的稳定性,比如设置超时时间、用户代理等。
|
||||
|
||||
同时,我还需要考虑如何解析返回的HTML内容,提取出关键信息。可能需要使用一些文本处理工具,如grep、sed或awk来过滤和格式化数据。
|
||||
<button class="show-less-btn" onclick="collapseThinking(2)">收起</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-item" data-step="3" style="display: none;">
|
||||
<div class="content-header">执行命令</div>
|
||||
<div class="content-body">
|
||||
<div class="tool-intent">获取网页内容并分析</div>
|
||||
<div class="tool-result">
|
||||
<div class="command-block">
|
||||
<div class="command-header">
|
||||
<span class="command-label">bash</span>
|
||||
</div>
|
||||
<pre class="command-code">curl -s "https://example.com/ai-trends-2026" | grep -i "trend"</pre>
|
||||
<div class="command-output">
|
||||
<div class="output-line">Trend 1: 多模态AI模型的普及</div>
|
||||
<div class="output-line">Trend 2: AI安全性和可解释性</div>
|
||||
<div class="output-line">Trend 3: 边缘AI计算</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-item" data-step="4" style="display: none;">
|
||||
<div class="content-body">
|
||||
<div class="thinking-content" id="thinkingContent4">
|
||||
很好,我已经收集到了足够的信息。现在我可以整理这些内容,为用户提供一个清晰、全面的答案。我会按照趋势的重要性和影响力来组织内容。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最终输出 -->
|
||||
<div class="text-output" id="textOutput" style="display: none;">
|
||||
<div class="text-content">
|
||||
<p>根据我的搜索和分析,2026年AI技术的主要发展趋势包括:</p>
|
||||
<ol>
|
||||
<li><strong>多模态AI模型的普及</strong>:能够同时处理文本、图像、音频等多种数据类型的AI模型将成为主流。</li>
|
||||
<li><strong>AI安全性和可解释性</strong>:随着AI应用的广泛部署,确保AI系统的安全性和决策的可解释性变得越来越重要。</li>
|
||||
<li><strong>边缘AI计算</strong>:将AI计算能力部署到边缘设备,实现更快的响应速度和更好的隐私保护。</li>
|
||||
</ol>
|
||||
<p>这些趋势将深刻影响各行各业的数字化转型进程。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 控制面板 -->
|
||||
<div class="control-panel">
|
||||
<button onclick="startDemo()" class="control-btn">开始演示</button>
|
||||
<button onclick="resetDemo()" class="control-btn">重置</button>
|
||||
<button onclick="toggleSummary()" class="control-btn">切换展开/折叠</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
191
static/demo/minimal-mode/index.html.backup
Normal file
191
static/demo/minimal-mode/index.html.backup
Normal file
@ -0,0 +1,191 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>极简模式 Demo</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="chat-area">
|
||||
<!-- 用户消息 -->
|
||||
<div class="message-block user-message">
|
||||
<div class="message-header">
|
||||
<svg class="icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
<span>用户</span>
|
||||
</div>
|
||||
<div class="message-text">
|
||||
帮我搜索一下最新的AI技术发展趋势
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI 消息 -->
|
||||
<div class="message-block assistant-message">
|
||||
<div class="message-header">
|
||||
<svg class="icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||||
</svg>
|
||||
<span>AI Assistant</span>
|
||||
</div>
|
||||
|
||||
<!-- 等待动画 -->
|
||||
<div class="generating-placeholder" id="generatingPlaceholder">
|
||||
<span class="letter">思</span>
|
||||
<span class="letter">考</span>
|
||||
<span class="letter">中</span>
|
||||
<span class="letter">.</span>
|
||||
<span class="letter">.</span>
|
||||
<span class="letter">.</span>
|
||||
</div>
|
||||
|
||||
<!-- 摘要行 -->
|
||||
<div class="summary-line" id="summaryLine" style="display: none;">
|
||||
<svg class="summary-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
<span class="summary-text" id="summaryText">正在思考如何搜索...</span>
|
||||
<div class="shimmer-overlay"></div>
|
||||
</div>
|
||||
|
||||
<!-- 展开的详细内容 -->
|
||||
<div class="detail-view" id="detailView" style="display: none;">
|
||||
<div class="timeline-container">
|
||||
<!-- 左侧时间线 -->
|
||||
<div class="timeline">
|
||||
<!-- 第一个步骤不显示图标,使用摘要行的图标 -->
|
||||
<div class="timeline-item timeline-item-first" data-step="0">
|
||||
<div class="timeline-line"></div>
|
||||
</div>
|
||||
<div class="timeline-item" data-step="1" style="display: none;">
|
||||
<svg class="timeline-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<path d="m21 21-4.35-4.35"></path>
|
||||
</svg>
|
||||
<div class="timeline-line"></div>
|
||||
</div>
|
||||
<div class="timeline-item" data-step="2" style="display: none;">
|
||||
<svg class="timeline-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"></path>
|
||||
<path d="M12 6v6l4 2"></path>
|
||||
</svg>
|
||||
<div class="timeline-line"></div>
|
||||
</div>
|
||||
<div class="timeline-item" data-step="3" style="display: none;">
|
||||
<svg class="timeline-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="4 17 10 11 4 5"></polyline>
|
||||
<line x1="12" y1="19" x2="20" y2="19"></line>
|
||||
</svg>
|
||||
<div class="timeline-line"></div>
|
||||
</div>
|
||||
<div class="timeline-item" data-step="4" style="display: none;">
|
||||
<svg class="timeline-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"></path>
|
||||
<path d="M12 6v6l4 2"></path>
|
||||
</svg>
|
||||
<div class="timeline-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容 -->
|
||||
<div class="content-area">
|
||||
<div class="content-item" data-step="0">
|
||||
<div class="content-body">
|
||||
<div class="thinking-content" id="thinkingContent0">
|
||||
我需要搜索最新的AI技术发展趋势。首先,我应该使用搜索工具来查找相关信息。搜索关键词应该包括"AI技术"、"人工智能"、"2026"、"发展趋势"等。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-item" data-step="1" style="display: none;">
|
||||
<div class="content-header">搜索网页</div>
|
||||
<div class="content-body">
|
||||
<div class="tool-intent">搜索:AI技术发展趋势 2026</div>
|
||||
<div class="tool-result">
|
||||
<div class="result-item">
|
||||
<div class="result-title">2026年AI技术十大趋势</div>
|
||||
<div class="result-url">https://example.com/ai-trends-2026</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<div class="result-title">人工智能最新突破</div>
|
||||
<div class="result-url">https://example.com/ai-breakthrough</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-item" data-step="2" style="display: none;">
|
||||
<div class="content-body">
|
||||
<div class="thinking-content" id="thinkingContent2">
|
||||
搜索结果显示了一些有价值的信息。现在我需要执行一个命令来获取更详细的数据分析。我将使用curl命令来获取第一个链接的内容,然后进行分析。这将帮助我提供更准确和详细的答案。让我执行这个命令...
|
||||
<button class="show-more-btn" onclick="expandThinking(2)">显示更多</button>
|
||||
</div>
|
||||
<div class="thinking-content-full" id="thinkingContentFull2" style="display: none;">
|
||||
搜索结果显示了一些有价值的信息。现在我需要执行一个命令来获取更详细的数据分析。我将使用curl命令来获取第一个链接的内容,然后进行分析。这将帮助我提供更准确和详细的答案。让我执行这个命令...
|
||||
|
||||
在执行之前,我需要确保命令的安全性和有效性。curl命令是一个常用的网络请求工具,可以帮助我获取网页内容。我会添加适当的参数来确保请求的稳定性,比如设置超时时间、用户代理等。
|
||||
|
||||
同时,我还需要考虑如何解析返回的HTML内容,提取出关键信息。可能需要使用一些文本处理工具,如grep、sed或awk来过滤和格式化数据。
|
||||
<button class="show-less-btn" onclick="collapseThinking(2)">收起</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-item" data-step="3" style="display: none;">
|
||||
<div class="content-header">执行命令</div>
|
||||
<div class="content-body">
|
||||
<div class="tool-intent">获取网页内容并分析</div>
|
||||
<div class="tool-result">
|
||||
<div class="command-block">
|
||||
<div class="command-header">
|
||||
<span class="command-label">bash</span>
|
||||
</div>
|
||||
<pre class="command-code">curl -s "https://example.com/ai-trends-2026" | grep -i "trend"</pre>
|
||||
<div class="command-output">
|
||||
<div class="output-line">Trend 1: 多模态AI模型的普及</div>
|
||||
<div class="output-line">Trend 2: AI安全性和可解释性</div>
|
||||
<div class="output-line">Trend 3: 边缘AI计算</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-item" data-step="4" style="display: none;">
|
||||
<div class="content-body">
|
||||
<div class="thinking-content" id="thinkingContent4">
|
||||
很好,我已经收集到了足够的信息。现在我可以整理这些内容,为用户提供一个清晰、全面的答案。我会按照趋势的重要性和影响力来组织内容。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最终输出 -->
|
||||
<div class="text-output" id="textOutput" style="display: none;">
|
||||
<div class="text-content">
|
||||
<p>根据我的搜索和分析,2026年AI技术的主要发展趋势包括:</p>
|
||||
<ol>
|
||||
<li><strong>多模态AI模型的普及</strong>:能够同时处理文本、图像、音频等多种数据类型的AI模型将成为主流。</li>
|
||||
<li><strong>AI安全性和可解释性</strong>:随着AI应用的广泛部署,确保AI系统的安全性和决策的可解释性变得越来越重要。</li>
|
||||
<li><strong>边缘AI计算</strong>:将AI计算能力部署到边缘设备,实现更快的响应速度和更好的隐私保护。</li>
|
||||
</ol>
|
||||
<p>这些趋势将深刻影响各行各业的数字化转型进程。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 控制面板 -->
|
||||
<div class="control-panel">
|
||||
<button onclick="startDemo()" class="control-btn">开始演示</button>
|
||||
<button onclick="resetDemo()" class="control-btn">重置</button>
|
||||
<button onclick="toggleSummary()" class="control-btn">切换展开/折叠</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
333
static/demo/minimal-mode/script.js
Normal file
333
static/demo/minimal-mode/script.js
Normal file
@ -0,0 +1,333 @@
|
||||
// 全局状态
|
||||
let currentStep = 0;
|
||||
let isExpanded = false;
|
||||
let demoRunning = false;
|
||||
|
||||
// 步骤配置
|
||||
const steps = [
|
||||
{
|
||||
type: 'thinking',
|
||||
icon: 'brain',
|
||||
summary: '正在思考如何搜索...',
|
||||
duration: 2000
|
||||
},
|
||||
{
|
||||
type: 'tool',
|
||||
icon: 'search',
|
||||
summary: '搜索:AI技术发展趋势 2026',
|
||||
duration: 2500
|
||||
},
|
||||
{
|
||||
type: 'thinking',
|
||||
icon: 'brain',
|
||||
summary: '搜索结果显示了一些有价值的信息。现在我需要执行...',
|
||||
duration: 2000
|
||||
},
|
||||
{
|
||||
type: 'tool',
|
||||
icon: 'terminal',
|
||||
summary: '执行命令:获取网页内容并分析',
|
||||
duration: 2500
|
||||
},
|
||||
{
|
||||
type: 'thinking',
|
||||
icon: 'brain',
|
||||
summary: '很好,我已经收集到了足够的信息。现在我可以整理...',
|
||||
duration: 2000
|
||||
}
|
||||
];
|
||||
|
||||
// 开始演示
|
||||
async function startDemo() {
|
||||
if (demoRunning) return;
|
||||
|
||||
demoRunning = true;
|
||||
resetDemo();
|
||||
|
||||
// 显示等待动画
|
||||
const placeholder = document.getElementById('generatingPlaceholder');
|
||||
placeholder.style.display = 'flex';
|
||||
|
||||
await sleep(1500);
|
||||
|
||||
// 隐藏等待动画,显示摘要行
|
||||
placeholder.style.display = 'none';
|
||||
const summaryLine = document.getElementById('summaryLine');
|
||||
summaryLine.style.display = 'flex';
|
||||
|
||||
// 执行每个步骤
|
||||
for (let i = 0; i < steps.length; i++) {
|
||||
currentStep = i;
|
||||
await executeStep(i);
|
||||
}
|
||||
|
||||
// 完成后显示最终输出
|
||||
await sleep(1000);
|
||||
summaryLine.classList.add('completed');
|
||||
|
||||
// 如果展开状态,显示最后一步
|
||||
if (isExpanded) {
|
||||
await showDetailStep(currentStep, true);
|
||||
}
|
||||
|
||||
// 淡化摘要并显示第一个步骤(极快)
|
||||
await fadeAndSwitch(summaryLine, steps[0].summary, true);
|
||||
|
||||
await sleep(800);
|
||||
|
||||
// 显示最终输出
|
||||
const textOutput = document.getElementById('textOutput');
|
||||
textOutput.style.display = 'block';
|
||||
setTimeout(() => {
|
||||
textOutput.classList.add('show');
|
||||
}, 50);
|
||||
|
||||
demoRunning = false;
|
||||
}
|
||||
|
||||
// 执行单个步骤
|
||||
async function executeStep(stepIndex) {
|
||||
const step = steps[stepIndex];
|
||||
|
||||
// 更新摘要文本
|
||||
updateSummary(step);
|
||||
|
||||
// 如果展开状态,显示详细内容
|
||||
if (isExpanded) {
|
||||
await showDetailStep(stepIndex);
|
||||
}
|
||||
|
||||
await sleep(step.duration);
|
||||
}
|
||||
|
||||
// 更新摘要
|
||||
function updateSummary(step) {
|
||||
const summaryText = document.getElementById('summaryText');
|
||||
const summaryIcon = document.querySelector('.summary-icon');
|
||||
|
||||
// 更新图标
|
||||
summaryIcon.innerHTML = getIconSVG(step.icon);
|
||||
|
||||
// 更新文本
|
||||
summaryText.textContent = step.summary;
|
||||
}
|
||||
|
||||
// 显示详细步骤
|
||||
async function showDetailStep(stepIndex, isNewStep = false) {
|
||||
const timelineItem = document.querySelector(`.timeline-item[data-step="${stepIndex}"]`);
|
||||
const contentItem = document.querySelector(`.content-item[data-step="${stepIndex}"]`);
|
||||
|
||||
if (!timelineItem || !contentItem) return;
|
||||
|
||||
// 添加动画类
|
||||
if (isNewStep) {
|
||||
timelineItem.classList.add('animated');
|
||||
contentItem.classList.add('animated');
|
||||
} else {
|
||||
timelineItem.classList.add('instant');
|
||||
contentItem.classList.add('instant');
|
||||
}
|
||||
|
||||
// 显示时间线项和内容
|
||||
timelineItem.style.display = 'flex';
|
||||
contentItem.style.display = 'block';
|
||||
|
||||
// 计算并设置竖线高度
|
||||
await sleep(isNewStep ? 100 : 10);
|
||||
updateLineHeight(stepIndex);
|
||||
|
||||
await sleep(isNewStep ? 200 : 10);
|
||||
}
|
||||
|
||||
// 更新竖线高度
|
||||
function updateLineHeight(stepIndex) {
|
||||
const timelineItem = document.querySelector(`.timeline-item[data-step="${stepIndex}"]`);
|
||||
const contentItem = document.querySelector(`.content-item[data-step="${stepIndex}"]`);
|
||||
|
||||
if (!timelineItem || !contentItem) return;
|
||||
|
||||
const line = timelineItem.querySelector('.timeline-line');
|
||||
if (!line) return;
|
||||
|
||||
// 获取内容的实际高度
|
||||
const contentHeight = contentItem.offsetHeight;
|
||||
|
||||
// 对于第一个步骤(没有图标),竖线从顶部开始
|
||||
if (stepIndex === 0) {
|
||||
const gap = 20; // 到下一个步骤的间距
|
||||
const lineHeight = contentHeight + gap;
|
||||
line.style.height = `${lineHeight}px`;
|
||||
return;
|
||||
}
|
||||
|
||||
// 对于其他步骤,计算竖线高度
|
||||
const iconHeight = 20; // 图标高度
|
||||
const gap = 20; // 间距
|
||||
|
||||
// 如果是最后一个步骤,不需要竖线
|
||||
if (stepIndex >= steps.length - 1) {
|
||||
line.style.height = '0';
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算竖线应该的高度:内容高度 + 间距 - 图标高度
|
||||
const lineHeight = Math.max(contentHeight + gap - iconHeight, 40);
|
||||
line.style.height = `${lineHeight}px`;
|
||||
}
|
||||
|
||||
// 切换展开/折叠
|
||||
async function toggleSummary() {
|
||||
const summaryLine = document.getElementById('summaryLine');
|
||||
const detailView = document.getElementById('detailView');
|
||||
|
||||
if (summaryLine.style.display === 'none') return;
|
||||
|
||||
isExpanded = !isExpanded;
|
||||
|
||||
if (isExpanded) {
|
||||
// 展开:极快淡化当前摘要,显示第一个步骤的内容
|
||||
summaryLine.classList.add('expanded');
|
||||
await fadeAndSwitch(summaryLine, steps[0].summary, true);
|
||||
|
||||
// 显示详细视图(快速)
|
||||
detailView.style.display = 'block';
|
||||
setTimeout(() => {
|
||||
detailView.classList.add('show');
|
||||
}, 10);
|
||||
|
||||
// 已完成的步骤快速显示,当前步骤慢速动画
|
||||
for (let i = 0; i <= currentStep; i++) {
|
||||
const isCurrentStep = i === currentStep && demoRunning;
|
||||
await showDetailStep(i, isCurrentStep);
|
||||
if (isCurrentStep) {
|
||||
await sleep(400); // 只有新步骤才有较慢的动画
|
||||
} else {
|
||||
await sleep(50); // 已有内容快速出现
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 折叠:极快淡化第一个步骤,显示当前摘要
|
||||
summaryLine.classList.remove('expanded');
|
||||
await fadeAndSwitch(summaryLine, steps[currentStep].summary, true);
|
||||
|
||||
// 隐藏详细视图(快速)
|
||||
detailView.classList.remove('show');
|
||||
await sleep(150);
|
||||
detailView.style.display = 'none';
|
||||
|
||||
// 重置所有步骤显示
|
||||
document.querySelectorAll('.timeline-item').forEach(item => {
|
||||
item.style.display = 'none';
|
||||
item.classList.remove('instant', 'animated');
|
||||
});
|
||||
document.querySelectorAll('.content-item').forEach(item => {
|
||||
item.style.display = 'none';
|
||||
item.classList.remove('instant', 'animated');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 淡化并切换文本
|
||||
async function fadeAndSwitch(element, newText, fast = false) {
|
||||
const summaryText = element.querySelector('.summary-text');
|
||||
const duration = fast ? 75 : 200;
|
||||
|
||||
// 淡出
|
||||
element.style.opacity = '0.3';
|
||||
await sleep(duration);
|
||||
|
||||
// 切换文本
|
||||
summaryText.textContent = newText;
|
||||
|
||||
// 淡入
|
||||
element.style.opacity = '1';
|
||||
await sleep(duration);
|
||||
}
|
||||
|
||||
// 展开思考内容
|
||||
function expandThinking(stepIndex) {
|
||||
const thinkingContent = document.getElementById(`thinkingContent${stepIndex}`);
|
||||
const thinkingContentFull = document.getElementById(`thinkingContentFull${stepIndex}`);
|
||||
|
||||
thinkingContent.style.display = 'none';
|
||||
thinkingContentFull.style.display = 'block';
|
||||
|
||||
// 重新计算并更新竖线高度
|
||||
setTimeout(() => {
|
||||
updateLineHeight(stepIndex);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
// 收起思考内容
|
||||
function collapseThinking(stepIndex) {
|
||||
const thinkingContent = document.getElementById(`thinkingContent${stepIndex}`);
|
||||
const thinkingContentFull = document.getElementById(`thinkingContentFull${stepIndex}`);
|
||||
|
||||
thinkingContent.style.display = 'block';
|
||||
thinkingContentFull.style.display = 'none';
|
||||
|
||||
// 重新计算并更新竖线高度
|
||||
setTimeout(() => {
|
||||
updateLineHeight(stepIndex);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
// 重置演示
|
||||
function resetDemo() {
|
||||
currentStep = 0;
|
||||
isExpanded = false;
|
||||
demoRunning = false;
|
||||
|
||||
// 隐藏所有元素
|
||||
document.getElementById('generatingPlaceholder').style.display = 'none';
|
||||
|
||||
const summaryLine = document.getElementById('summaryLine');
|
||||
summaryLine.style.display = 'none';
|
||||
summaryLine.classList.remove('completed', 'expanded');
|
||||
summaryLine.style.opacity = '1';
|
||||
|
||||
document.getElementById('detailView').style.display = 'none';
|
||||
document.getElementById('detailView').classList.remove('show');
|
||||
document.getElementById('textOutput').style.display = 'none';
|
||||
document.getElementById('textOutput').classList.remove('show');
|
||||
|
||||
// 重置所有步骤
|
||||
document.querySelectorAll('.timeline-item').forEach(item => {
|
||||
item.style.display = 'none';
|
||||
item.classList.remove('instant', 'animated');
|
||||
const line = item.querySelector('.timeline-line');
|
||||
if (line) {
|
||||
line.style.height = '0';
|
||||
}
|
||||
});
|
||||
document.querySelectorAll('.content-item').forEach(item => {
|
||||
item.style.display = 'none';
|
||||
item.classList.remove('instant', 'animated');
|
||||
});
|
||||
|
||||
// 重置思考内容
|
||||
document.querySelectorAll('.thinking-content').forEach(item => {
|
||||
item.style.display = 'block';
|
||||
});
|
||||
document.querySelectorAll('.thinking-content-full').forEach(item => {
|
||||
item.style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// 获取图标SVG
|
||||
function getIconSVG(iconType) {
|
||||
const icons = {
|
||||
brain: '<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"></path><path d="M12 6v6l4 2"></path>',
|
||||
search: '<circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.35-4.35"></path>',
|
||||
terminal: '<polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line>'
|
||||
};
|
||||
return icons[iconType] || icons.brain;
|
||||
}
|
||||
|
||||
// 工具函数:延迟
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// 点击摘要行切换展开/折叠
|
||||
document.getElementById('summaryLine').addEventListener('click', toggleSummary);
|
||||
503
static/demo/minimal-mode/style.css
Normal file
503
static/demo/minimal-mode/style.css
Normal file
@ -0,0 +1,503 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif;
|
||||
background: #f8f9fa;
|
||||
color: #1a1a1a;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.chat-area {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 32px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
/* 消息块 */
|
||||
.message-block {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.message-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 用户消息 */
|
||||
.user-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.user-message .message-text {
|
||||
background: rgba(0, 0, 0, 0.03);
|
||||
padding: 14px 18px;
|
||||
border-radius: 16px;
|
||||
max-width: 60%;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* AI消息 */
|
||||
.assistant-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* 等待动画 */
|
||||
.generating-placeholder {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
padding: 12px 0;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.generating-placeholder .letter {
|
||||
display: inline-block;
|
||||
animation: wave 1.6s ease-in-out infinite;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.generating-placeholder .letter:nth-child(1) { animation-delay: 0s; }
|
||||
.generating-placeholder .letter:nth-child(2) { animation-delay: 0.1s; }
|
||||
.generating-placeholder .letter:nth-child(3) { animation-delay: 0.2s; }
|
||||
.generating-placeholder .letter:nth-child(4) { animation-delay: 0.3s; }
|
||||
.generating-placeholder .letter:nth-child(5) { animation-delay: 0.4s; }
|
||||
.generating-placeholder .letter:nth-child(6) { animation-delay: 0.5s; }
|
||||
|
||||
@keyframes wave {
|
||||
0%, 100% {
|
||||
opacity: 0.4;
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 摘要行 */
|
||||
.summary-line {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
padding: 10px 0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.summary-line:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.summary-icon {
|
||||
flex-shrink: 0;
|
||||
color: #999;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.summary-text {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 文字渐变消失效果 */
|
||||
.summary-text::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 30px;
|
||||
background: linear-gradient(to right, transparent, white);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 运行中的闪光效果 */
|
||||
.shimmer-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
rgba(100, 100, 255, 0.15) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
animation: shimmer 2s infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
left: -100%;
|
||||
}
|
||||
100% {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.summary-line.completed .shimmer-overlay {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 摘要行展开时的连接线 */
|
||||
.summary-line.expanded {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.summary-line.expanded .summary-icon {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.summary-line.expanded .summary-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 100%;
|
||||
transform: translateX(-50%);
|
||||
width: 2px;
|
||||
height: 8px;
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
/* 详细视图 */
|
||||
.detail-view {
|
||||
margin-top: 0;
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.15s ease, max-height 0.15s ease;
|
||||
}
|
||||
|
||||
.detail-view.show {
|
||||
opacity: 1;
|
||||
max-height: 2000px;
|
||||
}
|
||||
|
||||
.timeline-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* 时间线 */
|
||||
.timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
/* 第一个时间线项(没有图标,只有竖线) */
|
||||
.timeline-item-first {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.timeline-item-first .timeline-line {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.timeline-item.instant {
|
||||
animation: fadeInInstant 0.1s ease forwards;
|
||||
}
|
||||
|
||||
.timeline-item.animated {
|
||||
animation: fadeInDown 0.4s ease forwards;
|
||||
}
|
||||
|
||||
.timeline-icon {
|
||||
flex-shrink: 0;
|
||||
color: #666;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.timeline-line {
|
||||
width: 2px;
|
||||
background: #e0e0e0;
|
||||
margin: 4px 0;
|
||||
transition: height 0.2s ease;
|
||||
}
|
||||
|
||||
.timeline-item:last-child .timeline-line {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes fadeInDown {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInInstant {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.content-item {
|
||||
opacity: 0;
|
||||
transform: translateX(-5px);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content-item.instant {
|
||||
animation: fadeInInstant 0.1s ease forwards;
|
||||
}
|
||||
|
||||
.content-item.animated {
|
||||
animation: fadeInLeft 0.4s ease forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeInLeft {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.content-header {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #999;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* 第一个内容项不显示header */
|
||||
.content-item[data-step="0"] .content-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 思考类型的内容项不显示header */
|
||||
.content-item.thinking-type .content-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content-body {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 思考内容 */
|
||||
.thinking-content {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
max-height: 100px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.thinking-content-full {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.show-more-btn,
|
||||
.show-less-btn {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
padding: 4px 12px;
|
||||
background: transparent;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.show-more-btn:hover,
|
||||
.show-less-btn:hover {
|
||||
background: #f5f5f5;
|
||||
border-color: #999;
|
||||
}
|
||||
|
||||
/* 工具内容 */
|
||||
.tool-intent {
|
||||
font-weight: 500;
|
||||
color: #444;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.tool-result {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.result-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.result-url {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 命令块 */
|
||||
.command-block {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.command-header {
|
||||
background: #e9ecef;
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.command-code {
|
||||
padding: 12px;
|
||||
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.command-output {
|
||||
padding: 12px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.output-line {
|
||||
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
/* 最终输出 */
|
||||
.text-output {
|
||||
margin-top: 16px;
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.text-output.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.text-content {
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.text-content p {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.text-content ol {
|
||||
margin-left: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.text-content li {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.text-content strong {
|
||||
font-weight: 600;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
/* 控制面板 */
|
||||
.control-panel {
|
||||
margin-top: 32px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 10px 20px;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: #0056b3;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.control-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
1
static/demo/test.txt
Normal file
1
static/demo/test.txt
Normal file
@ -0,0 +1 @@
|
||||
test
|
||||
Loading…
Reference in New Issue
Block a user