From 6c31b96be6f28557861c6093ead2205e567a1cb1 Mon Sep 17 00:00:00 2001 From: projectdx Date: Thu, 8 Jan 2026 16:38:24 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Add=20tag=20chips=20UI=20to=20all=203?= =?UTF-8?q?=20setting=20pages=20(ohli24,=20anilife,=20linkkf)=20-=20Modern?= =?UTF-8?q?=20UI=20replacing=20textarea=20with=20draggable=20chips=20-=20T?= =?UTF-8?q?ab=20renamed=20from=20=ED=99=88=ED=99=94=EB=A9=B4=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=20to=20=EC=9E=90=EB=8F=99=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../anime_downloader_anilife_setting.html | 123 +++++++- .../anime_downloader_linkkf_setting.html | 73 ++++- .../anime_downloader_ohli24_setting.html | 275 +++++++++++++++++- 3 files changed, 465 insertions(+), 6 deletions(-) diff --git a/templates/anime_downloader_anilife_setting.html b/templates/anime_downloader_anilife_setting.html index 51566f5..fa1a901 100644 --- a/templates/anime_downloader_anilife_setting.html +++ b/templates/anime_downloader_anilife_setting.html @@ -17,7 +17,7 @@ @@ -68,7 +68,23 @@ {{ macros.global_setting_scheduler_button(arg['scheduler'], arg['is_running']) }} {{ macros.setting_input_text('anilife_interval', '스케쥴링 실행 정보', value=arg['anilife_interval'], col='3', desc=['Inverval(minute 단위)이나 Cron 설정']) }} {{ macros.setting_checkbox('anilife_auto_start', '시작시 자동실행', value=arg['anilife_auto_start'], desc='On : 시작시 자동으로 스케쥴러에 등록됩니다.') }} - {{ macros.setting_input_textarea('anilife_auto_code_list', '자동 다운로드할 작품 코드', desc=['all 입력시 모두 받기', '구분자 | 또는 엔터'], value=arg['anilife_auto_code_list'], row='10') }} + +
+
+ 자동 다운로드할 작품 코드 +
+
+ +
+
+ +
+ +
+
+
Enter로 추가, X로 삭제, 드래그 순서변경 | all 입력시 모두 받기
+
+
{{ macros.setting_checkbox('anilife_auto_mode_all', '에피소드 모두 받기', value=arg['anilife_auto_mode_all'], desc=['On : 이전 에피소드를 모두 받습니다.', 'Off : 최신 에피소드만 받습니다.']) }} {{ macros.m_tab_content_end() }} @@ -354,6 +370,59 @@ background: rgba(59, 130, 246, 0.3) !important; } + /* Tag Chips Styles */ + .tag-chips-wrapper { + display: flex; + flex-wrap: wrap; + gap: 8px; + padding: 12px; + min-height: 60px; + background: rgba(0, 0, 0, 0.2); + border: 1px dashed rgba(255, 255, 255, 0.15); + border-radius: 8px; + } + .tag-chips-wrapper:empty::before { + content: '작품이 없습니다. 아래에서 추가하세요.'; + color: #64748b; + font-style: italic; + } + .tag-chip { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.3) 0%, rgba(37, 99, 235, 0.4) 100%); + border: 1px solid rgba(96, 165, 250, 0.4); + border-radius: 20px; + font-size: 0.9rem; + color: #e2e8f0; + cursor: grab; + transition: all 0.2s ease; + } + .tag-chip:hover { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.5) 0%, rgba(37, 99, 235, 0.6) 100%); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); + } + .tag-chip .tag-text { max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .tag-chip .tag-remove { + width: 18px; height: 18px; + background: rgba(239, 68, 68, 0.5); + border-radius: 50%; + cursor: pointer; + display: flex; align-items: center; justify-content: center; + font-size: 0.75rem; + } + .tag-chip .tag-remove:hover { background: rgba(239, 68, 68, 0.9); } + .tag-chip .tag-index { + width: 20px; height: 20px; + background: rgba(0, 0, 0, 0.3); + border-radius: 50%; + font-size: 0.7rem; + color: #94a3b8; + display: flex; align-items: center; justify-content: center; + } + @@ -588,8 +657,58 @@ $('#copy_cmd_btn').on('click', function() { // 초기 실행 - Action 탭 로드시 시스템 체크 $(document).ready(function() { runSystemCheck(); + initTagChips(); }); +// ====================================== +// Tag Chips 기능 +// ====================================== +function initTagChips() { + var value = $('#anilife_auto_code_list').val().trim(); + if (value) { + var items = value.split(/[|\n]/).map(s => s.trim()).filter(s => s.length > 0); + items.forEach(function(item, index) { addTagChip(item, index); }); + } + updateTagIndices(); +} +function addTagChip(text, index) { + var chip = $('
'+(index+1)+''+escapeHtml(text)+'
'); + $('#tag_chips_container').append(chip); +} +function updateHiddenField() { + var values = []; + $('#tag_chips_container .tag-chip').each(function() { values.push($(this).data('value')); }); + $('#anilife_auto_code_list').val(values.join('|')); +} +function updateTagIndices() { + $('#tag_chips_container .tag-chip').each(function(i) { $(this).find('.tag-index').text(i+1); }); +} +$('#tag_chips_container').on('click', '.tag-remove', function(e) { + e.stopPropagation(); + var chip = $(this).closest('.tag-chip'); + chip.fadeOut(200, function() { $(this).remove(); updateHiddenField(); updateTagIndices(); }); +}); +$('#add_tag_btn').on('click', function() { addNewTag(); }); +$('#new_tag_input').on('keypress', function(e) { if (e.which === 13) { e.preventDefault(); addNewTag(); } }); +function addNewTag() { + var text = $('#new_tag_input').val().trim(); + if (!text) { $.notify('작품명을 입력하세요', {type:'warning'}); return; } + var exists = false; + $('#tag_chips_container .tag-chip').each(function() { if ($(this).data('value') === text) exists = true; }); + if (exists) { $.notify('이미 등록된 작품입니다', {type:'warning'}); return; } + addTagChip(text, $('#tag_chips_container .tag-chip').length); + updateHiddenField(); + $('#new_tag_input').val(''); + $.notify('"'+text+'" 추가됨', {type:'success'}); +} +var draggedChip = null; +$('#tag_chips_container').on('dragstart', '.tag-chip', function(e) { draggedChip = this; $(this).addClass('dragging'); }); +$('#tag_chips_container').on('dragend', '.tag-chip', function() { $(this).removeClass('dragging'); draggedChip = null; updateHiddenField(); updateTagIndices(); }); +$('#tag_chips_container').on('dragover', function(e) { e.preventDefault(); var after = getDragAfterElement(this, e.originalEvent.clientX); if (!after) this.appendChild(draggedChip); else this.insertBefore(draggedChip, after); }); +function getDragAfterElement(container, x) { + return [...container.querySelectorAll('.tag-chip:not(.dragging)')].reduce((c, el) => { var box = el.getBoundingClientRect(); var offset = x - box.left - box.width/2; return (offset < 0 && offset > c.offset) ? {offset, element: el} : c; }, {offset: Number.NEGATIVE_INFINITY}).element; +} + {% endblock %} \ No newline at end of file diff --git a/templates/anime_downloader_ohli24_setting.html b/templates/anime_downloader_ohli24_setting.html index 6bcbc30..0d43126 100644 --- a/templates/anime_downloader_ohli24_setting.html +++ b/templates/anime_downloader_ohli24_setting.html @@ -27,7 +27,7 @@ @@ -79,7 +79,34 @@ {{ macros.global_setting_scheduler_button(arg['scheduler'], arg['is_running']) }} {{ macros.setting_input_text('ohli24_interval', '스케쥴링 실행 정보', value=arg['ohli24_interval'], col='3', desc=['Inverval(minute 단위)이나 Cron 설정']) }} {{ macros.setting_checkbox('ohli24_auto_start', '시작시 자동실행', value=arg['ohli24_auto_start'], desc='On : 시작시 자동으로 스케쥴러에 등록됩니다.') }} - {{ macros.setting_input_textarea('ohli24_auto_code_list', '자동 다운로드할 작품 코드', desc=['구분자 | 또는 엔터'], value=arg['ohli24_auto_code_list'], row='10') }} + +
+
+ 자동 다운로드할 작품 코드 +
+
+ + + + +
+ +
+ + +
+ +
+ +
+
+
+ Enter로 추가, 태그 X로 삭제, 드래그로 순서 변경 가능 +
+
+
{{ macros.setting_checkbox('ohli24_auto_mode_all', '에피소드 모두 받기', value=arg['ohli24_auto_mode_all'], desc=['On : 이전 에피소드를 모두 받습니다.', 'Off : 최신 에피소드만 받습니다.']) }} {{ macros.m_tab_content_end() }} @@ -305,6 +332,93 @@ .folder-item i { font-size: 1.1rem; } + + /* Tag Chips Styles */ + .tag-chips-wrapper { + display: flex; + flex-wrap: wrap; + gap: 8px; + padding: 12px; + min-height: 60px; + background: rgba(0, 0, 0, 0.2); + border: 1px dashed rgba(255, 255, 255, 0.15); + border-radius: 8px; + transition: all 0.3s ease; + } + + .tag-chips-wrapper:empty::before { + content: '작품이 없습니다. 아래에서 추가하세요.'; + color: #64748b; + font-style: italic; + } + + .tag-chips-wrapper.drag-over { + border-color: #3b82f6; + background: rgba(59, 130, 246, 0.1); + } + + .tag-chip { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.3) 0%, rgba(37, 99, 235, 0.4) 100%); + border: 1px solid rgba(96, 165, 250, 0.4); + border-radius: 20px; + font-size: 0.9rem; + color: #e2e8f0; + cursor: grab; + transition: all 0.2s ease; + user-select: none; + } + + .tag-chip:hover { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.5) 0%, rgba(37, 99, 235, 0.6) 100%); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); + } + + .tag-chip.dragging { + opacity: 0.5; + cursor: grabbing; + } + + .tag-chip .tag-text { + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .tag-chip .tag-remove { + display: flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + background: rgba(239, 68, 68, 0.5); + border-radius: 50%; + cursor: pointer; + transition: all 0.2s ease; + font-size: 0.75rem; + } + + .tag-chip .tag-remove:hover { + background: rgba(239, 68, 68, 0.9); + transform: scale(1.1); + } + + .tag-chip .tag-index { + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + background: rgba(0, 0, 0, 0.3); + border-radius: 50%; + font-size: 0.7rem; + color: #94a3b8; + } {% endblock %} \ No newline at end of file