v0.5.1: Ohli24 레이아웃 표준화 및 Anilife 폴백 체인 개선

This commit is contained in:
2026-01-04 01:00:17 +09:00
parent 97310ac900
commit 914277c2bc
14 changed files with 593 additions and 294 deletions

View File

@@ -22,7 +22,7 @@
}
</style>
<div id="yommi_wrapper" class="container-fluid mt-2 mt-md-4 mx-auto content-cloak" style="max-width: 100%;">
<div id="yommi_wrapper" class="container-fluid mt-2 mt-md-4 mx-auto anilife-common-wrapper content-cloak">
<div id="preloader" class="loader">
<div class="loader-inner">
<div class="loader-line-wrap"><div class="loader-line"></div></div>

View File

@@ -4,8 +4,21 @@
<link rel="stylesheet" href="{{ url_for('.static', filename='css/' ~ arg['sub'] ~ '.css') }}"/>
<div class="content-cloak ohli24-list-page">
<form id="form_search" class="form-inline" style="text-align:left; width:100%;">
<div id="ohli24_list_wrapper" class="ohli24-common-wrapper container-fluid mt-4 content-cloak ohli24-list-page">
<div class="glass-card p-4">
<div class="ohli24-header">
<div class="ohli24-header-left">
<div class="ohli24-icon-box">
<i class="bi bi-collection-play-fill text-primary" style="font-size: 1.5rem;"></i>
</div>
<div>
<h3 class="ohli24-header-title">다운로드 목록</h3>
<span class="ohli24-header-subtitle">성공적으로 완료된 에피소드 라이브러리</span>
</div>
</div>
</div>
<hr style="border-color: rgba(255,255,255,0.05); margin-bottom: 25px;">
<form id="form_search" class="form-inline mb-4" style="text-align:left; width:100%;">
<div class="search-container">
<div class="search-group-left">
<select id="order" name="order" class="form-control custom-select">
@@ -24,16 +37,11 @@
</div>
</div>
</form>
<div id='page1'></div>
{# {{ macros.m_hr_head_top() }} #}
{# {{ macros.m_row_start('0') }} #}
{# {{ macros.m_col(2, macros.m_strong('Poster')) }} #}
{# {{ macros.m_col(10, macros.m_strong('Info')) }} #}
{# {{ macros.m_row_end() }} #}
{# {{ macros.m_hr_head_bottom() }} #}
<div id='page1' class="mb-3"></div>
<div id="list_div"></div>
<div id='page2'></div>
<div id='page2' class="mt-3"></div>
</div>
</div>
<!-- Custom Confirmation Modal -->
<div class="modal fade" id="confirmModal" tabindex="-1" role="dialog" aria-hidden="true">
@@ -120,6 +128,14 @@
var current_data = null;
$(document).ready(function () {
// Force parent container to be fluid to allow full width
$("#main_container").removeClass("container").addClass("container-fluid");
// Smooth Load Trigger
setTimeout(function() {
$('.content-cloak, #menu_module_div, #menu_page_div').addClass('visible');
}, 100);
global_sub_request_search('1');
});

View File

@@ -4,46 +4,232 @@
<link rel="stylesheet" href="{{ url_for('.static', filename='css/' ~ arg['sub'] ~ '.css') }}"/>
<div class="queue-container content-cloak ohli24-queue-page">
<!-- 헤더 버튼 그룹 -->
<div class="queue-header">
<div class="header-title">
<i class="fa fa-download"></i>
<span>다운로드 큐</span>
<span class="queue-count" id="queue_count">0</span>
<div id="ohli24_queue_wrapper" class="ohli24-common-wrapper container-fluid mt-4 content-cloak ohli24-queue-page">
<div class="glass-card p-4">
<!-- 헤더 버튼 그룹 -->
<div class="ohli24-header">
<div class="ohli24-header-left">
<div class="ohli24-icon-box">
<i class="bi bi-cloud-download-fill text-primary" style="font-size: 1.5rem;"></i>
</div>
<div>
<h3 class="ohli24-header-title">다운로드 큐</h3>
<span class="ohli24-header-subtitle"><span id="queue_count" class="text-info font-weight-bold">0</span>개의 항목</span>
</div>
</div>
<div class="ohli24-header-right d-flex flex-wrap">
<button id="reset_btn" class="btn-modern btn-modern-danger mr-2 mb-2">
<i class="bi bi-arrow-counterclockwise mr-1"></i> 전체 초기화
</button>
<button id="delete_completed_btn" class="btn-modern btn-modern-warning mr-2 mb-2">
<i class="bi bi-trash mr-1"></i> 완료 항목 삭제
</button>
</div>
</div>
<div class="header-buttons">
<button id="reset_btn" class="queue-btn queue-btn-danger">
<i class="fa fa-refresh"></i> 초기화
</button>
<button id="delete_completed_btn" class="queue-btn queue-btn-warning">
<i class="fa fa-trash"></i> 완료 삭제
</button>
<button id="go_ffmpeg_btn" class="queue-btn queue-btn-info">
<i class="fa fa-external-link"></i> FFMPEG
</button>
<!-- 다운로드 목록 -->
<div id="download_list_div" class="queue-list d-flex flex-column gap-3"></div>
<!-- 빈 상태 표시 -->
<div id="empty_state" class="empty-state d-flex flex-column align-items-center justify-content-center py-5" style="display: none; min-height: 300px;">
<div class="empty-icon-wrapper mb-4">
<i class="bi bi-inbox-fill text-muted opacity-20" style="font-size: 5rem;"></i>
</div>
<h4 class="text-white-50 font-weight-bold mb-2">다운로드 대기 항목 없음</h4>
<p class="text-muted mb-0">새로운 에피소드를 추가하면 이곳에 실시간으로 나타납니다.</p>
<div class="mt-4">
<span class="badge badge-pill badge-dark py-2 px-3" style="background: rgba(255,255,255,0.05); color: #64748b; font-weight: 500;">
<i class="bi bi-info-circle mr-1"></i> 에피소드 목록에서 '다운로드'를 눌러보세요
</span>
</div>
</div>
</div>
<!-- 다운로드 목록 -->
<div id="download_list_div" class="queue-list"></div>
<!-- 빈 상태 표시 -->
<div id="empty_state" class="empty-state" style="display: none;">
<i class="fa fa-inbox"></i>
<p>다운로드 대기 중인 항목이 없습니다</p>
</div>
</div>
<style>
/* Queue specific tweaks */
#download_list_div { display: flex; flex-direction: column; gap: 10px; width: 100%; }
.queue-item { margin-bottom: 5px; }
@media (max-width: 768px) {
.queue-item { padding: 10px; }
.item-info { flex: 1; }
.progress-wrapper { height: 24px; border-radius: 12px; }
.progress-label { font-size: 11px; }
/* Premium Glassmorphism UI */
.ohli24-queue-page {
animation: fadeIn 0.5s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.icon-box {
width: 48px;
height: 48px;
background: rgba(59, 130, 246, 0.1);
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(59, 130, 246, 0.2);
}
/* Modern Buttons */
.btn-modern {
border: none;
border-radius: 12px;
padding: 10px 18px;
font-weight: 600;
font-size: 0.9rem;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: inline-flex;
align-items: center;
justify-content: center;
color: white;
text-transform: none;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.btn-modern:hover {
transform: translateY(-2px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.2);
filter: brightness(1.1);
}
.btn-modern:active {
transform: translateY(0);
}
.btn-modern-danger { background: linear-gradient(135deg, #f43f5e 0%, #e11d48 100%); }
.btn-modern-warning { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }
.btn-modern-info { background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%); }
.btn-modern-secondary { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); }
/* Queue Items */
.queue-item {
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 16px;
padding: 16px;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 15px;
}
.queue-item:hover {
background: rgba(255, 255, 255, 0.03);
border-color: rgba(255, 255, 255, 0.1);
transform: translateX(4px);
}
.item-number {
font-family: 'JetBrains Mono', monospace;
font-weight: 700;
color: #64748b;
font-size: 1.1rem;
min-width: 40px;
}
.item-info {
flex: 1;
min-width: 0;
}
.item-filename {
color: #f1f5f9;
font-weight: 600;
font-size: 0.95rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 4px;
}
.item-meta {
font-size: 0.8rem;
color: #94a3b8;
}
/* Modern Progress Bar */
.progress-container {
flex: 1.5;
min-width: 150px;
}
.progress-wrapper {
position: relative;
height: 28px;
background: rgba(0, 0, 0, 0.3);
border-radius: 14px;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.05);
}
.progress-bar {
height: 100%;
border-radius: 14px;
transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.3s ease;
}
.status-waiting { background: linear-gradient(90deg, #64748b, #475569); }
.status-downloading {
background: linear-gradient(90deg, #3b82f6, #2563eb);
box-shadow: 0 0 10px rgba(59, 130, 246, 0.3);
}
.status-completed {
background: linear-gradient(90deg, #10b981, #059669);
box-shadow: 0 0 10px rgba(16, 185, 129, 0.3);
}
.status-failed { background: linear-gradient(90deg, #ef4444, #dc2626); }
.progress-label {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 700;
color: white;
text-shadow: 0 1px 2px rgba(0,0,0,0.5);
pointer-events: none;
padding: 0 10px;
white-space: nowrap;
overflow: hidden;
}
/* Actions */
.cancel-btn {
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.2);
color: #f87171;
padding: 6px 12px;
border-radius: 8px;
font-size: 0.8rem;
font-weight: 600;
transition: all 0.2s;
cursor: pointer;
}
.cancel-btn:hover {
background: #ef4444;
color: white;
}
@media (max-width: 992px) {
.queue-item { flex-direction: column; align-items: stretch; }
.progress-container { flex: none; width: 100%; }
.item-actions { display: flex; justify-content: flex-end; }
}
.empty-state {
animation: fadeIn 0.8s ease-out;
}
.empty-icon-wrapper i {
display: inline-block;
transition: transform 0.3s ease;
}
.empty-state:hover .empty-icon-wrapper i {
transform: translateY(-10px);
color: #3b82f6 !important;
opacity: 0.4 !important;
}
</style>
@@ -54,6 +240,14 @@
var current_data = null;
$(document).ready(function () {
// Force parent container to be fluid to allow full width
$("#main_container").removeClass("container").addClass("container-fluid");
// Smooth Load Trigger
setTimeout(function() {
$('.content-cloak, #menu_module_div, #menu_page_div').addClass('visible');
}, 100);
var socket = io.connect(window.location.href);
socket.on('on_start', (data) => {})
socket.on('start', function (data) {
@@ -128,11 +322,23 @@
var progressBar = document.getElementById("progress_" + entity_id);
if (progressBar != null) {
// Update percentage and label
progressBar.style.width = percent + '%';
var label = status_kor;
if (percent != 0) label += " (" + percent + "%)";
if (speed) label += " " + speed;
document.getElementById("progress_" + entity_id + "_label").innerHTML = label;
// Real-time status class (color) update
var statusClass = getStatusClass(status_kor);
$(progressBar).removeClass('status-waiting status-downloading status-completed status-failed').addClass(statusClass);
// Auto-refresh list if completed to show 'Watch' button or other actions
if (status_kor === '완료' || status_kor === 'completed') {
// Throttle refresh to avoid flickering if multiple complete
if (window.statusRefreshTimeout) clearTimeout(window.statusRefreshTimeout);
window.statusRefreshTimeout = setTimeout(on_start, 1000);
}
}
}

View File

@@ -23,9 +23,21 @@
<!-- </div>-->
</div>
</div>
<div id="yommi_wrapper" class="container-fluid ohli24-request-page mt-2 mt-md-4 mx-auto" style="max-width: 100%;">
<div id="ohli24_request_wrapper" class="ohli24-common-wrapper container-fluid mt-4 content-cloak ohli24-request-page">
<form id="program_list">
<div class="card p-2 p-md-4 mb-2 mb-md-4 border-light" style="background: rgba(30,30,40,0.6); backdrop-filter: blur(10px); box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
<div class="glass-card p-4 mb-4">
<div class="ohli24-header">
<div class="ohli24-header-left">
<div class="ohli24-icon-box">
<i class="bi bi-play-circle-fill text-primary" style="font-size: 1.5rem;"></i>
</div>
<div>
<h3 class="ohli24-header-title">Ohli24 요청</h3>
<span class="ohli24-header-subtitle">작품 분석 및 개별 에피소드 다운로드</span>
</div>
</div>
</div>
<hr style="border-color: rgba(255,255,255,0.05); margin-bottom: 25px;">
<div class="form-group mb-0">
<label for="code" class="text-white font-weight-bold mb-2"><i class="fa fa-search mr-2"></i>작품 Code / 제목</label>
<div class="input-group input-group-lg">
@@ -126,7 +138,7 @@
let str = "";
// 1. Info Card (Top)
str += `<div class='card p-2 p-md-4 mb-3 mb-md-4 border-light' style="background: rgba(30,30,40,0.6); backdrop-filter: blur(10px);">`;
str += `<div class='glass-card p-4 mb-4'>`;
str += `<div class="row">`;
// Poster
@@ -224,8 +236,13 @@
}
str += '</div>';
// 먼저 HTML을 삽입하고 레이아웃이 완료된 후 bootstrapToggle 적용
document.getElementById("episode_list").innerHTML = str;
$('input[id^="checkbox_"]').bootstrapToggle()
// 다음 프레임에서 bootstrapToggle 적용 (모든 카드가 먼저 표시됨)
requestAnimationFrame(function() {
$('input[id^="checkbox_"]').bootstrapToggle();
});
}
$(function () {
@@ -746,16 +763,16 @@
}
/* ===== 상단 정보 카드 ===== */
.card.p-lg-5, .card.border-light {
.glass-card {
width: calc(100% - 10px) !important;
max-width: 100% !important;
padding: 6px !important; /* Reduced from 15px */
padding: 10px !important;
margin: 5px !important;
border-radius: 12px !important;
box-sizing: border-box !important;
}
.card.p-lg-5 > .row {
.glass-card > .row {
display: flex !important;
flex-direction: column !important;
width: 100% !important;

View File

@@ -17,9 +17,23 @@
<div id="ajax_loader" class="ajax-loader-container" style="display: none;">
<div class="ajax-spinner"></div>
</div>
<div id="yommi_wrapper" class="container-fluid mt-4 mx-auto content-cloak" style="max-width: 100%;">
<div id="ohli24_search_wrapper" class="ohli24-common-wrapper container-fluid mt-4 content-cloak ohli24-search-page">
<!-- Search Section -->
<div class="card p-4 mb-4 border-0" style="background: rgba(30,30,40,0.6); backdrop-filter: blur(10px); border-radius: 16px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
<div class="glass-card p-4 mb-4">
<div class="ohli24-header">
<div class="ohli24-header-left">
<div class="ohli24-icon-box">
<i class="bi bi-search text-primary" style="font-size: 1.5rem;"></i>
</div>
<div>
<h3 class="ohli24-header-title">Ohli24 검색</h3>
<span class="ohli24-header-subtitle">애니메이션 검색 및 탐색</span>
</div>
</div>
</div>
<hr style="border-color: rgba(255,255,255,0.05); margin-bottom: 25px;">
<div class="row align-items-center">
<div class="col-md-8 mx-auto">
<div class="input-group input-group-lg">
@@ -42,23 +56,23 @@
<!-- Category Buttons -->
<div class="row mt-4 justify-content-center">
<div id="anime_category" class="d-flex flex-wrap justify-content-center gap-2" role="group">
<button id="ing" type="button" class="btn btn-success btn-pill px-4 mx-1 active active-glow">방영중</button>
<button id="fin" type="button" class="btn btn-outline-light btn-pill px-4 mx-1 active-glow">완결</button>
<button id="theater" type="button" class="btn btn-outline-primary btn-pill px-4 mx-1 active-glow">극장판</button>
<div id="anime_category" class="d-flex flex-wrap justify-content-center" role="group">
<button id="ing" type="button" class="btn btn-success btn-pill px-4 mx-1 active active-glow mb-2">방영중</button>
<button id="fin" type="button" class="btn btn-outline-light btn-pill px-4 mx-1 active-glow mb-2">완결</button>
<button id="theater" type="button" class="btn btn-outline-primary btn-pill px-4 mx-1 active-glow mb-2">극장판</button>
</div>
</div>
<!-- Year Selection (Sub-category) -->
<div id="year_filter_row" class="row mt-3 justify-content-center animate__animated animate__fadeIn" style="display: none;">
<div id="year_category" class="d-flex flex-wrap justify-content-center gap-2" role="group">
<button data-sca="" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3 active">전체</button>
<button data-sca="2025" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3">2025</button>
<button data-sca="2024" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3">2024</button>
<button data-sca="2023" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3">2023</button>
<button data-sca="2022" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3">2022</button>
<button data-sca="2021" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3">2021</button>
<button data-sca="2020" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3">2020</button>
<div id="year_category" class="d-flex flex-wrap justify-content-center" role="group">
<button data-sca="" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3 mx-1 mb-2 active">전체</button>
<button data-sca="2025" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3 mx-1 mb-2">2025</button>
<button data-sca="2024" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3 mx-1 mb-2">2024</button>
<button data-sca="2023" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3 mx-1 mb-2">2023</button>
<button data-sca="2022" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3 mx-1 mb-2">2022</button>
<button data-sca="2021" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3 mx-1 mb-2">2021</button>
<button data-sca="2020" type="button" class="btn btn-sm btn-outline-secondary btn-pill px-3 mx-1 mb-2">2020</button>
</div>
</div>
</div>
@@ -68,7 +82,7 @@
<div id="airing_list"></div>
</form>
<form id="screen_movie_list_form">
<div id="screen_movie_list" class="container-fluid px-0"></div>
<div id="screen_movie_list"></div>
</form>
<form id="program_auto_form">
@@ -633,12 +647,7 @@
document.addEventListener("scroll", debounce(onScroll, 300));
</script>
<style>
body {
font-family: 'NamumSquareNeo', system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Noto Sans, Liberation Sans, Arial, sans-serif;
background-image: linear-gradient(135deg, #1f2937, #111827, #0f172a);
color: #e2e8f0;
min-height: 100vh;
}
/* Responsive Grid System */
/* Responsive Grid System */
.anime-grid {
@@ -661,55 +670,6 @@
}
}
/* Navigation Menu Override */
ul.nav.nav-pills.bg-light {
background-color: rgba(30, 41, 59, 0.6) !important;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 50rem !important;
padding: 6px !important;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2) !important;
display: inline-flex !important;
flex-wrap: wrap;
justify-content: center;
width: auto !important;
margin-bottom: 20px;
}
ul.nav.nav-pills .nav-item {
margin: 0 2px;
}
ul.nav.nav-pills .nav-link {
border-radius: 50rem !important;
padding: 8px 20px !important;
color: #94a3b8 !important;
font-weight: 600;
transition: all 0.3s ease;
}
ul.nav.nav-pills .nav-link:hover {
background-color: rgba(255, 255, 255, 0.1);
color: #fff !important;
transform: translateY(-1px);
}
ul.nav.nav-pills .nav-link.active {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important;
color: #fff !important;
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.4);
}
/* Glassmorphism Cards */
.glass-card {
background: rgba(30, 41, 59, 0.7) !important;
backdrop-filter: blur(12px) !important;
border: 1px solid rgba(255, 255, 255, 0.08) !important;
border-radius: 16px !important;
transition: transform 0.3s ease, box-shadow 0.3s ease !important;
overflow: hidden;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.glass-card:hover {
transform: translateY(-5px);
@@ -894,39 +854,6 @@
transition-delay: 300ms;
}
/* Navigation Menu Override (Top Sub-menu) */
ul.nav.nav-pills.bg-light {
background-color: rgba(30, 41, 59, 0.6) !important;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 50rem !important;
padding: 6px !important;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2) !important;
display: inline-flex !important;
flex-wrap: wrap;
justify-content: center;
width: auto !important;
margin-bottom: 20px;
}
ul.nav.nav-pills .nav-item { margin: 0 2px; }
ul.nav.nav-pills .nav-link {
border-radius: 50rem !important;
padding: 8px 20px !important;
color: #94a3b8 !important;
font-weight: 600;
transition: all 0.3s ease;
}
ul.nav.nav-pills .nav-link:hover {
background-color: rgba(255, 255, 255, 0.1);
color: #fff !important;
transform: translateY(-1px);
}
ul.nav.nav-pills .nav-link.active {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important;
color: #fff !important;
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.4);
}
</style>
<script type="text/javascript">
@@ -941,7 +868,7 @@ $(document).ready(function(){
/* Mobile Margin Fix */
@media (max-width: 768px) {
body { overflow-x: hidden !important; padding: 0 !important; margin: 0 !important; }
.container, .container-fluid, .row, form, #program_list, #program_auto_form, #episode_list, .queue-container, #yommi_wrapper, #main_container {
.container, .container-fluid, .row, form, #program_list, #program_auto_form, #episode_list, .queue-container, #ohli24_search_wrapper, #main_container {
width: 100% !important; max-width: 100% !important;
padding-left: 4px !important; padding-right: 4px !important;
margin-left: 0 !important; margin-right: 0 !important;

View File

@@ -3,12 +3,22 @@
<link rel="stylesheet" href="{{ url_for('.static', filename='css/mobile_custom.css') }}"/>
<link rel="stylesheet" href="{{ url_for('.static', filename='css/' ~ arg['sub'] ~ '.css') }}"/>
<div id="ohli24_setting_wrapper" class="container-fluid mt-4 mx-auto content-cloak" style="max-width: 100%;">
<div id="ohli24_setting_wrapper" class="ohli24-common-wrapper container-fluid mt-4 content-cloak">
<div class="glass-card p-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="text-white font-weight-bold"><i class="bi bi-gear-fill mr-2"></i>Ohli24 설정</h2>
{{ macros.m_button_group([['globalSettingSaveBtn', '설정 저장']])}}
<div class="ohli24-header">
<div class="ohli24-header-left">
<div class="ohli24-icon-box">
<i class="bi bi-gear-fill text-primary" style="font-size: 1.5rem;"></i>
</div>
<div>
<h3 class="ohli24-header-title">Ohli24 설정</h3>
<span class="ohli24-header-subtitle">플러그인 설정 및 시스템 상태</span>
</div>
</div>
<div class="ohli24-header-right">
{{ macros.m_button_group([['globalSettingSaveBtn', '설정 저장']])}}
</div>
</div>
{{ macros.m_row_start('5') }}
@@ -161,105 +171,6 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css">
<style>
/* Smooth Load Transition */
.content-cloak,
#menu_module_div,
#menu_page_div {
opacity: 0;
transition: opacity 0.5s ease-out;
}
/* Staggered Delays for Natural Top-Down Flow */
#menu_module_div.visible {
opacity: 1;
transition-delay: 0ms;
}
#menu_page_div.visible {
opacity: 1;
transition-delay: 150ms;
}
.content-cloak.visible {
opacity: 1;
transition-delay: 300ms;
}
/* Navigation Menu Override (Top Sub-menu) */
ul.nav.nav-pills.bg-light {
background-color: rgba(30, 41, 59, 0.6) !important;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 50rem !important; /* Pill shape container */
padding: 6px !important;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2) !important;
display: inline-flex !important; /* Fit content */
flex-wrap: wrap; /* allow wrap on small screens */
justify-content: center;
width: auto !important; /* Prevent full width */
margin-bottom: 20px;
}
ul.nav.nav-pills .nav-item {
margin: 0 2px;
}
ul.nav.nav-pills .nav-link {
border-radius: 50rem !important;
padding: 8px 20px !important;
color: #94a3b8 !important; /* Muted text */
font-weight: 600;
transition: all 0.3s ease;
}
ul.nav.nav-pills .nav-link:hover {
background-color: rgba(255, 255, 255, 0.1);
color: #fff !important;
transform: translateY(-1px);
}
ul.nav.nav-pills .nav-link.active {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important;
color: #fff !important;
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.4);
}
/* Global Background */
body {
font-family: 'NamumSquareNeo', system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Noto Sans, Liberation Sans, Arial, sans-serif;
background-image: linear-gradient(135deg, #1f2937, #111827, #0f172a);
color: #e2e8f0;
min-height: 100vh;
}
/* Glass Card Container */
.glass-card {
background: rgba(30, 41, 59, 0.7);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
/* Tabs Styling */
.nav-tabs {
border-bottom: 2px solid rgba(255, 255, 255, 0.1);
}
.nav-tabs .nav-link {
color: #94a3b8;
border: none;
font-weight: 600;
padding: 10px 20px;
border-radius: 8px 8px 0 0;
transition: all 0.2s;
}
.nav-tabs .nav-link:hover {
color: #e2e8f0;
background: rgba(255, 255, 255, 0.05);
}
.nav-tabs .nav-link.active {
color: #60a5fa !important;
background: rgba(30, 41, 59, 0.8) !important;