dsh-ppt 0.4.5 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -67
- package/README.en.md +170 -168
- package/README.md +172 -170
- package/package.json +1 -1
- package/skills/dsh-ppt/scripts/deck-core.mjs +208 -75
|
@@ -171,6 +171,7 @@ export const LANGUAGES = {
|
|
|
171
171
|
presenterHint: '备注:未开演讲者窗口时按 S 显示在顶部;打开后只在演讲者窗口显示,观众看不到',
|
|
172
172
|
wrapHint: '已环绕到',
|
|
173
173
|
popupBlocked: '浏览器拦截了演讲者窗口,请允许弹出窗口后按 V 重试',
|
|
174
|
+
fullscreenUnavailable: '当前窗口无法进入全屏,请在浏览器中独立打开演示文件后重试。',
|
|
174
175
|
notesCloseLabel: '关闭备注',
|
|
175
176
|
overviewHint: '点击缩略图跳页,方向键切换,Enter 进入,Esc 退出'
|
|
176
177
|
},
|
|
@@ -206,6 +207,7 @@ export const LANGUAGES = {
|
|
|
206
207
|
presenterHint: 'Notes: S shows them on top until a presenter window is open; afterwards only in the presenter window.',
|
|
207
208
|
wrapHint: 'Wrapped to',
|
|
208
209
|
popupBlocked: 'The presenter window was blocked. Allow pop-ups and press V to retry.',
|
|
210
|
+
fullscreenUnavailable: 'Fullscreen is unavailable here. Open the presentation in a browser tab and try again.',
|
|
209
211
|
notesCloseLabel: 'Close notes',
|
|
210
212
|
overviewHint: 'Click a thumbnail to jump; arrows move, Enter opens, Esc exits'
|
|
211
213
|
},
|
|
@@ -241,6 +243,7 @@ export const LANGUAGES = {
|
|
|
241
243
|
presenterHint: '备注:未开演讲者窗口时按 S 显示在顶部;打开后只在演讲者窗口显示 · Notes: S shows on top until a presenter window is open',
|
|
242
244
|
wrapHint: '已环绕到 · Wrapped to',
|
|
243
245
|
popupBlocked: '浏览器拦截了演讲者窗口 · Presenter window blocked; allow pop-ups and press V',
|
|
246
|
+
fullscreenUnavailable: '当前窗口无法全屏,请独立打开演示文件 · Open this presentation in a browser tab for fullscreen.',
|
|
244
247
|
notesCloseLabel: '关闭备注 · Close notes',
|
|
245
248
|
overviewHint: '点击缩略图跳页 · Click to jump, arrows move, Enter opens, Esc exits'
|
|
246
249
|
},
|
|
@@ -1157,6 +1160,10 @@ body.notes-open #notes-panel{display:block}
|
|
|
1157
1160
|
padding:9px 18px;font-size:13px;
|
|
1158
1161
|
}
|
|
1159
1162
|
#toast[hidden]{display:none}
|
|
1163
|
+
#blank{position:fixed;inset:0;z-index:80;display:none;pointer-events:none}
|
|
1164
|
+
body.blank-black #blank{display:block;background:#000}
|
|
1165
|
+
body.blank-white #blank{display:block;background:#fff}
|
|
1166
|
+
body.blank-black #hud,body.blank-white #hud{visibility:hidden;pointer-events:none}
|
|
1160
1167
|
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
|
|
1161
1168
|
body.overview #stage{
|
|
1162
1169
|
display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:18px;
|
|
@@ -1239,11 +1246,12 @@ ${slides}
|
|
|
1239
1246
|
</div>
|
|
1240
1247
|
<div id="help-panel" hidden>
|
|
1241
1248
|
<div class="notes-label">${escapeHtml(ui.helpTitle)}</div>
|
|
1242
|
-
<p><kbd>←</kbd> <kbd>→</kbd> / <kbd>Space</kbd> 翻页 · <kbd>Home</kbd> / <kbd>End</kbd> 首末页 · <kbd>S</kbd> ${escapeHtml(ui.notesToggle)} · <kbd>V</kbd> ${escapeHtml(ui.presenterToggle)} · <kbd>G</kbd> ${escapeHtml(ui.overviewToggle)} · <kbd>F</kbd> 全屏 · <kbd>P</kbd> 打印 · <kbd>Esc</kbd> ${escapeHtml(ui.closeLabel)} · <kbd>?</kbd> ${escapeHtml(ui.helpTitle)}</p>
|
|
1249
|
+
<p><kbd>←</kbd> <kbd>→</kbd> / <kbd>Space</kbd> 翻页 · <kbd>Home</kbd> / <kbd>End</kbd> 首末页 · <kbd>S</kbd> ${escapeHtml(ui.notesToggle)} · <kbd>V</kbd> ${escapeHtml(ui.presenterToggle)} · <kbd>G</kbd> ${escapeHtml(ui.overviewToggle)} · <kbd>F</kbd> 全屏 · <kbd>P</kbd> 打印 · <kbd>B</kbd> 黑屏 · <kbd>W</kbd> 白屏 · <kbd>Esc</kbd> ${escapeHtml(ui.closeLabel)} · <kbd>?</kbd> ${escapeHtml(ui.helpTitle)}</p>
|
|
1243
1250
|
<p>${escapeHtml(ui.presenterHint)}</p>
|
|
1244
1251
|
<p>${escapeHtml(ui.overviewHint)}</p>
|
|
1245
1252
|
</div>
|
|
1246
1253
|
<div id="toast" role="status" hidden></div>
|
|
1254
|
+
<div id="blank" aria-hidden="true"></div>
|
|
1247
1255
|
<script>
|
|
1248
1256
|
(() => {
|
|
1249
1257
|
const frames = Array.from(document.querySelectorAll('.slide-frame'));
|
|
@@ -1262,6 +1270,7 @@ ${slides}
|
|
|
1262
1270
|
const toastEl = document.getElementById('toast');
|
|
1263
1271
|
let index = 0;
|
|
1264
1272
|
let presenterWin = null;
|
|
1273
|
+
let presenterInitSent = false;
|
|
1265
1274
|
let toastTimer = 0;
|
|
1266
1275
|
const total = slides.length;
|
|
1267
1276
|
const ui = ${JSON.stringify(ui).replace(/</g, '\\u003c')};
|
|
@@ -1292,13 +1301,40 @@ ${slides}
|
|
|
1292
1301
|
updatePresenter();
|
|
1293
1302
|
}
|
|
1294
1303
|
|
|
1295
|
-
function
|
|
1296
|
-
if (presenterOpen()
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1304
|
+
function sendInit() {
|
|
1305
|
+
if (!presenterOpen()) return;
|
|
1306
|
+
try {
|
|
1307
|
+
presenterWin.postMessage({
|
|
1308
|
+
__dshPpt: 'init',
|
|
1309
|
+
title: (document.title.split(' · ')[1] || document.title),
|
|
1310
|
+
css: document.querySelector('style') ? document.querySelector('style').textContent : '',
|
|
1311
|
+
total: total,
|
|
1312
|
+
slides: slides.map(function (slide) { return slide.outerHTML; }),
|
|
1313
|
+
}, '*');
|
|
1314
|
+
} catch (_) { /* popup closing */ }
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
function sendState() {
|
|
1318
|
+
if (!presenterOpen()) return;
|
|
1319
|
+
const next = (index + 1) % total;
|
|
1320
|
+
try {
|
|
1321
|
+
presenterWin.postMessage({
|
|
1322
|
+
__dshPpt: 'state',
|
|
1323
|
+
index: index,
|
|
1324
|
+
total: total,
|
|
1325
|
+
title: titleOf(index),
|
|
1326
|
+
nextTitle: titleOf(next),
|
|
1327
|
+
notes: notesOf(index),
|
|
1328
|
+
nextNotes: notesOf(next),
|
|
1329
|
+
blank: document.body.classList.contains('blank-black') ? 'black' : (document.body.classList.contains('blank-white') ? 'white' : null),
|
|
1330
|
+
}, '*');
|
|
1331
|
+
} catch (_) { /* popup closing */ }
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
function updatePresenter() { sendState(); }
|
|
1335
|
+
|
|
1301
1336
|
function go(next) {
|
|
1337
|
+
if (document.body.classList.contains('blank-black') || document.body.classList.contains('blank-white')) setBlank(null);
|
|
1302
1338
|
const wrapped = next < 0 || next >= total;
|
|
1303
1339
|
index = (next + total) % total;
|
|
1304
1340
|
slides.forEach(function (slide, i) {
|
|
@@ -1342,71 +1378,167 @@ ${slides}
|
|
|
1342
1378
|
}
|
|
1343
1379
|
}
|
|
1344
1380
|
|
|
1345
|
-
function presenterHtml() {
|
|
1346
|
-
const
|
|
1347
|
-
const
|
|
1348
|
-
const
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
+ '#
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1381
|
+
function presenterHtml() {
|
|
1382
|
+
const labelCurrent = ${JSON.stringify(ui.currentLabel)};
|
|
1383
|
+
const labelNext = ${JSON.stringify(ui.nextLabel)};
|
|
1384
|
+
const labelNotes = ${JSON.stringify(ui.notesLabel)};
|
|
1385
|
+
const noNotes = ${JSON.stringify(ui.noNotes)};
|
|
1386
|
+
const hintText = ${JSON.stringify('← → 翻页 · P 暂停 · T 重置 · +/- 字号 · B/W 黑/白屏 · Esc 结束')};
|
|
1387
|
+
const deckTitle = ${JSON.stringify(manifest.title).replace(/</g, '\\u003c')};
|
|
1388
|
+
const langAttr = ${JSON.stringify(lang.attr)};
|
|
1389
|
+
const cs = getComputedStyle(document.documentElement);
|
|
1390
|
+
const val = function (name, fallback) { const v = cs.getPropertyValue(name).trim(); return v || fallback; };
|
|
1391
|
+
const css = [
|
|
1392
|
+
':root{--bg:' + val('--bg', '#070B14') + ';--panel:' + val('--panel', '#0D1424') + ';--fg:' + val('--fg', '#E8F1FF') + ';--muted:' + val('--muted', '#7E8BA8') + ';--accent:' + val('--accent', '#7C3AED') + ';--accent2:' + val('--accent2', '#06B6D4') + ';--font-heading:' + val('--font-heading', 'sans-serif') + ';--font-body:' + val('--font-body', 'sans-serif') + '}',
|
|
1393
|
+
'*{box-sizing:border-box}',
|
|
1394
|
+
'body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--font-body);height:100vh;display:flex;flex-direction:column;overflow:hidden}',
|
|
1395
|
+
'header{display:flex;align-items:center;gap:14px;padding:9px 16px;border-bottom:1px solid color-mix(in srgb, var(--muted) 40%, transparent);font-size:13px;color:var(--muted)}',
|
|
1396
|
+
'header #ptitle{color:var(--fg);font-weight:700;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}',
|
|
1397
|
+
'header #pclock{font-variant-numeric:tabular-nums}',
|
|
1398
|
+
'main{flex:1;display:grid;grid-template-columns:1.12fr .88fr;gap:12px;padding:12px 16px;min-height:0}',
|
|
1399
|
+
'.col{display:flex;flex-direction:column;gap:10px;min-height:0}',
|
|
1400
|
+
'.card{background:var(--panel);border:1px solid color-mix(in srgb, var(--muted) 30%, transparent);border-radius:12px;padding:10px 12px;min-height:0;display:flex;flex-direction:column;gap:6px}',
|
|
1401
|
+
'.card-notes{flex:1}',
|
|
1402
|
+
'.lbl{color:var(--accent);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}',
|
|
1403
|
+
'.pv{position:relative;width:100%;aspect-ratio:16/9;overflow:hidden;border-radius:8px;background:var(--bg);border:1px solid color-mix(in srgb, var(--muted) 25%, transparent);--pv-scale:.3}',
|
|
1404
|
+
'.pv-slide{position:absolute;left:0;top:0;transform:translate(var(--pv-x,0px),var(--pv-y,0px)) scale(var(--pv-scale,.25));transform-origin:top left}',
|
|
1405
|
+
'.pv-slide .slide{display:flex !important;position:absolute;left:0;top:0;width:100vw;height:100vh;animation:none !important}',
|
|
1406
|
+
'.pv-slide .bullets li{opacity:1 !important;animation:none !important}',
|
|
1407
|
+
'#thumbs{display:flex;gap:8px;overflow-x:auto;padding-bottom:2px}',
|
|
1408
|
+
'#thumbs .thumb{position:relative;flex:0 0 128px;aspect-ratio:16/9;border-radius:8px;overflow:hidden;border:1px solid color-mix(in srgb, var(--muted) 35%, transparent);cursor:pointer;background:var(--bg);--pv-scale:.09}',
|
|
1409
|
+
'#thumbs .thumb.is-current{outline:2px solid var(--accent);outline-offset:1px}',
|
|
1410
|
+
'#pnotes{font-size:var(--notes-size,19px);line-height:1.6;white-space:pre-wrap;overflow:auto;flex:1}',
|
|
1411
|
+
'footer{display:flex;align-items:center;gap:8px;padding:9px 16px;border-top:1px solid color-mix(in srgb, var(--muted) 40%, transparent);flex-wrap:wrap}',
|
|
1412
|
+
'button{background:color-mix(in srgb, var(--panel) 90%, transparent);color:var(--fg);border:1px solid color-mix(in srgb, var(--muted) 45%, transparent);border-radius:99px;padding:6px 12px;min-height:34px;font:inherit;font-size:13px;cursor:pointer}',
|
|
1413
|
+
'button:hover{border-color:var(--accent);color:var(--accent)}',
|
|
1414
|
+
'button[aria-pressed="true"]{background:var(--accent);color:var(--bg);border-color:var(--accent)}',
|
|
1415
|
+
'#ptimer{font-variant-numeric:tabular-nums;font-size:20px;font-weight:700;color:var(--accent);margin-left:6px}',
|
|
1416
|
+
'.bar{flex:1;min-width:100px;height:8px;background:color-mix(in srgb, var(--muted) 28%, transparent);border-radius:99px;overflow:hidden}',
|
|
1417
|
+
'#pprog{height:100%;width:0;background:var(--accent);transition:width .2s}',
|
|
1418
|
+
'.hint{color:var(--muted);font-size:11px;width:100%}',
|
|
1419
|
+
].join('');
|
|
1420
|
+
const js = [
|
|
1421
|
+
'var total=0,index=0,elapsed=0,started=Date.now(),paused=false,notesSize=19,slides=[],cssText="",curBlank=null,initialized=false,readyTries=0;',
|
|
1422
|
+
'function q(id){return document.getElementById(id)}',
|
|
1423
|
+
'function post(m){try{if(window.opener)window.opener.postMessage(m,"*")}catch(e){}}',
|
|
1424
|
+
'function fmt(ms){var s=Math.max(0,Math.floor(ms/1000)),m=Math.floor(s/60);s=s%60;var h=Math.floor(m/60);m=m%60;return (h>0?(h<10?"0":"")+h+":":"")+(m<10?"0":"")+m+":"+(s<10?"0":"")+s}',
|
|
1425
|
+
'function clock(){var d=new Date();return ("0"+d.getHours()).slice(-2)+":"+("0"+d.getMinutes()).slice(-2)+":"+("0"+d.getSeconds()).slice(-2)}',
|
|
1426
|
+
'function tick(){if(!paused)elapsed=Date.now()-started;q("ptimer").textContent=fmt(elapsed);q("pclock").textContent=clock();if(window.requestAnimationFrame)requestAnimationFrame(tick);else setTimeout(tick,250)}',
|
|
1427
|
+
'function fitBox(box){var rw=box.clientWidth||320,rh=box.clientHeight||180;var s=Math.min(rw/window.innerWidth,rh/window.innerHeight);var ox=(rw-window.innerWidth*s)/2,oy=(rh-window.innerHeight*s)/2;box.style.setProperty("--pv-scale",String(s));box.style.setProperty("--pv-x",ox+"px");box.style.setProperty("--pv-y",oy+"px")}',
|
|
1428
|
+
'function fit(){fitBox(q("pv-cur"));fitBox(q("pv-next"));var th=q("thumbs").children;for(var i=0;i<th.length;i++){fitBox(th[i])}}',
|
|
1429
|
+
'function setSlide(box,html){box.innerHTML="";var f=document.createElement("div");f.className="pv-slide";f.innerHTML=html;box.appendChild(f)}',
|
|
1430
|
+
'function buildThumbs(){var box=q("thumbs");box.innerHTML="";slides.forEach(function(html,i){var t=document.createElement("div");t.className="thumb";var f=document.createElement("div");f.className="pv-slide";f.innerHTML=html;t.appendChild(f);t.addEventListener("click",function(){post({__dshPpt:"cmd",kind:"go",index:i})});box.appendChild(t)});fit()}',
|
|
1431
|
+
'function renderState(st){index=st.index;total=st.total||total;q("pcounter").textContent=(index+1)+" / "+total;setSlide(q("pv-cur"),slides[index]||"");setSlide(q("pv-next"),slides[(index+1)%total]||"");q("pnotes").textContent=st.notes||' + JSON.stringify(ui.noNotes) + ';q("pprog").style.width=((index+1)/total*100)+"%";curBlank=st.blank||null;q("pv-black").setAttribute("aria-pressed",curBlank==="black"?"true":"false");q("pv-white").setAttribute("aria-pressed",curBlank==="white"?"true":"false");q("pv-black").textContent=curBlank==="black"?"取消黑屏":"黑屏";q("pv-white").textContent=curBlank==="white"?"取消白屏":"白屏";var th=q("thumbs").children;for(var i=0;i<th.length;i++){th[i].classList.toggle("is-current",i===index)}fit()}',
|
|
1432
|
+
'function togglePause(){paused=!paused;if(!paused)started=Date.now()-elapsed;var b=q("pause");b.setAttribute("aria-pressed",paused?"true":"false");b.textContent=paused?"继续":"暂停"}',
|
|
1433
|
+
'function resetTimer(){started=Date.now();elapsed=0}',
|
|
1434
|
+
'function applyNotesSize(){document.documentElement.style.setProperty("--notes-size",notesSize+"px")}',
|
|
1435
|
+
'window.addEventListener("message",function(e){var d=e.data||{};if(d.__dshPpt==="init"&&!initialized){initialized=true;slides=d.slides||[];total=d.total||slides.length;cssText=d.css||"";var st=document.createElement("style");st.textContent=cssText;document.head.appendChild(st);q("ptitle").textContent=d.title||"";buildThumbs()}else if(d.__dshPpt==="state"){renderState(d)}else if(d.__dshPpt==="close"){window.close()}});',
|
|
1436
|
+
'window.addEventListener("resize",fit);',
|
|
1437
|
+
'window.addEventListener("beforeunload",function(){post({__dshPpt:"cmd",kind:"closed"})});',
|
|
1438
|
+
'document.addEventListener("keydown",function(e){var k=e.key;if(k==="ArrowRight"||k===" "){e.preventDefault();post({__dshPpt:"cmd",kind:"go",delta:1})}else if(k==="ArrowLeft"){e.preventDefault();post({__dshPpt:"cmd",kind:"go",delta:-1})}else if(k==="Escape"){post({__dshPpt:"cmd",kind:"end"});window.close()}else if(k.toLowerCase()==="p"){togglePause()}else if(k.toLowerCase()==="t"){resetTimer()}else if(k==="+"||k==="="){notesSize=Math.min(34,notesSize+2);applyNotesSize()}else if(k==="-"){notesSize=Math.max(12,notesSize-2);applyNotesSize()}else if(k.toLowerCase()==="b"){post({__dshPpt:"cmd",kind:"blank",color:curBlank==="black"?null:"black"})}else if(k.toLowerCase()==="w"){post({__dshPpt:"cmd",kind:"blank",color:curBlank==="white"?null:"white"})}});',
|
|
1439
|
+
'q("prev").addEventListener("click",function(){post({__dshPpt:"cmd",kind:"go",delta:-1})});',
|
|
1440
|
+
'q("next").addEventListener("click",function(){post({__dshPpt:"cmd",kind:"go",delta:1})});',
|
|
1441
|
+
'q("pause").addEventListener("click",togglePause);',
|
|
1442
|
+
'q("reset").addEventListener("click",resetTimer);',
|
|
1443
|
+
'q("font-plus").addEventListener("click",function(){notesSize=Math.min(34,notesSize+2);applyNotesSize()});',
|
|
1444
|
+
'q("font-minus").addEventListener("click",function(){notesSize=Math.max(12,notesSize-2);applyNotesSize()});',
|
|
1445
|
+
'q("pv-black").addEventListener("click",function(){post({__dshPpt:"cmd",kind:"blank",color:curBlank==="black"?null:"black"})});',
|
|
1446
|
+
'q("pv-white").addEventListener("click",function(){post({__dshPpt:"cmd",kind:"blank",color:curBlank==="white"?null:"white"})});',
|
|
1447
|
+
'q("end").addEventListener("click",function(){post({__dshPpt:"cmd",kind:"end"});window.close()});',
|
|
1448
|
+
'applyNotesSize();tick();function ready(){if(initialized)return;post({__dshPpt:"ready"});readyTries++;if(readyTries<5)setTimeout(ready,700)}setTimeout(ready,250);',
|
|
1449
|
+
].join('');
|
|
1450
|
+
return '<!DOCTYPE html><html lang="' + langAttr + '"><head><meta charset="UTF-8"><title>' + deckTitle + ' · 演讲者视图</title><style>' + css + '</style></head><body>'
|
|
1451
|
+
+ '<header><div id="ptitle"></div><div id="pcounter"></div><div id="pclock" title="本地时间"></div></header>'
|
|
1452
|
+
+ '<main><div class="col">'
|
|
1453
|
+
+ '<div class="card"><div class="lbl">' + labelCurrent + '</div><div class="pv" id="pv-cur"></div></div>'
|
|
1454
|
+
+ '<div class="card"><div class="lbl">' + labelNext + '</div><div class="pv" id="pv-next"></div></div>'
|
|
1455
|
+
+ '<div class="card"><div class="lbl">SLIDES</div><div id="thumbs"></div></div>'
|
|
1456
|
+
+ '</div><div class="col"><div class="card card-notes"><div class="lbl">' + labelNotes + '</div><div id="pnotes"></div></div></div></main>'
|
|
1457
|
+
+ '<footer><button id="prev" title="上一页 (←)">◀</button><button id="next" title="下一页 (→)">▶</button><button id="pause" title="暂停 (P)" aria-pressed="false">暂停</button><button id="reset" title="重置计时 (T)">重置计时</button><button id="font-minus" title="备注字号减小 (-)">A−</button><button id="font-plus" title="备注字号增大 (+)">A+</button><button id="pv-black" title="黑屏 (B)">黑屏</button><button id="pv-white" title="白屏 (W)">白屏</button><button id="end" title="结束放映 (Esc)">结束放映</button><div id="ptimer">00:00</div><div class="bar"><div id="pprog"></div></div><div class="hint">' + hintText + '</div></footer>'
|
|
1458
|
+
+ '<scr' + 'ipt>' + js + '</scr' + 'ipt></body></html>';
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
async function toggleFullscreen() {
|
|
1391
1462
|
try {
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
function closePresenter() {
|
|
1402
|
-
if (presenterOpen()) { try { presenterWin.close(); } catch (_) { /* already gone */ } }
|
|
1403
|
-
document.body.classList.remove('presenting');
|
|
1463
|
+
if (document.fullscreenElement) { await document.exitFullscreen?.(); return; }
|
|
1464
|
+
if (!document.documentElement.requestFullscreen || document.fullscreenEnabled === false) {
|
|
1465
|
+
toast(ui.fullscreenUnavailable);
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
await document.documentElement.requestFullscreen();
|
|
1469
|
+
if (!document.fullscreenElement) toast(ui.fullscreenUnavailable);
|
|
1470
|
+
} catch (_) { toast(ui.fullscreenUnavailable); }
|
|
1404
1471
|
}
|
|
1405
1472
|
|
|
1406
|
-
|
|
1407
|
-
if (
|
|
1408
|
-
|
|
1409
|
-
|
|
1473
|
+
function openPresenter() {
|
|
1474
|
+
if (presenterOpen()) { try { presenterWin.focus(); } catch (_) { /* gone */ } return; }
|
|
1475
|
+
let url = '';
|
|
1476
|
+
try {
|
|
1477
|
+
url = URL.createObjectURL(new Blob([presenterHtml()], { type: 'text/html' }));
|
|
1478
|
+
} catch (_) { toast(ui.popupBlocked); return; }
|
|
1479
|
+
presenterInitSent = false;
|
|
1480
|
+
presenterWin = window.open(url, 'dsh-ppt-presenter', 'width=1180,height=760');
|
|
1481
|
+
if (!presenterWin) {
|
|
1482
|
+
try { URL.revokeObjectURL(url); } catch (_) { /* ignore */ }
|
|
1483
|
+
toast(ui.popupBlocked);
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
document.body.classList.add('presenting');
|
|
1487
|
+
if (document.activeElement && typeof document.activeElement.blur === 'function') document.activeElement.blur();
|
|
1488
|
+
setTimeout(function () { try { URL.revokeObjectURL(url); } catch (_) { /* ignore */ } }, 5000);
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
function closePresenter() {
|
|
1492
|
+
if (presenterOpen()) {
|
|
1493
|
+
try { presenterWin.postMessage({ __dshPpt: 'close' }, '*'); presenterWin.close(); } catch (_) { /* gone */ }
|
|
1494
|
+
}
|
|
1495
|
+
document.body.classList.remove('presenting');
|
|
1496
|
+
presenterInitSent = false;
|
|
1497
|
+
if (document.fullscreenElement) { try { document.exitFullscreen?.(); } catch (_) { /* ignore */ } }
|
|
1498
|
+
setBlank(null);
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
function setBlank(color) {
|
|
1502
|
+
document.body.classList.remove('blank-black', 'blank-white');
|
|
1503
|
+
if (color === 'black') document.body.classList.add('blank-black');
|
|
1504
|
+
else if (color === 'white') document.body.classList.add('blank-white');
|
|
1505
|
+
sendState();
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
setInterval(function () {
|
|
1509
|
+
if (!presenterOpen() && document.body.classList.contains('presenting')) {
|
|
1510
|
+
document.body.classList.remove('presenting');
|
|
1511
|
+
if (document.fullscreenElement) { try { document.exitFullscreen?.(); } catch (_) { /* ignore */ } }
|
|
1512
|
+
setBlank(null);
|
|
1513
|
+
}
|
|
1514
|
+
}, 800);
|
|
1515
|
+
|
|
1516
|
+
window.addEventListener('message', function (event) {
|
|
1517
|
+
if (event.source !== presenterWin) return;
|
|
1518
|
+
const data = event.data || {};
|
|
1519
|
+
if (data.__dshPpt === 'cmd' && (data.kind === 'end' || data.kind === 'closed')) { closePresenter(); return; }
|
|
1520
|
+
if (!presenterOpen()) return;
|
|
1521
|
+
if (data.__dshPpt === 'ready') {
|
|
1522
|
+
if (!presenterInitSent) { presenterInitSent = true; sendInit(); }
|
|
1523
|
+
sendState();
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
if (data.__dshPpt !== 'cmd') return;
|
|
1527
|
+
if (data.kind === 'go') {
|
|
1528
|
+
setBlank(null);
|
|
1529
|
+
if (typeof data.index === 'number') go(data.index);
|
|
1530
|
+
else go(index + (data.delta || 0));
|
|
1531
|
+
sendState();
|
|
1532
|
+
} else if (data.kind === 'blank') {
|
|
1533
|
+
setBlank(data.color || null);
|
|
1534
|
+
} else if (data.kind === 'end') {
|
|
1535
|
+
closePresenter();
|
|
1536
|
+
} else if (data.kind === 'closed') {
|
|
1537
|
+
document.body.classList.remove('presenting');
|
|
1538
|
+
setBlank(null);
|
|
1539
|
+
}
|
|
1540
|
+
});
|
|
1541
|
+
|
|
1410
1542
|
document.addEventListener('keydown', (event) => {
|
|
1411
1543
|
const key = event.key;
|
|
1412
1544
|
const lower = key.toLowerCase();
|
|
@@ -1417,6 +1549,7 @@ ${slides}
|
|
|
1417
1549
|
if (!helpPanel.hidden) { helpPanel.hidden = true; return; }
|
|
1418
1550
|
if (document.body.classList.contains('overview')) { setOverview(false); return; }
|
|
1419
1551
|
if (notesOpen()) { setNotes(false); return; }
|
|
1552
|
+
if (document.body.classList.contains('presenting') || document.body.classList.contains('blank-black') || document.body.classList.contains('blank-white')) { closePresenter(); return; }
|
|
1420
1553
|
if (document.fullscreenElement) { document.exitFullscreen?.(); return; }
|
|
1421
1554
|
return;
|
|
1422
1555
|
}
|
|
@@ -1435,8 +1568,7 @@ ${slides}
|
|
|
1435
1568
|
} else if (key === 'Enter' && inOverview) {
|
|
1436
1569
|
event.preventDefault(); setOverview(false);
|
|
1437
1570
|
} else if (lower === 'f') {
|
|
1438
|
-
|
|
1439
|
-
else document.exitFullscreen?.();
|
|
1571
|
+
void toggleFullscreen();
|
|
1440
1572
|
} else if (lower === 'g') {
|
|
1441
1573
|
setOverview(!inOverview);
|
|
1442
1574
|
} else if (lower === 's') {
|
|
@@ -1445,6 +1577,10 @@ ${slides}
|
|
|
1445
1577
|
} else if (lower === 'v') {
|
|
1446
1578
|
if (presenterOpen()) closePresenter();
|
|
1447
1579
|
else openPresenter();
|
|
1580
|
+
} else if (lower === 'b') {
|
|
1581
|
+
setBlank(document.body.classList.contains('blank-black') ? null : 'black');
|
|
1582
|
+
} else if (lower === 'w') {
|
|
1583
|
+
setBlank(document.body.classList.contains('blank-white') ? null : 'white');
|
|
1448
1584
|
} else if (lower === 'p') {
|
|
1449
1585
|
window.print();
|
|
1450
1586
|
} else if (key === '?') {
|
|
@@ -1467,10 +1603,7 @@ ${slides}
|
|
|
1467
1603
|
if (Math.abs(delta) > 48 && !document.body.classList.contains('overview')) go(index + (delta < 0 ? 1 : -1));
|
|
1468
1604
|
}, { passive: true });
|
|
1469
1605
|
|
|
1470
|
-
fullscreenBtn.addEventListener('click', () => {
|
|
1471
|
-
if (!document.fullscreenElement) document.documentElement.requestFullscreen?.();
|
|
1472
|
-
else document.exitFullscreen?.();
|
|
1473
|
-
});
|
|
1606
|
+
fullscreenBtn.addEventListener('click', () => { void toggleFullscreen(); });
|
|
1474
1607
|
notesBtn.addEventListener('click', () => {
|
|
1475
1608
|
if (document.body.classList.contains('presenting')) { toast(ui.presenterHint); return; }
|
|
1476
1609
|
setNotes(!notesOpen());
|