skill-forge-hub 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -4
- package/annual-report/SKILL.md +17 -2
- package/annual-report/resources/disk-and-html.md +3 -1
- package/annual-report/resources/reference.html +360 -0
- package/annual-report/user-config.md +3 -0
- package/cli.js +175 -36
- package/daily-report/SKILL.md +20 -5
- package/daily-report/resources/content-rules.md +9 -9
- package/daily-report/resources/disk-and-html.md +4 -2
- package/daily-report/resources/reference.html +613 -0
- package/daily-report/resources/usage-guide.md +10 -10
- package/daily-report/user-config.md +5 -2
- package/magic-deck/SKILL.md +52 -0
- package/magic-deck/resources/deck-spec.md +248 -0
- package/magic-deck/resources/reference.html +710 -0
- package/magic-deck/user-config.md +37 -0
- package/meta-forge/SKILL.md +46 -0
- package/meta-forge/resources/forge-rules.md +74 -0
- package/meta-forge/resources/usage-guide.md +41 -0
- package/meta-forge/user-config.md +19 -0
- package/package.json +3 -1
- package/spec-doc2spec/SKILL.md +17 -8
- package/spec-doc2spec/user-config.md +22 -0
- package/spec-spec2doc/SKILL.md +17 -8
- package/spec-spec2doc/user-config.md +22 -0
- package/weekly-report/SKILL.md +20 -5
- package/weekly-report/resources/content-rules.md +10 -10
- package/weekly-report/resources/disk-and-html.md +4 -2
- package/weekly-report/resources/industry-dictionary.md +1 -1
- package/weekly-report/resources/reference.html +866 -0
- package/weekly-report/resources/usage-guide.md +10 -10
- package/weekly-report/user-config.md +5 -2
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Reference 骨架 — magic-deck 定稿样例</title>
|
|
7
|
+
<style>
|
|
8
|
+
/* ══════════ 0. 基础 ══════════ */
|
|
9
|
+
*{ margin:0; padding:0; box-sizing:border-box; }
|
|
10
|
+
html,body{ width:100%; height:100%; overflow:hidden;
|
|
11
|
+
font-family:ui-monospace,'SF Mono',SFMono-Regular,Menlo,Consolas,'Microsoft YaHei',monospace; }
|
|
12
|
+
button,select,input{ font-family:inherit; }
|
|
13
|
+
/* ══════════ 1. 四主题 CSS 变量(8 变量齐备) ══════════ */
|
|
14
|
+
:root, [data-theme="tech"]{
|
|
15
|
+
--bg:#0a1929; --bg2:#071525; --panel:rgba(11,30,55,.72); --panel-solid:#0b1e37;
|
|
16
|
+
--text:#bae6fd; --muted:#5b7fa5; --accent:#0ea5e9; --accent2:#14b8a6;
|
|
17
|
+
--border:rgba(14,165,233,.18); --glow:rgba(14,165,233,.35);
|
|
18
|
+
--title-grad:linear-gradient(90deg,#7dd3fc,#0ea5e9 45%,#818cf8);
|
|
19
|
+
--tag-bg:rgba(14,165,233,.12);
|
|
20
|
+
}
|
|
21
|
+
[data-theme="biz"]{
|
|
22
|
+
--bg:#f4f6fb; --bg2:#e9edf5; --panel:rgba(255,255,255,.85); --panel-solid:#ffffff;
|
|
23
|
+
--text:#1e293b; --muted:#64748b; --accent:#2563eb; --accent2:#0891b2;
|
|
24
|
+
--border:rgba(37,99,235,.14); --glow:rgba(37,99,235,.18);
|
|
25
|
+
--title-grad:linear-gradient(90deg,#1e40af,#2563eb 55%,#0891b2);
|
|
26
|
+
--tag-bg:rgba(37,99,235,.08);
|
|
27
|
+
}
|
|
28
|
+
[data-theme="mono"]{
|
|
29
|
+
--bg:#fafafa; --bg2:#f0f0f0; --panel:rgba(255,255,255,.9); --panel-solid:#ffffff;
|
|
30
|
+
--text:#16181d; --muted:#6b7280; --accent:#111111; --accent2:#4b5563;
|
|
31
|
+
--border:rgba(17,17,17,.14); --glow:rgba(17,17,17,.08);
|
|
32
|
+
--title-grad:linear-gradient(90deg,#111,#333 60%,#666);
|
|
33
|
+
--tag-bg:rgba(17,17,17,.06);
|
|
34
|
+
}
|
|
35
|
+
[data-theme="warm"]{
|
|
36
|
+
--bg:#fdf3e7; --bg2:#f7e7d3; --panel:rgba(255,250,243,.85); --panel-solid:#fffaf3;
|
|
37
|
+
--text:#43301f; --muted:#8a7565; --accent:#d97706; --accent2:#ea580c;
|
|
38
|
+
--border:rgba(217,119,6,.18); --glow:rgba(217,119,6,.22);
|
|
39
|
+
--title-grad:linear-gradient(90deg,#b45309,#d97706 50%,#ea580c);
|
|
40
|
+
--tag-bg:rgba(217,119,6,.1);
|
|
41
|
+
}
|
|
42
|
+
/* ══════════ 2. 背景装饰(舞台外围氛围层) ══════════ */
|
|
43
|
+
.bg-glow{ position:fixed; inset:0; z-index:0;
|
|
44
|
+
background:
|
|
45
|
+
radial-gradient(60% 50% at 50% 0%, var(--glow) 0%, transparent 100%),
|
|
46
|
+
radial-gradient(40% 35% at 100% 100%, var(--glow) 0%, transparent 100%),
|
|
47
|
+
var(--bg); }
|
|
48
|
+
.bg-grid{ position:fixed; inset:0; z-index:0; pointer-events:none;
|
|
49
|
+
background-image:linear-gradient(var(--border) 1px,transparent 1px),linear-gradient(90deg,var(--border) 1px,transparent 1px);
|
|
50
|
+
background-size:72px 72px;
|
|
51
|
+
-webkit-mask-image:radial-gradient(circle at center, #000 0%, transparent 78%);
|
|
52
|
+
mask-image:radial-gradient(circle at center, #000 0%, transparent 78%); }
|
|
53
|
+
/* ══════════ 3. 舞台与切换 ══════════ */
|
|
54
|
+
.stage{ position:fixed; inset:0; z-index:1; display:flex; align-items:center; justify-content:center; }
|
|
55
|
+
.deck{ position:relative; width:1280px; height:720px; transform:scale(var(--sc,1)); transform-origin:center center; flex:0 0 auto; }
|
|
56
|
+
.slide{ position:absolute; inset:0; padding:64px 80px; background:var(--bg); display:flex; flex-direction:column;
|
|
57
|
+
opacity:0; visibility:hidden; transform:translateX(36px);
|
|
58
|
+
transition:opacity .45s ease, transform .45s ease, visibility 0s linear .45s; }
|
|
59
|
+
.slide.active{ opacity:1; visibility:visible; transform:translateX(0); transition:opacity .45s ease, transform .45s ease; }
|
|
60
|
+
.slide.dim{ opacity:0; transform:translateX(-36px); }
|
|
61
|
+
/* ══════════ 4. 版式组件(主题变量驱动) ══════════ */
|
|
62
|
+
.s-title{ font-size:40px; font-weight:900; line-height:1.25;
|
|
63
|
+
background:var(--title-grad); -webkit-background-clip:text; background-clip:text;
|
|
64
|
+
color:transparent; text-shadow:var(--glow) 0 0 18px; }
|
|
65
|
+
.s-head{ display:flex; align-items:center; gap:18px; margin-bottom:34px; }
|
|
66
|
+
.s-head::before{ content:''; width:6px; height:34px; border-radius:3px;
|
|
67
|
+
background:linear-gradient(180deg,var(--accent),var(--accent2)); box-shadow:0 0 12px var(--glow); }
|
|
68
|
+
.s-head h2{ font-size:32px; font-weight:800; color:var(--text); }
|
|
69
|
+
.tag{ flex:0 0 auto; margin-left:auto; font-size:13px; padding:5px 14px; border-radius:999px;
|
|
70
|
+
color:var(--accent); background:var(--tag-bg); border:1px solid var(--border); }
|
|
71
|
+
.panel{ background:var(--panel); border:1px solid var(--border); border-radius:14px;
|
|
72
|
+
padding:22px 26px; backdrop-filter:blur(6px); }
|
|
73
|
+
.grid2{ display:grid; grid-template-columns:1fr 1fr; gap:22px; }
|
|
74
|
+
.grid3{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:22px; }
|
|
75
|
+
.grid4{ display:grid; grid-template-columns:repeat(4,1fr); gap:18px; }
|
|
76
|
+
.card{ background:var(--panel); border:1px solid var(--border); border-radius:12px; padding:20px 22px;
|
|
77
|
+
transition:transform .2s ease, box-shadow .2s ease; }
|
|
78
|
+
.card:hover{ transform:translateY(-4px); box-shadow:0 8px 24px var(--glow); }
|
|
79
|
+
.card h4{ font-size:18px; font-weight:700; color:var(--text); margin-bottom:8px; }
|
|
80
|
+
.card h4 .en{ font-size:12px; color:var(--muted); margin-left:8px; font-weight:400; }
|
|
81
|
+
.card p{ font-size:14px; color:var(--muted); line-height:1.7; }
|
|
82
|
+
.bignum{ background:var(--panel); border:1px solid var(--border); border-radius:12px; padding:18px 20px; text-align:center; }
|
|
83
|
+
.bignum b{ display:block; font-size:52px; font-weight:900; background:var(--title-grad);
|
|
84
|
+
-webkit-background-clip:text; background-clip:text; color:transparent; }
|
|
85
|
+
.bignum span{ font-size:13px; color:var(--muted); }
|
|
86
|
+
.toc{ display:flex; flex-direction:column; gap:14px; }
|
|
87
|
+
.toc-item{ display:flex; align-items:center; gap:16px; padding:16px 22px; border-radius:12px;
|
|
88
|
+
background:var(--panel); border:1px solid var(--border); cursor:pointer;
|
|
89
|
+
transition:transform .2s ease; }
|
|
90
|
+
.toc-item:hover{ transform:translateX(8px); }
|
|
91
|
+
.toc-item .no{ width:34px; height:34px; border-radius:8px; display:flex; align-items:center; justify-content:center;
|
|
92
|
+
font-weight:800; color:var(--accent); background:var(--tag-bg); }
|
|
93
|
+
.toc-item .t{ font-size:18px; font-weight:600; color:var(--text); }
|
|
94
|
+
.toc-item .pg{ margin-left:auto; font-size:13px; color:var(--muted); }
|
|
95
|
+
.checklist{ list-style:none; display:flex; flex-direction:column; gap:12px; }
|
|
96
|
+
.checklist li{ position:relative; padding-left:34px; font-size:16px; color:var(--text); line-height:1.6; }
|
|
97
|
+
.checklist li::before{ content:'✓'; position:absolute; left:0; top:2px; width:22px; height:22px;
|
|
98
|
+
display:flex; align-items:center; justify-content:center; font-size:13px; color:#fff; border-radius:6px;
|
|
99
|
+
background:linear-gradient(135deg,var(--accent),var(--accent2)); }
|
|
100
|
+
.theme-card{ display:flex; align-items:center; gap:16px; padding:18px 22px; border-radius:12px;
|
|
101
|
+
background:var(--panel); border:1px solid var(--border); cursor:pointer; }
|
|
102
|
+
.theme-card .sw{ display:flex; gap:6px; }
|
|
103
|
+
.theme-card .sw i{ width:20px; height:20px; border-radius:50%; border:2px solid rgba(255,255,255,.3); }
|
|
104
|
+
.chip{ display:inline-block; font-size:12px; padding:3px 10px; border-radius:999px;
|
|
105
|
+
color:var(--accent); background:var(--tag-bg); border:1px solid var(--border); }
|
|
106
|
+
.cover{ justify-content:center; align-items:flex-start; }
|
|
107
|
+
.logo{ width:76px; height:76px; border-radius:18px; margin-bottom:30px;
|
|
108
|
+
background:linear-gradient(135deg,var(--accent),var(--accent2)); box-shadow:0 0 28px var(--glow); }
|
|
109
|
+
.cover .s-title{ font-size:64px; }
|
|
110
|
+
.sub{ font-size:20px; color:var(--muted); margin-top:22px; letter-spacing:.5px; }
|
|
111
|
+
.sign{ margin-top:auto; font-size:12px; color:var(--muted); letter-spacing:2px; }
|
|
112
|
+
.endpage{ justify-content:center; align-items:center; text-align:center; }
|
|
113
|
+
.endpage .s-title{ font-size:56px; }
|
|
114
|
+
.endpage p{ margin-top:20px; font-size:16px; color:var(--muted); }
|
|
115
|
+
/* ══════════ 5. 五模板骨架 ══════════ */
|
|
116
|
+
.t-minimal .s-title{ font-size:44px; text-align:center; }
|
|
117
|
+
.t-minimal .points{ margin:0 auto; max-width:640px; list-style:none; }
|
|
118
|
+
.t-minimal .points li{ padding:12px 0; border-bottom:1px solid var(--border);
|
|
119
|
+
font-size:20px; line-height:1.9; color:var(--text); }
|
|
120
|
+
.t-minimal .bignum b{ font-size:88px; }
|
|
121
|
+
.t-magazine .s-title{ font-size:96px; line-height:1.05; }
|
|
122
|
+
.m-split{ display:grid; grid-template-columns:46% 54%; gap:56px; align-items:center; }
|
|
123
|
+
.m-split h3{ font-size:36px; font-weight:800; color:var(--text); margin-bottom:18px; }
|
|
124
|
+
.m-split p{ font-size:16px; color:var(--muted); line-height:1.9; }
|
|
125
|
+
.m-figure{ height:380px; border-radius:18px; display:flex; align-items:flex-end; padding:20px;
|
|
126
|
+
background:linear-gradient(135deg,var(--accent),var(--accent2)); opacity:.85; }
|
|
127
|
+
.m-strip{ display:flex; justify-content:space-between; }
|
|
128
|
+
.m-strip>div{ flex:1; text-align:center; border-left:1px solid var(--border); }
|
|
129
|
+
.m-strip>div:first-child{ border-left:0; }
|
|
130
|
+
.m-strip b{ display:block; font-size:64px; font-weight:900; background:var(--title-grad);
|
|
131
|
+
-webkit-background-clip:text; background-clip:text; color:transparent; }
|
|
132
|
+
.m-strip span{ font-size:14px; color:var(--muted); }
|
|
133
|
+
.t-keynote .s-title{ font-size:112px; }
|
|
134
|
+
.k-big{ font-size:64px; font-weight:900; text-align:center; color:var(--text); margin:auto; }
|
|
135
|
+
.k-sub{ font-size:22px; color:var(--muted); text-align:center; margin-top:18px; }
|
|
136
|
+
.k-bar{ width:320px; height:4px; border-radius:2px; margin:40px auto 0;
|
|
137
|
+
background:linear-gradient(90deg,var(--accent),var(--accent2)); }
|
|
138
|
+
.k-ring{ width:120px; height:120px; border-radius:50%; margin:10px auto;
|
|
139
|
+
background:conic-gradient(var(--accent) 0 72%, var(--border) 72% 100%);
|
|
140
|
+
display:flex; align-items:center; justify-content:center; }
|
|
141
|
+
.k-ring b{ font-size:34px; color:var(--text); }
|
|
142
|
+
.tl{ position:relative; padding-left:32px; }
|
|
143
|
+
.tl::before{ content:''; position:absolute; left:6px; top:8px; bottom:8px; width:3px; background:var(--accent); }
|
|
144
|
+
.tl-item{ position:relative; padding:18px 0 18px 24px; }
|
|
145
|
+
.tl-item::before{ content:''; position:absolute; left:-32px; top:24px; width:14px; height:14px;
|
|
146
|
+
border-radius:50%; background:var(--accent); box-shadow:0 0 0 4px var(--tag-bg); }
|
|
147
|
+
.tl-time{ font-size:24px; font-weight:800; color:var(--accent); }
|
|
148
|
+
.tl-item h4{ font-size:18px; font-weight:700; color:var(--text); margin:6px 0; }
|
|
149
|
+
.tl-item p{ font-size:14px; color:var(--muted); line-height:1.7; }
|
|
150
|
+
/* ══════════ 6. 操作元素(固定样式) ══════════ */
|
|
151
|
+
.toolbar{ position:fixed; right:18px; top:14px; z-index:10;
|
|
152
|
+
display:flex; align-items:center; gap:10px; }
|
|
153
|
+
.group{ display:flex; align-items:center; gap:8px; padding:4px 6px; border-radius:999px;
|
|
154
|
+
background:var(--panel-solid); border:1px solid var(--border); box-shadow:0 4px 16px rgba(0,0,0,.18); }
|
|
155
|
+
.tbtn{ display:inline-flex; align-items:center; justify-content:center; gap:6px;
|
|
156
|
+
padding:7px 12px; line-height:1; font-size:13px; color:var(--text);
|
|
157
|
+
background:transparent; border:1px solid transparent; border-radius:999px; cursor:pointer;
|
|
158
|
+
white-space:nowrap; transition:all .15s ease; }
|
|
159
|
+
.tbtn:hover{ border-color:var(--border); background:var(--tag-bg); }
|
|
160
|
+
.tbtn.on{ color:#fff; background:var(--accent); border-color:var(--accent); }
|
|
161
|
+
.tbtn.theme{ width:16px; height:16px; padding:0; border-radius:50%; border:2px solid rgba(255,255,255,.35); }
|
|
162
|
+
.tbtn.theme.on{ outline:2px solid var(--accent); outline-offset:2px; }
|
|
163
|
+
.tbtn.theme[data-theme="tech"]{ background:#0ea5e9; }
|
|
164
|
+
.tbtn.theme[data-theme="biz"]{ background:#2563eb; }
|
|
165
|
+
.tbtn.theme[data-theme="mono"]{ background:#111; }
|
|
166
|
+
.tbtn.theme[data-theme="warm"]{ background:#d97706; }
|
|
167
|
+
select.tbtn{ appearance:auto; padding:7px 10px; }
|
|
168
|
+
label.tbtn{ cursor:pointer; }
|
|
169
|
+
label.tbtn input[type=checkbox]{ width:14px; height:14px; margin:0; flex:0 0 auto; accent-color:var(--accent); }
|
|
170
|
+
.pageno{ position:fixed; left:18px; bottom:16px; z-index:10; padding:6px 14px; border-radius:999px;
|
|
171
|
+
font-size:13px; color:var(--muted); background:var(--panel-solid); border:1px solid var(--border); }
|
|
172
|
+
.pageno b{ color:var(--accent); }
|
|
173
|
+
.nav{ position:fixed; top:50%; transform:translateY(-50%); z-index:10; width:46px; height:46px;
|
|
174
|
+
border-radius:50%; font-size:22px; color:var(--text); background:var(--panel-solid);
|
|
175
|
+
border:1px solid var(--border); cursor:pointer; opacity:0; transition:all .2s ease; }
|
|
176
|
+
.nav.prev{ left:18px; } .nav.next{ right:18px; }
|
|
177
|
+
body:hover .nav{ opacity:1; }
|
|
178
|
+
.nav:hover{ border-color:var(--accent); color:var(--accent); box-shadow:0 0 14px var(--glow); }
|
|
179
|
+
@media (hover:none){ .nav{ opacity:1; } }
|
|
180
|
+
.hint{ position:fixed; bottom:16px; left:50%; transform:translateX(-50%); z-index:10;
|
|
181
|
+
font-size:11px; color:var(--muted); letter-spacing:.5px; }
|
|
182
|
+
.help-mask{ position:fixed; inset:0; z-index:20; display:none; align-items:center; justify-content:center;
|
|
183
|
+
background:rgba(0,0,0,.55); backdrop-filter:blur(4px); }
|
|
184
|
+
.help-mask.open{ display:flex; }
|
|
185
|
+
.help-panel{ width:560px; max-height:70vh; overflow:auto; padding:30px 34px; border-radius:16px;
|
|
186
|
+
background:var(--panel-solid); border:1px solid var(--border); color:var(--text); }
|
|
187
|
+
.help-panel h3{ font-size:20px; margin-bottom:16px; }
|
|
188
|
+
.help-panel table{ width:100%; border-collapse:collapse; font-size:13px; }
|
|
189
|
+
.help-panel td{ padding:6px 8px; border-bottom:1px solid var(--border); }
|
|
190
|
+
.help-panel kbd{ padding:2px 8px; border-radius:5px; background:var(--tag-bg);
|
|
191
|
+
border:1px solid var(--border); font-family:inherit; }
|
|
192
|
+
.help-note{ margin-top:14px; font-size:12px; color:var(--muted); line-height:1.8; }
|
|
193
|
+
/* ═══ 选页复制面板 ═══ */
|
|
194
|
+
.copy-panel{ width:430px; max-height:72vh; display:flex; flex-direction:column; padding:24px 26px;
|
|
195
|
+
border-radius:16px; background:var(--panel-solid); border:1px solid var(--border); color:var(--text); }
|
|
196
|
+
.copy-panel h3{ font-size:18px; margin:0; }
|
|
197
|
+
.cp-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; }
|
|
198
|
+
.cp-all{ display:inline-flex; align-items:center; gap:6px; font-size:13px; cursor:pointer; }
|
|
199
|
+
.cp-list{ flex:1; overflow-y:auto; border:1px solid var(--border); border-radius:10px; }
|
|
200
|
+
.cp-item{ display:flex; align-items:center; gap:10px; padding:9px 12px; border-bottom:1px solid var(--border);
|
|
201
|
+
cursor:pointer; font-size:13px; }
|
|
202
|
+
.cp-item:last-child{ border-bottom:none; }
|
|
203
|
+
.cp-item:hover{ background:var(--tag-bg); }
|
|
204
|
+
.cp-item input{ margin:0; flex:0 0 auto; }
|
|
205
|
+
.cp-no{ flex-shrink:0; color:var(--accent); font-weight:600; }
|
|
206
|
+
.cp-preview{ flex:1; min-width:0; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
207
|
+
.cp-foot{ display:flex; justify-content:flex-end; gap:10px; margin-top:14px; }
|
|
208
|
+
.cp-foot .tbtn{ border:1px solid var(--border); background:var(--tag-bg); cursor:pointer; }
|
|
209
|
+
.cp-foot .tbtn.primary{ background:var(--accent); color:var(--bg); border-color:transparent; }
|
|
210
|
+
/* ══════════ 7. 全屏放映纯净模式(PPT 放映规范:黑底 + 幻灯充满画面) ══════════ */
|
|
211
|
+
:root:fullscreen{ background:#000; } /* 放映黑底:与 PPT 软件放映一致,比例不匹配处补黑边,不带主题氛围边框 */
|
|
212
|
+
:root:fullscreen .bg-glow,
|
|
213
|
+
:root:fullscreen .bg-grid{ display:none !important; } /* 氛围层只在窗口浏览态,放映时退场 */
|
|
214
|
+
:root:fullscreen .toolbar,
|
|
215
|
+
:root:fullscreen .pageno,
|
|
216
|
+
:root:fullscreen .hint,
|
|
217
|
+
:root:fullscreen .nav,
|
|
218
|
+
:root:fullscreen .help-mask{ display:none !important; }
|
|
219
|
+
</style>
|
|
220
|
+
</head>
|
|
221
|
+
<body>
|
|
222
|
+
<div class="bg-glow"></div>
|
|
223
|
+
<div class="bg-grid"></div>
|
|
224
|
+
<div class="progress" style="position:fixed;top:0;left:0;right:0;height:3px;z-index:10;background:transparent;">
|
|
225
|
+
<div class="fill" id="progFill" style="height:100%;width:0;background:linear-gradient(90deg,var(--accent),var(--accent2));transition:width .3s ease;"></div>
|
|
226
|
+
</div>
|
|
227
|
+
<div class="toolbar">
|
|
228
|
+
<div class="group" title="主题:点色点整份换肤,点「仅本页」后只改当前页">
|
|
229
|
+
<button class="tbtn theme" data-theme="tech" title="tech 深蓝科技"></button>
|
|
230
|
+
<button class="tbtn theme" data-theme="biz" title="biz 浅色商务"></button>
|
|
231
|
+
<button class="tbtn theme" data-theme="mono" title="mono 极简黑白"></button>
|
|
232
|
+
<button class="tbtn theme" data-theme="warm" title="warm 温暖渐变"></button>
|
|
233
|
+
<button class="tbtn" id="btnScope" title="开启后主题只作用于当前页">⭮ 仅本页</button>
|
|
234
|
+
</div>
|
|
235
|
+
<div class="group">
|
|
236
|
+
<button class="tbtn" id="btnPlay">▶ 自动播放</button>
|
|
237
|
+
<select class="tbtn" id="selInterval">
|
|
238
|
+
<option value="3">3s</option>
|
|
239
|
+
<option value="5" selected>5s</option>
|
|
240
|
+
<option value="8">8s</option>
|
|
241
|
+
<option value="10">10s</option>
|
|
242
|
+
</select>
|
|
243
|
+
<label class="tbtn"><input type="checkbox" id="chkLoop">循环</label>
|
|
244
|
+
<label class="tbtn"><input type="checkbox" id="chkFromStart" checked>从头</label>
|
|
245
|
+
</div>
|
|
246
|
+
<div class="group">
|
|
247
|
+
<button class="tbtn" id="btnCopy">⧉ 复制本页</button>
|
|
248
|
+
<button class="tbtn" id="btnCopyMulti">▦ 选页复制</button>
|
|
249
|
+
<button class="tbtn" id="btnShot">📷 截图本页</button>
|
|
250
|
+
<button class="tbtn" id="btnShotMulti">▦ 选页截图</button>
|
|
251
|
+
<button class="tbtn" id="btnFull">⛶ 全屏</button>
|
|
252
|
+
<button class="tbtn help" id="btnHelp">?</button>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
<div class="pageno" id="pageNo"><b>1</b> / 9</div>
|
|
256
|
+
<div class="hint">← → 翻页 · 空格下一页 · P 播放 · S 截图 · C 复制 · F 全屏 · ? 操作说明</div>
|
|
257
|
+
<button class="nav prev" id="btnPrev">‹</button>
|
|
258
|
+
<button class="nav next" id="btnNext">›</button>
|
|
259
|
+
<main class="stage"><div class="deck" id="deck">
|
|
260
|
+
|
|
261
|
+
<!-- 页 1 封面(classic 模板) -->
|
|
262
|
+
<section class="slide cover active" data-title="封面">
|
|
263
|
+
<div class="logo"></div>
|
|
264
|
+
<div class="s-title">魔法幻灯<br>Magic Deck</div>
|
|
265
|
+
<div class="sub">HTML 形态 · PPT 体验 · 单文件离线放映</div>
|
|
266
|
+
<div class="sign">GENERATED BY SKILLFORGE</div>
|
|
267
|
+
</section>
|
|
268
|
+
|
|
269
|
+
<!-- 页 2 目录 -->
|
|
270
|
+
<section class="slide" data-title="目录">
|
|
271
|
+
<div class="s-head"><h2>目录</h2><span class="tag">CONTENTS</span></div>
|
|
272
|
+
<div class="toc">
|
|
273
|
+
<div class="toc-item" data-goto="2"><span class="no">01</span><span class="t">经典商务版式</span><span class="pg">第 3 页</span></div>
|
|
274
|
+
<div class="toc-item" data-goto="3"><span class="no">02</span><span class="t">极简留白版式</span><span class="pg">第 4 页</span></div>
|
|
275
|
+
<div class="toc-item" data-goto="4"><span class="no">03</span><span class="t">杂志画册版式</span><span class="pg">第 5 页</span></div>
|
|
276
|
+
<div class="toc-item" data-goto="5"><span class="no">04</span><span class="t">黑金发布版式</span><span class="pg">第 6 页</span></div>
|
|
277
|
+
<div class="toc-item" data-goto="6"><span class="no">05</span><span class="t">时间线叙事版式</span><span class="pg">第 7 页</span></div>
|
|
278
|
+
</div>
|
|
279
|
+
</section>
|
|
280
|
+
|
|
281
|
+
<!-- 页 3 内容页(classic:左色条 + 卡片栅格) -->
|
|
282
|
+
<section class="slide" data-title="经典商务">
|
|
283
|
+
<div class="s-head"><h2>核心能力</h2><span class="tag">CLASSIC</span></div>
|
|
284
|
+
<div class="grid3">
|
|
285
|
+
<div class="card"><h4>单文件交付<span class="en">Single File</span></h4><p>零外部依赖,双击即开,离线可用,分享只需一个 HTML。</p></div>
|
|
286
|
+
<div class="card"><h4>播放引擎<span class="en">Player</span></h4><p>自动播放、循环、间隔可设,从头或当前页续播。</p></div>
|
|
287
|
+
<div class="card"><h4>纯净放映<span class="en">Clean</span></h4><p>全屏时工具栏页码全部退场,只留舞台主体。</p></div>
|
|
288
|
+
</div>
|
|
289
|
+
</section>
|
|
290
|
+
|
|
291
|
+
<!-- 页 4 minimal 极简留白 -->
|
|
292
|
+
<section class="slide" data-title="极简留白">
|
|
293
|
+
<div class="t-minimal">
|
|
294
|
+
<div class="s-title" style="margin-bottom:28px;">少即是多</div>
|
|
295
|
+
<ul class="points">
|
|
296
|
+
<li>没有卡片描边,只有文字与留白</li>
|
|
297
|
+
<li>信息密度低,观点反而更突出</li>
|
|
298
|
+
<li>适合观点陈述与开场引导</li>
|
|
299
|
+
</ul>
|
|
300
|
+
</div>
|
|
301
|
+
</section>
|
|
302
|
+
|
|
303
|
+
<!-- 页 5 magazine 杂志画册 -->
|
|
304
|
+
<section class="slide" data-title="杂志画册">
|
|
305
|
+
<div class="t-magazine">
|
|
306
|
+
<div class="m-split">
|
|
307
|
+
<div>
|
|
308
|
+
<h3>图与文的黄金分栏</h3>
|
|
309
|
+
<p>左文右图 46/54 分栏,图位用主题渐变色块占位,杂志感扑面而来。数据页另有横向大数字带 m-strip 呈现关键指标。</p>
|
|
310
|
+
</div>
|
|
311
|
+
<div class="m-figure"><span class="chip">封面图位 · 渐变占位</span></div>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
</section>
|
|
315
|
+
|
|
316
|
+
<!-- 页 6 keynote 黑金发布 -->
|
|
317
|
+
<section class="slide" data-title="黑金发布">
|
|
318
|
+
<div class="t-keynote">
|
|
319
|
+
<div class="k-big">一个观点,讲透</div>
|
|
320
|
+
<div class="k-sub">每页只放一个核心信息,配合环形进度展示达成率</div>
|
|
321
|
+
<div class="k-bar"></div>
|
|
322
|
+
<div class="k-ring"><b>72%</b></div>
|
|
323
|
+
</div>
|
|
324
|
+
</section>
|
|
325
|
+
|
|
326
|
+
<!-- 页 7 timeline 时间线叙事 -->
|
|
327
|
+
<section class="slide" data-title="时间线叙事">
|
|
328
|
+
<div class="s-head"><h2>里程碑</h2><span class="tag">TIMELINE</span></div>
|
|
329
|
+
<div class="t-timeline">
|
|
330
|
+
<div class="tl">
|
|
331
|
+
<div class="tl-item"><div class="tl-time">2026 Q1</div><h4>立项与架构</h4><p>完成技术选型与四主题变量体系。</p></div>
|
|
332
|
+
<div class="tl-item"><div class="tl-time">2026 Q2</div><h4>模板库建设</h4><p>五套版式骨架与主题正交组合。</p></div>
|
|
333
|
+
<div class="tl-item"><div class="tl-time">2026 Q3</div><h4>单页换肤</h4><p>整份或单页自由切换主题。</p></div>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
</section>
|
|
337
|
+
|
|
338
|
+
<!-- 页 8 数据页(bignum 栅格 + magazine 数字带演示) -->
|
|
339
|
+
<section class="slide" data-title="数据一览">
|
|
340
|
+
<div class="s-head"><h2>数据一览</h2><span class="tag">DATA</span></div>
|
|
341
|
+
<div class="grid4">
|
|
342
|
+
<div class="bignum"><b>28</b><span>模板组件</span></div>
|
|
343
|
+
<div class="bignum"><b>5</b><span>版式模板</span></div>
|
|
344
|
+
<div class="bignum"><b>4</b><span>配色主题</span></div>
|
|
345
|
+
<div class="bignum"><b>12</b><span>键盘热键</span></div>
|
|
346
|
+
</div>
|
|
347
|
+
<div class="m-strip" style="margin-top:40px;">
|
|
348
|
+
<div><b>99.9%</b><span>可用性</span></div>
|
|
349
|
+
<div><b>1</b><span>文件交付</span></div>
|
|
350
|
+
<div><b>0</b><span>外部依赖</span></div>
|
|
351
|
+
</div>
|
|
352
|
+
</section>
|
|
353
|
+
|
|
354
|
+
<!-- 页 9 结束页 -->
|
|
355
|
+
<section class="slide endpage" data-title="结束">
|
|
356
|
+
<div class="s-title">谢谢观看</div>
|
|
357
|
+
<p>按 F 全屏放映 · 按 ? 查看全部热键</p>
|
|
358
|
+
<p class="sign">GENERATED BY SKILLFORGE</p>
|
|
359
|
+
</section>
|
|
360
|
+
|
|
361
|
+
</div></main>
|
|
362
|
+
<div class="help-mask" id="helpMask">
|
|
363
|
+
<div class="help-panel">
|
|
364
|
+
<h3>操作说明</h3>
|
|
365
|
+
<table>
|
|
366
|
+
<tr><td><kbd>←</kbd> <kbd>→</kbd> / <kbd>空格</kbd></td><td>翻页</td></tr>
|
|
367
|
+
<tr><td><kbd>P</kbd></td><td>播放 / 暂停</td></tr>
|
|
368
|
+
<tr><td><kbd>S</kbd></td><td>截图本页(PNG 2x,只含主体)</td></tr>
|
|
369
|
+
<tr><td><kbd>C</kbd></td><td>复制本页文字(只含主体)</td></tr>
|
|
370
|
+
<tr><td><kbd>F</kbd></td><td>全屏放映(操作元素自动退场)</td></tr>
|
|
371
|
+
<tr><td><kbd>1</kbd>-<kbd>9</kbd></td><td>数字直达对应页</td></tr>
|
|
372
|
+
<tr><td><kbd>Home</kbd> / <kbd>End</kbd></td><td>首页 / 末页</td></tr>
|
|
373
|
+
<tr><td><kbd>Esc</kbd></td><td>关弹层 / 退全屏</td></tr>
|
|
374
|
+
</table>
|
|
375
|
+
<div class="help-note">工具栏色点=整份换主题;点「⭮ 仅本页」后色点只改当前页,再点恢复整份。截图与复制只含主体内容,绝不包含工具栏/页码/箭头等操作元素;「▦ 选页复制 / 选页截图」可勾选任意页批量执行,截图逐张下载 PNG。</div>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
<div class="help-mask" id="copyMask">
|
|
379
|
+
<div class="copy-panel">
|
|
380
|
+
<div class="cp-head">
|
|
381
|
+
<h3>选择要复制的页面</h3>
|
|
382
|
+
<label class="cp-all"><input type="checkbox" id="cpAll" checked>全选</label>
|
|
383
|
+
</div>
|
|
384
|
+
<div class="cp-list" id="cpList"></div>
|
|
385
|
+
<div class="cp-foot">
|
|
386
|
+
<button class="tbtn" id="cpCancel">取消</button>
|
|
387
|
+
<button class="tbtn primary" id="cpDo">复制所选</button>
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
<div class="help-mask" id="shotMask">
|
|
392
|
+
<div class="copy-panel">
|
|
393
|
+
<div class="cp-head">
|
|
394
|
+
<h3>选择要截图的页面</h3>
|
|
395
|
+
<label class="cp-all"><input type="checkbox" id="spAll" checked>全选</label>
|
|
396
|
+
</div>
|
|
397
|
+
<div class="cp-list" id="spList"></div>
|
|
398
|
+
<div class="cp-foot">
|
|
399
|
+
<button class="tbtn" id="spCancel">取消</button>
|
|
400
|
+
<button class="tbtn primary" id="spDo">截图所选</button>
|
|
401
|
+
</div>
|
|
402
|
+
<div class="help-note">逐张下载 PNG(2560×1440 两倍清晰度);浏览器首次会询问「下载多个文件」的许可,点允许即可。</div>
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
|
405
|
+
<script>
|
|
406
|
+
var slides = Array.prototype.slice.call(document.querySelectorAll('.slide'));
|
|
407
|
+
var N = slides.length;
|
|
408
|
+
var cur = 0, timer = null, playing = false;
|
|
409
|
+
var progFill = document.getElementById('progFill');
|
|
410
|
+
var pageNo = document.getElementById('pageNo');
|
|
411
|
+
var btnPlay = document.getElementById('btnPlay');
|
|
412
|
+
var btnScope = document.getElementById('btnScope');
|
|
413
|
+
var selInterval = document.getElementById('selInterval');
|
|
414
|
+
var chkLoop = document.getElementById('chkLoop');
|
|
415
|
+
var chkFromStart = document.getElementById('chkFromStart');
|
|
416
|
+
|
|
417
|
+
function go(i){
|
|
418
|
+
cur = Math.max(0, Math.min(N - 1, i));
|
|
419
|
+
slides.forEach(function(s, k){
|
|
420
|
+
s.classList.toggle('active', k === cur);
|
|
421
|
+
s.classList.toggle('dim', k !== cur);
|
|
422
|
+
});
|
|
423
|
+
progFill.style.width = ((cur + 1) / N * 100) + '%';
|
|
424
|
+
pageNo.innerHTML = '<b>' + (cur + 1) + '</b> / ' + N;
|
|
425
|
+
if (location.hash !== '#/' + (cur + 1)) history.replaceState(null, '', '#/' + (cur + 1));
|
|
426
|
+
}
|
|
427
|
+
function next(){ if (cur < N - 1) go(cur + 1); else if (chkLoop.checked) go(0); }
|
|
428
|
+
function prev(){ if (cur > 0) go(cur - 1); else if (chkLoop.checked) go(N - 1); }
|
|
429
|
+
function stopPlay(){ if (timer){ clearInterval(timer); timer = null; } playing = false; btnPlay.classList.remove('on'); btnPlay.textContent = '▶ 自动播放'; }
|
|
430
|
+
function startPlay(){
|
|
431
|
+
var sec = parseInt(selInterval.value, 10); if (!isFinite(sec) || sec < 1) sec = 5;
|
|
432
|
+
if (chkFromStart.checked) go(0);
|
|
433
|
+
stopPlay();
|
|
434
|
+
playing = true; btnPlay.classList.add('on'); btnPlay.textContent = '⏸ 暂停';
|
|
435
|
+
timer = setInterval(function(){
|
|
436
|
+
if (cur < N - 1) go(cur + 1); else if (chkLoop.checked) go(0); else stopPlay();
|
|
437
|
+
}, sec * 1000);
|
|
438
|
+
}
|
|
439
|
+
btnPlay.addEventListener('click', function(){ playing ? stopPlay() : startPlay(); });
|
|
440
|
+
|
|
441
|
+
/* ── 主题切换(整份 / 仅本页) ── */
|
|
442
|
+
function setGlobalTheme(name){
|
|
443
|
+
document.documentElement.setAttribute('data-theme', name);
|
|
444
|
+
slides.forEach(function(s){ s.removeAttribute('data-theme'); });
|
|
445
|
+
}
|
|
446
|
+
function setSlideTheme(name){
|
|
447
|
+
slides[cur].setAttribute('data-theme', name);
|
|
448
|
+
}
|
|
449
|
+
Array.prototype.slice.call(document.querySelectorAll('.tbtn.theme')).forEach(function(b){
|
|
450
|
+
b.addEventListener('click', function(){
|
|
451
|
+
btnScope.classList.contains('on') ? setSlideTheme(b.dataset.theme) : setGlobalTheme(b.dataset.theme);
|
|
452
|
+
Array.prototype.slice.call(document.querySelectorAll('.tbtn.theme')).forEach(function(x){ x.classList.remove('on'); });
|
|
453
|
+
b.classList.add('on');
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
btnScope.addEventListener('click', function(){
|
|
457
|
+
btnScope.classList.toggle('on');
|
|
458
|
+
btnScope.textContent = btnScope.classList.contains('on') ? '⭮ 本页模式' : '⭮ 仅本页';
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
/* ── 翻页 / 键盘 ── */
|
|
462
|
+
document.getElementById('btnPrev').addEventListener('click', prev);
|
|
463
|
+
document.getElementById('btnNext').addEventListener('click', next);
|
|
464
|
+
document.addEventListener('keydown', function(e){
|
|
465
|
+
if (e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT') return;
|
|
466
|
+
if (e.repeat) return;
|
|
467
|
+
switch (e.key) {
|
|
468
|
+
case 'ArrowRight': case 'ArrowDown': case 'PageDown': next(); e.preventDefault(); break;
|
|
469
|
+
case 'ArrowLeft': case 'ArrowUp': case 'PageUp': prev(); e.preventDefault(); break;
|
|
470
|
+
case ' ': next(); e.preventDefault(); break;
|
|
471
|
+
case 'Home': go(0); break;
|
|
472
|
+
case 'End': go(N - 1); break;
|
|
473
|
+
case 'p': case 'P': playing ? stopPlay() : startPlay(); break;
|
|
474
|
+
case 's': case 'S': shotPage(); break;
|
|
475
|
+
case 'c': case 'C': copyPage(); break;
|
|
476
|
+
case 'f': case 'F': toggleFull(); break;
|
|
477
|
+
case '?': helpMask.classList.add('open'); break;
|
|
478
|
+
case 'Escape': helpMask.classList.remove('open'); copyMask.classList.remove('open'); shotMask.classList.remove('open'); if (document.fullscreenElement) document.exitFullscreen(); break;
|
|
479
|
+
default:
|
|
480
|
+
if (/^[1-9]$/.test(e.key) && parseInt(e.key, 10) <= N) go(parseInt(e.key, 10) - 1);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
/* ── 页内连接 / 外部链接 / hash 定位 ── */
|
|
485
|
+
document.getElementById('deck').addEventListener('click', function(e){
|
|
486
|
+
var t = e.target.closest('[data-goto]');
|
|
487
|
+
if (t) go(parseInt(t.getAttribute('data-goto'), 10));
|
|
488
|
+
});
|
|
489
|
+
window.addEventListener('hashchange', function(){
|
|
490
|
+
var m = location.hash.match(/^#\/(\d+)$/);
|
|
491
|
+
if (m) go(parseInt(m[1], 10) - 1);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
/* ── 全屏 ── */
|
|
495
|
+
function toggleFull(){
|
|
496
|
+
if (document.fullscreenElement) document.exitFullscreen();
|
|
497
|
+
else document.documentElement.requestFullscreen();
|
|
498
|
+
}
|
|
499
|
+
document.getElementById('btnFull').addEventListener('click', toggleFull);
|
|
500
|
+
|
|
501
|
+
/* ── 复制本页(只含主体) ── */
|
|
502
|
+
function copyPage(){
|
|
503
|
+
var text = slides[cur].innerText.replace(/\n{2,}/g, '\n').trim();
|
|
504
|
+
var done = function(){ btnCopy.textContent = '✓ 已复制'; setTimeout(function(){ btnCopy.textContent = '⧉ 复制本页'; }, 1500); };
|
|
505
|
+
var btnCopy = document.getElementById('btnCopy');
|
|
506
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
507
|
+
navigator.clipboard.writeText(text).then(done).catch(function(){ fallbackCopy(text); done(); });
|
|
508
|
+
} else { fallbackCopy(text); done(); }
|
|
509
|
+
}
|
|
510
|
+
function fallbackCopy(text){
|
|
511
|
+
var ta = document.createElement('textarea');
|
|
512
|
+
ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0';
|
|
513
|
+
document.body.appendChild(ta); ta.select();
|
|
514
|
+
try { document.execCommand('copy'); } catch(e) {}
|
|
515
|
+
document.body.removeChild(ta);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/* ── 选页复制(默认全选=复制全部;只含各页主体文字) ── */
|
|
519
|
+
var copyMask = document.getElementById('copyMask');
|
|
520
|
+
var cpList = document.getElementById('cpList');
|
|
521
|
+
var cpAll = document.getElementById('cpAll');
|
|
522
|
+
var cpDo = document.getElementById('cpDo');
|
|
523
|
+
var btnCopyMulti = document.getElementById('btnCopyMulti');
|
|
524
|
+
function escapeHtml(t){ return t.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
|
525
|
+
function pagePreview(i){
|
|
526
|
+
var t = (slides[i].innerText || '').replace(/\n{2,}/g, '\n').trim();
|
|
527
|
+
var line = t.split('\n')[0] || '';
|
|
528
|
+
return line.length > 26 ? line.slice(0, 26) + '…' : line;
|
|
529
|
+
}
|
|
530
|
+
function openCopyPanel(){
|
|
531
|
+
var html = '';
|
|
532
|
+
for (var i = 0; i < N; i++) {
|
|
533
|
+
html += '<label class="cp-item"><input type="checkbox" class="cp-check" value="' + i + '" checked>'
|
|
534
|
+
+ '<span class="cp-no">第 ' + (i + 1) + ' 页</span>'
|
|
535
|
+
+ '<span class="cp-preview">' + escapeHtml(pagePreview(i)) + '</span></label>';
|
|
536
|
+
}
|
|
537
|
+
cpList.innerHTML = html;
|
|
538
|
+
cpAll.checked = true;
|
|
539
|
+
updateCpBtn();
|
|
540
|
+
closeShotPanel();
|
|
541
|
+
copyMask.classList.add('open');
|
|
542
|
+
}
|
|
543
|
+
function updateCpBtn(){
|
|
544
|
+
var n = copyMask.querySelectorAll('.cp-check:checked').length;
|
|
545
|
+
cpDo.textContent = n ? '复制所选(' + n + ' 页)' : '复制所选';
|
|
546
|
+
}
|
|
547
|
+
function closeCopyPanel(){ copyMask.classList.remove('open'); }
|
|
548
|
+
function copySelectedPages(){
|
|
549
|
+
var boxes = copyMask.querySelectorAll('.cp-check:checked');
|
|
550
|
+
var parts = [];
|
|
551
|
+
Array.prototype.forEach.call(boxes, function(box){
|
|
552
|
+
var i = parseInt(box.value, 10);
|
|
553
|
+
var text = slides[i].innerText.replace(/\n{2,}/g, '\n').trim();
|
|
554
|
+
parts.push('【第 ' + (i + 1) + ' 页】\n' + text);
|
|
555
|
+
});
|
|
556
|
+
if (parts.length === 0) return;
|
|
557
|
+
var all = parts.join('\n\n');
|
|
558
|
+
closeCopyPanel();
|
|
559
|
+
var done = function(){ btnCopyMulti.textContent = '✓ 已复制'; setTimeout(function(){ btnCopyMulti.textContent = '▦ 选页复制'; }, 1500); };
|
|
560
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
561
|
+
navigator.clipboard.writeText(all).then(done).catch(function(){ fallbackCopy(all); done(); });
|
|
562
|
+
} else { fallbackCopy(all); done(); }
|
|
563
|
+
}
|
|
564
|
+
btnCopyMulti.addEventListener('click', openCopyPanel);
|
|
565
|
+
document.getElementById('cpCancel').addEventListener('click', closeCopyPanel);
|
|
566
|
+
cpDo.addEventListener('click', copySelectedPages);
|
|
567
|
+
cpAll.addEventListener('change', function(){
|
|
568
|
+
var boxes = copyMask.querySelectorAll('.cp-check');
|
|
569
|
+
Array.prototype.forEach.call(boxes, function(box){ box.checked = cpAll.checked; });
|
|
570
|
+
updateCpBtn();
|
|
571
|
+
});
|
|
572
|
+
cpList.addEventListener('change', function(e){
|
|
573
|
+
if (e.target.classList.contains('cp-check')) {
|
|
574
|
+
var boxes = copyMask.querySelectorAll('.cp-check');
|
|
575
|
+
var allChecked = true;
|
|
576
|
+
Array.prototype.forEach.call(boxes, function(box){ if (!box.checked) allChecked = false; });
|
|
577
|
+
cpAll.checked = allChecked;
|
|
578
|
+
updateCpBtn();
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
copyMask.addEventListener('click', function(e){ if (e.target === copyMask) closeCopyPanel(); });
|
|
582
|
+
|
|
583
|
+
/* ── 截图(SVG foreignObject → Canvas → PNG 2x,只含主体):单页 + 选页批量共用 renderShot ── */
|
|
584
|
+
function renderShot(i, cb){
|
|
585
|
+
var clone = slides[i].cloneNode(true);
|
|
586
|
+
clone.classList.remove('dim');
|
|
587
|
+
clone.style.position = 'relative'; clone.style.inset = 'auto';
|
|
588
|
+
clone.style.padding = '64px 80px'; clone.style.display = 'flex';
|
|
589
|
+
clone.style.opacity = '1'; clone.style.visibility = 'visible';
|
|
590
|
+
clone.style.transform = 'none'; clone.style.transition = 'none';
|
|
591
|
+
var styleText = Array.prototype.slice.call(document.querySelectorAll('style')).map(function(x){ return x.textContent; }).join('\n');
|
|
592
|
+
var wrap = document.createElement('div');
|
|
593
|
+
wrap.setAttribute('data-theme', document.documentElement.getAttribute('data-theme') || 'tech');
|
|
594
|
+
wrap.style.width = '1280px'; wrap.style.height = '720px';
|
|
595
|
+
wrap.style.background = 'var(--bg)';
|
|
596
|
+
wrap.appendChild(clone);
|
|
597
|
+
var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="720" viewBox="0 0 1280 720">'
|
|
598
|
+
+ '<foreignObject width="1280" height="720">'
|
|
599
|
+
+ '<div xmlns="http://www.w3.org/1999/xhtml"><style>' + styleText + '</style>' + wrap.outerHTML + '</div>'
|
|
600
|
+
+ '</foreignObject></svg>';
|
|
601
|
+
var img = new Image();
|
|
602
|
+
img.onload = function(){
|
|
603
|
+
var c = document.createElement('canvas'); c.width = 2560; c.height = 1440;
|
|
604
|
+
var ctx = c.getContext('2d');
|
|
605
|
+
ctx.scale(2, 2); ctx.drawImage(img, 0, 0);
|
|
606
|
+
var a = document.createElement('a');
|
|
607
|
+
a.download = 'slide-' + (i + 1) + '-' + (document.documentElement.getAttribute('data-theme') || 'tech') + '.png';
|
|
608
|
+
a.href = c.toDataURL('image/png'); a.click();
|
|
609
|
+
if (cb) cb();
|
|
610
|
+
};
|
|
611
|
+
img.onerror = function(){ if (cb) cb(new Error('shot fail')); };
|
|
612
|
+
img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
|
|
613
|
+
}
|
|
614
|
+
function shotPage(){
|
|
615
|
+
var btnShot = document.getElementById('btnShot');
|
|
616
|
+
renderShot(cur, function(err){
|
|
617
|
+
if (err){ btnShot.textContent = '✗ 失败'; setTimeout(function(){ btnShot.textContent = '📷 截图本页'; }, 1500); }
|
|
618
|
+
else { btnShot.textContent = '✓ 已保存'; setTimeout(function(){ btnShot.textContent = '📷 截图本页'; }, 1500); }
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/* ── 选页截图(默认全选=逐张下载全部页 PNG,串行接力不挤占) ── */
|
|
623
|
+
var shotMask = document.getElementById('shotMask');
|
|
624
|
+
var spList = document.getElementById('spList');
|
|
625
|
+
var spAll = document.getElementById('spAll');
|
|
626
|
+
var spDo = document.getElementById('spDo');
|
|
627
|
+
var btnShotMulti = document.getElementById('btnShotMulti');
|
|
628
|
+
function openShotPanel(){
|
|
629
|
+
var html = '';
|
|
630
|
+
for (var i = 0; i < N; i++) {
|
|
631
|
+
html += '<label class="cp-item"><input type="checkbox" class="sp-check" value="' + i + '" checked>'
|
|
632
|
+
+ '<span class="cp-no">第 ' + (i + 1) + ' 页</span>'
|
|
633
|
+
+ '<span class="cp-preview">' + escapeHtml(pagePreview(i)) + '</span></label>';
|
|
634
|
+
}
|
|
635
|
+
spList.innerHTML = html;
|
|
636
|
+
spAll.checked = true;
|
|
637
|
+
updateSpBtn();
|
|
638
|
+
closeCopyPanel();
|
|
639
|
+
shotMask.classList.add('open');
|
|
640
|
+
}
|
|
641
|
+
function updateSpBtn(){
|
|
642
|
+
var n = shotMask.querySelectorAll('.sp-check:checked').length;
|
|
643
|
+
spDo.textContent = n ? '截图所选(' + n + ' 页)' : '截图所选';
|
|
644
|
+
}
|
|
645
|
+
function closeShotPanel(){ shotMask.classList.remove('open'); }
|
|
646
|
+
function shotSelectedPages(){
|
|
647
|
+
var boxes = shotMask.querySelectorAll('.sp-check:checked');
|
|
648
|
+
var idx = [];
|
|
649
|
+
Array.prototype.forEach.call(boxes, function(box){ idx.push(parseInt(box.value, 10)); });
|
|
650
|
+
if (idx.length === 0) return;
|
|
651
|
+
spDo.disabled = true;
|
|
652
|
+
var k = 0;
|
|
653
|
+
var step = function(){
|
|
654
|
+
if (k >= idx.length) {
|
|
655
|
+
closeShotPanel();
|
|
656
|
+
spDo.disabled = false;
|
|
657
|
+
btnShotMulti.textContent = '✓ 已保存 ' + idx.length + ' 页';
|
|
658
|
+
setTimeout(function(){ btnShotMulti.textContent = '▦ 选页截图'; }, 1500);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
spDo.textContent = '已保存 ' + k + '/' + idx.length + ' 页…';
|
|
662
|
+
renderShot(idx[k], step); k++;
|
|
663
|
+
};
|
|
664
|
+
step();
|
|
665
|
+
}
|
|
666
|
+
btnShotMulti.addEventListener('click', openShotPanel);
|
|
667
|
+
document.getElementById('spCancel').addEventListener('click', closeShotPanel);
|
|
668
|
+
spDo.addEventListener('click', shotSelectedPages);
|
|
669
|
+
spAll.addEventListener('change', function(){
|
|
670
|
+
var boxes = shotMask.querySelectorAll('.sp-check');
|
|
671
|
+
Array.prototype.forEach.call(boxes, function(box){ box.checked = spAll.checked; });
|
|
672
|
+
updateSpBtn();
|
|
673
|
+
});
|
|
674
|
+
spList.addEventListener('change', function(e){
|
|
675
|
+
if (e.target.classList.contains('sp-check')) {
|
|
676
|
+
var boxes = shotMask.querySelectorAll('.sp-check');
|
|
677
|
+
var allChecked = true;
|
|
678
|
+
Array.prototype.forEach.call(boxes, function(box){ if (!box.checked) allChecked = false; });
|
|
679
|
+
spAll.checked = allChecked;
|
|
680
|
+
updateSpBtn();
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
shotMask.addEventListener('click', function(e){ if (e.target === shotMask) closeShotPanel(); });
|
|
684
|
+
|
|
685
|
+
/* ── 帮助弹层 / 缩放 ── */
|
|
686
|
+
var helpMask = document.getElementById('helpMask');
|
|
687
|
+
document.getElementById('btnHelp').addEventListener('click', function(){ helpMask.classList.add('open'); });
|
|
688
|
+
helpMask.addEventListener('click', function(e){ if (e.target === helpMask) helpMask.classList.remove('open'); });
|
|
689
|
+
|
|
690
|
+
function fit(){
|
|
691
|
+
document.documentElement.style.setProperty('--sc', Math.min(window.innerWidth / 1280, window.innerHeight / 720));
|
|
692
|
+
}
|
|
693
|
+
window.addEventListener('resize', fit);
|
|
694
|
+
|
|
695
|
+
/* ── 初始化:?theme / ?shot / #/N ── */
|
|
696
|
+
var themeParam = new URLSearchParams(location.search).get('theme');
|
|
697
|
+
if (['tech','biz','mono','warm'].indexOf(themeParam) > -1) setGlobalTheme(themeParam);
|
|
698
|
+
var sm = location.hash.match(/^#\/(\d+)$/);
|
|
699
|
+
var shotParam = new URLSearchParams(location.search).get('shot');
|
|
700
|
+
var jumped = false;
|
|
701
|
+
if (sm){ go(parseInt(sm[1], 10) - 1); jumped = true; }
|
|
702
|
+
if (shotParam && shotParam !== 'all' && /^\d+$/.test(shotParam)){
|
|
703
|
+
var p = parseInt(shotParam, 10);
|
|
704
|
+
if (p >= 1 && p <= N){ go(p - 1); jumped = true; }
|
|
705
|
+
}
|
|
706
|
+
fit();
|
|
707
|
+
if (!jumped) go(0);
|
|
708
|
+
</script>
|
|
709
|
+
</body>
|
|
710
|
+
</html>
|