Update index.html
Browse files- index.html +59 -43
index.html
CHANGED
|
@@ -203,10 +203,27 @@
|
|
| 203 |
touch-action: none;
|
| 204 |
}
|
| 205 |
|
| 206 |
-
|
| 207 |
-
.board-card[data-cat="
|
| 208 |
-
|
| 209 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
|
| 211 |
.board-card::before {
|
| 212 |
content: '';
|
|
@@ -219,9 +236,13 @@
|
|
| 219 |
z-index: 2;
|
| 220 |
}
|
| 221 |
|
|
|
|
| 222 |
.board-card.highlight {
|
| 223 |
border: 1.8px solid #fbbf24;
|
| 224 |
-
box-shadow: 0 16px 40px rgba(251, 191, 36, 0.
|
|
|
|
|
|
|
|
|
|
| 225 |
}
|
| 226 |
.board-card.highlight::before {
|
| 227 |
background: #fbbf24;
|
|
@@ -262,7 +283,6 @@
|
|
| 262 |
}
|
| 263 |
.cat-icon svg { width: 20px; height: 20px; }
|
| 264 |
.board-card.highlight .cat-icon {
|
| 265 |
-
color: #fbbf24;
|
| 266 |
background: rgba(251, 191, 36, 0.16);
|
| 267 |
}
|
| 268 |
.card-title {
|
|
@@ -280,6 +300,7 @@
|
|
| 280 |
padding-left: 2px;
|
| 281 |
}
|
| 282 |
|
|
|
|
| 283 |
.card-center-metric {
|
| 284 |
flex: 1;
|
| 285 |
display: flex;
|
|
@@ -294,15 +315,13 @@
|
|
| 294 |
line-height: 1;
|
| 295 |
letter-spacing: -2px;
|
| 296 |
font-feature-settings: "tnum";
|
| 297 |
-
color: var(--
|
| 298 |
-
|
| 299 |
-
.board-card.highlight .metric-number {
|
| 300 |
-
color: #fbbf24;
|
| 301 |
-
text-shadow: 0 0 30px rgba(251, 191, 36, 0.4);
|
| 302 |
}
|
| 303 |
.metric-unit {
|
| 304 |
font-size: 1.2rem;
|
| 305 |
-
color: var(--
|
|
|
|
| 306 |
font-weight: 500;
|
| 307 |
}
|
| 308 |
|
|
@@ -324,9 +343,6 @@
|
|
| 324 |
border-radius: 999px;
|
| 325 |
transition: width 0.4s ease;
|
| 326 |
}
|
| 327 |
-
.board-card.highlight .progress-fill {
|
| 328 |
-
background: #fbbf24;
|
| 329 |
-
}
|
| 330 |
.metric-details {
|
| 331 |
display: flex;
|
| 332 |
justify-content: space-between;
|
|
@@ -601,7 +617,7 @@
|
|
| 601 |
|
| 602 |
<!-- 編輯狀態控制列 -->
|
| 603 |
<div class="edit-banner" id="editBanner">
|
| 604 |
-
<span style="font-size:0.85rem; color:#38bdf8;">✦ 自由排版中:拖曳卡片或
|
| 605 |
<button class="btn btn-secondary" onclick="randomizeQuote()">隨機金句</button>
|
| 606 |
<button class="btn btn-secondary" onclick="openAdminDrawer()">作息與設定</button>
|
| 607 |
<button class="btn btn-success" onclick="exitEditMode()">完成並鎖定</button>
|
|
@@ -653,10 +669,9 @@
|
|
| 653 |
</div>
|
| 654 |
|
| 655 |
<div class="drawer-content">
|
| 656 |
-
<!-- 固化預設值與資料備份核心 -->
|
| 657 |
<div class="setting-block" style="border: 1px solid #38bdf8;">
|
| 658 |
<h4>永久固化排版 (解決重開機重設問題)</h4>
|
| 659 |
-
<p style="font-size:0.8rem; color:#94a3b8;">排版
|
| 660 |
|
| 661 |
<div style="display:flex; flex-direction:column; gap:8px;">
|
| 662 |
<button class="btn btn-success" style="padding:10px; font-size:0.9rem;" onclick="exportAsHardcodedHtml()">⭐ 固化為全新預設檔 (下載 index.html)</button>
|
|
@@ -691,7 +706,7 @@
|
|
| 691 |
|
| 692 |
<div class="setting-block">
|
| 693 |
<div style="display:flex; justify-content:space-between; align-items:center;">
|
| 694 |
-
<h4>倒數項目資料庫</h4>
|
| 695 |
<button class="btn btn-primary" onclick="addNewEvent()">+ 新增卡片</button>
|
| 696 |
</div>
|
| 697 |
<div id="editorItemList" style="display:flex; flex-direction:column; gap:10px;"></div>
|
|
@@ -825,7 +840,6 @@
|
|
| 825 |
"16:00-16:45"
|
| 826 |
];
|
| 827 |
|
| 828 |
-
// 本地預設種子(可透過一鍵固化功能直接改寫)
|
| 829 |
/*START_DEFAULT_DATA*/
|
| 830 |
const DEFAULT_DATA = {
|
| 831 |
theme: "dark-slate",
|
|
@@ -845,9 +859,6 @@
|
|
| 845 |
let isClassBreak = false;
|
| 846 |
let currentPin = "";
|
| 847 |
|
| 848 |
-
// =========================================
|
| 849 |
-
// IndexedDB 持久化雙重快取架構
|
| 850 |
-
// =========================================
|
| 851 |
function openDB() {
|
| 852 |
return new Promise((resolve) => {
|
| 853 |
const req = indexedDB.open("ClassroomFocusDB", 1);
|
|
@@ -901,14 +912,10 @@
|
|
| 901 |
saveToIndexedDB(appState);
|
| 902 |
}
|
| 903 |
|
| 904 |
-
// =========================================
|
| 905 |
-
// 固化為全新預設檔 (下載即可徹底解決跑位)
|
| 906 |
-
// =========================================
|
| 907 |
function exportAsHardcodedHtml() {
|
| 908 |
let currentHtml = document.documentElement.outerHTML;
|
| 909 |
const targetDataString = JSON.stringify(appState, null, 2);
|
| 910 |
|
| 911 |
-
// 正則精準替換 DEFAULT_DATA 變數區塊
|
| 912 |
const regex = /\/\*START_DEFAULT_DATA\*\/[\s\S]*?\/\*END_DEFAULT_DATA\*\//;
|
| 913 |
const replacement = `/*START_DEFAULT_DATA*/\n const DEFAULT_DATA = ${targetDataString};\n /*END_DEFAULT_DATA*/`;
|
| 914 |
|
|
@@ -924,7 +931,7 @@
|
|
| 924 |
document.body.removeChild(a);
|
| 925 |
URL.revokeObjectURL(url);
|
| 926 |
|
| 927 |
-
alert("✓ 固化成功!
|
| 928 |
}
|
| 929 |
|
| 930 |
function openPinModal() {
|
|
@@ -1021,7 +1028,7 @@
|
|
| 1021 |
renderBoard();
|
| 1022 |
updateClock();
|
| 1023 |
closeAdminDrawer();
|
| 1024 |
-
alert("✓ 設定已成功匯入
|
| 1025 |
} else {
|
| 1026 |
alert("設定檔格式不符。");
|
| 1027 |
}
|
|
@@ -1202,6 +1209,7 @@
|
|
| 1202 |
});
|
| 1203 |
}
|
| 1204 |
|
|
|
|
| 1205 |
const canvas = document.getElementById("aurora-canvas");
|
| 1206 |
const ctx = canvas.getContext("2d");
|
| 1207 |
let animId = null;
|
|
@@ -1218,9 +1226,11 @@
|
|
| 1218 |
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| 1219 |
step += 0.008;
|
| 1220 |
|
| 1221 |
-
|
| 1222 |
-
const
|
| 1223 |
-
const
|
|
|
|
|
|
|
| 1224 |
|
| 1225 |
const colors = [
|
| 1226 |
{ r: 16, g: 185, b: 129, a: 0.18 },
|
|
@@ -1261,17 +1271,20 @@
|
|
| 1261 |
ctx.filter = "none";
|
| 1262 |
});
|
| 1263 |
|
| 1264 |
-
|
| 1265 |
-
|
| 1266 |
-
|
|
|
|
|
|
|
|
|
|
| 1267 |
radGrad.addColorStop(0.6, "rgba(56, 189, 248, 0.10)");
|
| 1268 |
radGrad.addColorStop(1, "rgba(0, 0, 0, 0)");
|
| 1269 |
|
| 1270 |
ctx.beginPath();
|
| 1271 |
-
ctx.arc(
|
| 1272 |
ctx.fillStyle = radGrad;
|
| 1273 |
ctx.fill();
|
| 1274 |
-
}
|
| 1275 |
|
| 1276 |
if (isClassBreak) {
|
| 1277 |
animId = requestAnimationFrame(renderAuroraWaves);
|
|
@@ -1362,14 +1375,14 @@
|
|
| 1362 |
<input type="date" value="${item.date}" onchange="appState.cards[${index}].date=this.value">
|
| 1363 |
</div>
|
| 1364 |
<div style="display:flex; justify-content:space-between; align-items:center;">
|
| 1365 |
-
<select onchange="appState.cards[${index}].cat=this.value">
|
| 1366 |
<option value="exam" ${item.cat==='exam'?'selected':''}>學業考試 (書本)</option>
|
| 1367 |
<option value="sports" ${item.cat==='sports'?'selected':''}>運動競賽 (獎盃)</option>
|
| 1368 |
<option value="event" ${item.cat==='event'?'selected':''}>校慶活動 (旗幟)</option>
|
| 1369 |
<option value="life" ${item.cat==='life'?'selected':''}>常態生活 (時鐘)</option>
|
| 1370 |
</select>
|
| 1371 |
-
<label style="font-size:0.8rem; color:#cbd5e1; display:flex; align-items:center; gap:4px;">
|
| 1372 |
-
<input type="checkbox" ${item.highlight ? 'checked':''} onchange="
|
| 1373 |
</label>
|
| 1374 |
<button class="btn btn-danger" style="padding:4px 8px;" onclick="removeCard(${index})">刪除</button>
|
| 1375 |
</div>
|
|
@@ -1378,9 +1391,10 @@
|
|
| 1378 |
});
|
| 1379 |
}
|
| 1380 |
|
| 1381 |
-
|
| 1382 |
-
|
| 1383 |
-
|
|
|
|
| 1384 |
}
|
| 1385 |
|
| 1386 |
function addNewEvent() {
|
|
@@ -1398,11 +1412,13 @@
|
|
| 1398 |
totalSpan: 100
|
| 1399 |
});
|
| 1400 |
renderDrawerEditor();
|
|
|
|
| 1401 |
}
|
| 1402 |
|
| 1403 |
function removeCard(idx) {
|
| 1404 |
appState.cards.splice(idx, 1);
|
| 1405 |
renderDrawerEditor();
|
|
|
|
| 1406 |
}
|
| 1407 |
|
| 1408 |
function saveAllSettings() {
|
|
|
|
| 203 |
touch-action: none;
|
| 204 |
}
|
| 205 |
|
| 206 |
+
/* 4 大分類的主題代表色 */
|
| 207 |
+
.board-card[data-cat="exam"] {
|
| 208 |
+
--theme-color: #38bdf8;
|
| 209 |
+
--num-color: #38bdf8;
|
| 210 |
+
--num-glow: rgba(56, 189, 248, 0.35);
|
| 211 |
+
}
|
| 212 |
+
.board-card[data-cat="sports"] {
|
| 213 |
+
--theme-color: #f97316;
|
| 214 |
+
--num-color: #fb923c;
|
| 215 |
+
--num-glow: rgba(249, 115, 22, 0.35);
|
| 216 |
+
}
|
| 217 |
+
.board-card[data-cat="event"] {
|
| 218 |
+
--theme-color: #ec4899;
|
| 219 |
+
--num-color: #f472b6;
|
| 220 |
+
--num-glow: rgba(236, 72, 153, 0.35);
|
| 221 |
+
}
|
| 222 |
+
.board-card[data-cat="life"] {
|
| 223 |
+
--theme-color: #10b981;
|
| 224 |
+
--num-color: #34d399;
|
| 225 |
+
--num-glow: rgba(16, 185, 129, 0.35);
|
| 226 |
+
}
|
| 227 |
|
| 228 |
.board-card::before {
|
| 229 |
content: '';
|
|
|
|
| 236 |
z-index: 2;
|
| 237 |
}
|
| 238 |
|
| 239 |
+
/* 重點高亮卡片:覆蓋為極致琥珀金強調色 */
|
| 240 |
.board-card.highlight {
|
| 241 |
border: 1.8px solid #fbbf24;
|
| 242 |
+
box-shadow: 0 16px 40px rgba(251, 191, 36, 0.28);
|
| 243 |
+
--theme-color: #fbbf24;
|
| 244 |
+
--num-color: #fbbf24;
|
| 245 |
+
--num-glow: rgba(251, 191, 36, 0.45);
|
| 246 |
}
|
| 247 |
.board-card.highlight::before {
|
| 248 |
background: #fbbf24;
|
|
|
|
| 283 |
}
|
| 284 |
.cat-icon svg { width: 20px; height: 20px; }
|
| 285 |
.board-card.highlight .cat-icon {
|
|
|
|
| 286 |
background: rgba(251, 191, 36, 0.16);
|
| 287 |
}
|
| 288 |
.card-title {
|
|
|
|
| 300 |
padding-left: 2px;
|
| 301 |
}
|
| 302 |
|
| 303 |
+
/* 數字文字色彩重新連動主題色 */
|
| 304 |
.card-center-metric {
|
| 305 |
flex: 1;
|
| 306 |
display: flex;
|
|
|
|
| 315 |
line-height: 1;
|
| 316 |
letter-spacing: -2px;
|
| 317 |
font-feature-settings: "tnum";
|
| 318 |
+
color: var(--num-color);
|
| 319 |
+
text-shadow: 0 0 24px var(--num-glow);
|
|
|
|
|
|
|
|
|
|
| 320 |
}
|
| 321 |
.metric-unit {
|
| 322 |
font-size: 1.2rem;
|
| 323 |
+
color: var(--theme-color);
|
| 324 |
+
opacity: 0.85;
|
| 325 |
font-weight: 500;
|
| 326 |
}
|
| 327 |
|
|
|
|
| 343 |
border-radius: 999px;
|
| 344 |
transition: width 0.4s ease;
|
| 345 |
}
|
|
|
|
|
|
|
|
|
|
| 346 |
.metric-details {
|
| 347 |
display: flex;
|
| 348 |
justify-content: space-between;
|
|
|
|
| 617 |
|
| 618 |
<!-- 編輯狀態控制列 -->
|
| 619 |
<div class="edit-banner" id="editBanner">
|
| 620 |
+
<span style="font-size:0.85rem; color:#38bdf8;">✦ 自由排版中:拖曳卡片或縮放邊角</span>
|
| 621 |
<button class="btn btn-secondary" onclick="randomizeQuote()">隨機金句</button>
|
| 622 |
<button class="btn btn-secondary" onclick="openAdminDrawer()">作息與設定</button>
|
| 623 |
<button class="btn btn-success" onclick="exitEditMode()">完成並鎖定</button>
|
|
|
|
| 669 |
</div>
|
| 670 |
|
| 671 |
<div class="drawer-content">
|
|
|
|
| 672 |
<div class="setting-block" style="border: 1px solid #38bdf8;">
|
| 673 |
<h4>永久固化排版 (解決重開機重設問題)</h4>
|
| 674 |
+
<p style="font-size:0.8rem; color:#94a3b8;">排版完成後,點擊下方「固化為全新預設檔」,下載覆蓋原本的 `index.html`,重開機保證 100% 永不跑位。</p>
|
| 675 |
|
| 676 |
<div style="display:flex; flex-direction:column; gap:8px;">
|
| 677 |
<button class="btn btn-success" style="padding:10px; font-size:0.9rem;" onclick="exportAsHardcodedHtml()">⭐ 固化為全新預設檔 (下載 index.html)</button>
|
|
|
|
| 706 |
|
| 707 |
<div class="setting-block">
|
| 708 |
<div style="display:flex; justify-content:space-between; align-items:center;">
|
| 709 |
+
<h4>倒數項目資料庫 (可複選多個高亮)</h4>
|
| 710 |
<button class="btn btn-primary" onclick="addNewEvent()">+ 新增卡片</button>
|
| 711 |
</div>
|
| 712 |
<div id="editorItemList" style="display:flex; flex-direction:column; gap:10px;"></div>
|
|
|
|
| 840 |
"16:00-16:45"
|
| 841 |
];
|
| 842 |
|
|
|
|
| 843 |
/*START_DEFAULT_DATA*/
|
| 844 |
const DEFAULT_DATA = {
|
| 845 |
theme: "dark-slate",
|
|
|
|
| 859 |
let isClassBreak = false;
|
| 860 |
let currentPin = "";
|
| 861 |
|
|
|
|
|
|
|
|
|
|
| 862 |
function openDB() {
|
| 863 |
return new Promise((resolve) => {
|
| 864 |
const req = indexedDB.open("ClassroomFocusDB", 1);
|
|
|
|
| 912 |
saveToIndexedDB(appState);
|
| 913 |
}
|
| 914 |
|
|
|
|
|
|
|
|
|
|
| 915 |
function exportAsHardcodedHtml() {
|
| 916 |
let currentHtml = document.documentElement.outerHTML;
|
| 917 |
const targetDataString = JSON.stringify(appState, null, 2);
|
| 918 |
|
|
|
|
| 919 |
const regex = /\/\*START_DEFAULT_DATA\*\/[\s\S]*?\/\*END_DEFAULT_DATA\*\//;
|
| 920 |
const replacement = `/*START_DEFAULT_DATA*/\n const DEFAULT_DATA = ${targetDataString};\n /*END_DEFAULT_DATA*/`;
|
| 921 |
|
|
|
|
| 931 |
document.body.removeChild(a);
|
| 932 |
URL.revokeObjectURL(url);
|
| 933 |
|
| 934 |
+
alert("✓ 固化成功!請用剛下載的 `index.html` 直接覆蓋 Lively 資料夾內的原檔,開機將永久預設此版面。");
|
| 935 |
}
|
| 936 |
|
| 937 |
function openPinModal() {
|
|
|
|
| 1028 |
renderBoard();
|
| 1029 |
updateClock();
|
| 1030 |
closeAdminDrawer();
|
| 1031 |
+
alert("✓ 設定已成功匯入!所有排版、顏色與重點標記已即刻生效。");
|
| 1032 |
} else {
|
| 1033 |
alert("設定檔格式不符。");
|
| 1034 |
}
|
|
|
|
| 1209 |
});
|
| 1210 |
}
|
| 1211 |
|
| 1212 |
+
// 極光引擎 (支援多重高亮焦點光暈)
|
| 1213 |
const canvas = document.getElementById("aurora-canvas");
|
| 1214 |
const ctx = canvas.getContext("2d");
|
| 1215 |
let animId = null;
|
|
|
|
| 1226 |
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| 1227 |
step += 0.008;
|
| 1228 |
|
| 1229 |
+
// 抓取所有被設為高亮的卡片
|
| 1230 |
+
const highlightCards = appState.cards.filter(c => c.highlight);
|
| 1231 |
+
const primaryHighlight = highlightCards[0];
|
| 1232 |
+
const focalX = primaryHighlight ? (primaryHighlight.x + primaryHighlight.w / 2) : canvas.width * 0.7;
|
| 1233 |
+
const focalY = primaryHighlight ? (primaryHighlight.y + primaryHighlight.h / 2) : canvas.height * 0.4;
|
| 1234 |
|
| 1235 |
const colors = [
|
| 1236 |
{ r: 16, g: 185, b: 129, a: 0.18 },
|
|
|
|
| 1271 |
ctx.filter = "none";
|
| 1272 |
});
|
| 1273 |
|
| 1274 |
+
// 為所有複選高亮的卡片同時繪製共振金色光暈
|
| 1275 |
+
highlightCards.forEach(c => {
|
| 1276 |
+
const cx = c.x + c.w / 2;
|
| 1277 |
+
const cy = c.y + c.h / 2;
|
| 1278 |
+
const radGrad = ctx.createRadialGradient(cx, cy, 30, cx, cy, c.w * 0.85);
|
| 1279 |
+
radGrad.addColorStop(0, "rgba(251, 191, 36, 0.20)");
|
| 1280 |
radGrad.addColorStop(0.6, "rgba(56, 189, 248, 0.10)");
|
| 1281 |
radGrad.addColorStop(1, "rgba(0, 0, 0, 0)");
|
| 1282 |
|
| 1283 |
ctx.beginPath();
|
| 1284 |
+
ctx.arc(cx, cy, c.w * 0.85, 0, Math.PI * 2);
|
| 1285 |
ctx.fillStyle = radGrad;
|
| 1286 |
ctx.fill();
|
| 1287 |
+
});
|
| 1288 |
|
| 1289 |
if (isClassBreak) {
|
| 1290 |
animId = requestAnimationFrame(renderAuroraWaves);
|
|
|
|
| 1375 |
<input type="date" value="${item.date}" onchange="appState.cards[${index}].date=this.value">
|
| 1376 |
</div>
|
| 1377 |
<div style="display:flex; justify-content:space-between; align-items:center;">
|
| 1378 |
+
<select onchange="appState.cards[${index}].cat=this.value; renderBoard();">
|
| 1379 |
<option value="exam" ${item.cat==='exam'?'selected':''}>學業考試 (書本)</option>
|
| 1380 |
<option value="sports" ${item.cat==='sports'?'selected':''}>運動競賽 (獎盃)</option>
|
| 1381 |
<option value="event" ${item.cat==='event'?'selected':''}>校慶活動 (旗幟)</option>
|
| 1382 |
<option value="life" ${item.cat==='life'?'selected':''}>常態生活 (時鐘)</option>
|
| 1383 |
</select>
|
| 1384 |
+
<label style="font-size:0.8rem; color:#cbd5e1; display:flex; align-items:center; gap:4px; cursor:pointer;">
|
| 1385 |
+
<input type="checkbox" ${item.highlight ? 'checked':''} onchange="toggleCardHighlight(${index}, this.checked)"> 重要高亮
|
| 1386 |
</label>
|
| 1387 |
<button class="btn btn-danger" style="padding:4px 8px;" onclick="removeCard(${index})">刪除</button>
|
| 1388 |
</div>
|
|
|
|
| 1391 |
});
|
| 1392 |
}
|
| 1393 |
|
| 1394 |
+
// 支援複選高亮
|
| 1395 |
+
function toggleCardHighlight(idx, isChecked) {
|
| 1396 |
+
appState.cards[idx].highlight = isChecked;
|
| 1397 |
+
renderBoard();
|
| 1398 |
}
|
| 1399 |
|
| 1400 |
function addNewEvent() {
|
|
|
|
| 1412 |
totalSpan: 100
|
| 1413 |
});
|
| 1414 |
renderDrawerEditor();
|
| 1415 |
+
renderBoard();
|
| 1416 |
}
|
| 1417 |
|
| 1418 |
function removeCard(idx) {
|
| 1419 |
appState.cards.splice(idx, 1);
|
| 1420 |
renderDrawerEditor();
|
| 1421 |
+
renderBoard();
|
| 1422 |
}
|
| 1423 |
|
| 1424 |
function saveAllSettings() {
|