@kangsicj/dcinsideenhancer 1.3.0 → 1.3.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.
@@ -2,7 +2,7 @@
2
2
  // @name DCInside Enhancer
3
3
  // @name:ko 디시 인핸서
4
4
  // @namespace https://github.com/kangsicj/DCInsideEnhancer
5
- // @version 1.3.0
5
+ // @version 1.3.1
6
6
  // @description DCInside filter & enhancer — mobile & PC. Block users/words/heads/notices/images & GIFs across posts, infinite list, zoom viewer, theme sync, encrypted multi-device sync.
7
7
  // @description:ko 디시인사이드 차단·편의 기능 (모바일·PC) - 유저·단어(제목/본문)·말머리·공지 차단, 같은/비슷한 이미지·움짤 모든 글 차단, 디시콘 제거, 차단 미디어 데이터 절약, 목록 자동 보충·무한 스크롤·뒤로 가기 위치 복원, 스크랩, 이미지 확대뷰 개선, 야간모드 자동 연동, 암호화 기기 간 동기화
8
8
  // @author kangsicj
@@ -2,7 +2,7 @@
2
2
  // @name DCInside Enhancer
3
3
  // @name:ko 디시 인핸서
4
4
  // @namespace https://github.com/kangsicj/DCInsideEnhancer
5
- // @version 1.3.0
5
+ // @version 1.3.1
6
6
  // @description DCInside filter & enhancer — mobile & PC. Block users/words/heads/notices/images & GIFs across posts, infinite list, zoom viewer, theme sync, encrypted multi-device sync.
7
7
  // @description:ko 디시인사이드 차단·편의 기능 (모바일·PC) - 유저·단어(제목/본문)·말머리·공지 차단, 같은/비슷한 이미지·움짤 모든 글 차단, 디시콘 제거, 차단 미디어 데이터 절약, 목록 자동 보충·무한 스크롤·뒤로 가기 위치 복원, 스크랩, 이미지 확대뷰 개선, 야간모드 자동 연동, 암호화 기기 간 동기화
8
8
  // @author kangsicj
@@ -75,7 +75,7 @@
75
75
  // 0. 설정·상수
76
76
  // =========================================================
77
77
 
78
- const SCRIPT_VERSION = '1.3.0';
78
+ const SCRIPT_VERSION = '1.3.1';
79
79
  const PROJECT_URL = 'https://github.com/kangsicj/DCInsideEnhancer';
80
80
  const DONATE_URL = 'https://fairy.hada.io/@jinny#support';
81
81
 
@@ -223,6 +223,7 @@
223
223
  hideNewsRows: true,
224
224
  hideCommentAds: true,
225
225
  hideCommentFab: true,
226
+ mobileRecentStrip: true,
226
227
  hideSilbeAlarm: true,
227
228
  alarmBubble: true,
228
229
  alarmKeepPolling: true,
@@ -2068,6 +2069,58 @@
2068
2069
 
2069
2070
  .dc-pv-anchor { text-decoration: underline !important; text-decoration-color: var(--dc-accent-ink) !important; }
2070
2071
 
2072
+ html.dc-recent-strip-on #lately_pop.dc-strip-lately #swiper-lately,
2073
+ html.dc-recent-strip-on #lately_pop.dc-strip-lately #lately_paging1 { display: none !important; }
2074
+ .dc-recent-strip {
2075
+ display: flex;
2076
+ gap: 8px;
2077
+ padding: 12px 12px 14px;
2078
+ overflow-x: auto;
2079
+ overflow-y: hidden;
2080
+ -webkit-overflow-scrolling: touch;
2081
+ scroll-snap-type: x proximity;
2082
+ scrollbar-width: none;
2083
+ overscroll-behavior-x: contain;
2084
+ }
2085
+ .dc-recent-strip::-webkit-scrollbar { display: none; }
2086
+ .dc-recent-strip[hidden] { display: none !important; }
2087
+ .dc-recent-strip .dc-rs-chip {
2088
+ flex: 0 0 auto;
2089
+ display: inline-flex;
2090
+ align-items: center;
2091
+ gap: 4px;
2092
+ max-width: 60vw;
2093
+ height: 36px;
2094
+ padding: 0 4px 0 14px;
2095
+ border: 1px solid var(--dc-border);
2096
+ border-radius: 999px;
2097
+ background: var(--dc-surface-2);
2098
+ color: var(--dc-text);
2099
+ font-size: 14px;
2100
+ font-weight: 600;
2101
+ text-decoration: none;
2102
+ white-space: nowrap;
2103
+ scroll-snap-align: start;
2104
+ }
2105
+ .dc-recent-strip .dc-rs-chip.here { border-color: var(--dc-accent-ink); color: var(--dc-accent-ink); background: var(--dc-accent-soft); }
2106
+ .dc-recent-strip .dc-rs-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
2107
+ .dc-recent-strip .dc-rs-kind { color: var(--dc-text-dim); font-size: 13px; }
2108
+ .dc-recent-strip .dc-rs-del {
2109
+ flex: 0 0 auto;
2110
+ width: 28px;
2111
+ height: 28px;
2112
+ margin: 0;
2113
+ padding: 0;
2114
+ border: none;
2115
+ border-radius: 50%;
2116
+ background: transparent;
2117
+ color: var(--dc-text-dim);
2118
+ font-size: 18px;
2119
+ line-height: 28px;
2120
+ }
2121
+ .dc-recent-strip .dc-rs-pin,
2122
+ .dc-recent-strip .dc-rs-empty { flex: 0 0 auto; align-self: center; padding: 0 6px; color: var(--dc-text-dim); font-size: 12px; white-space: nowrap; }
2123
+
2071
2124
  .dc-key-cursor { outline: 2px solid var(--dc-accent-ink) !important; outline-offset: -2px; background: var(--dc-accent-soft) !important; }
2072
2125
 
2073
2126
  #dc-custom-modal .dc-about {
@@ -10453,6 +10506,14 @@
10453
10506
  off: '댓글로 이동 버튼을 보여 줍니다.',
10454
10507
  refresh: 'root'
10455
10508
  },
10509
+ {
10510
+ group: 'clean',
10511
+ key: 'mobileRecentStrip',
10512
+ name: "'최근 방문'을 한 줄로 밀어 보기 (모바일)",
10513
+ on: '위쪽 [최근 방문]을 누르면 넓은 표 대신 갤러리가 순서대로 한 줄에 나오고, 옆으로 밀어 봅니다. 고정한 순서를 그대로 따릅니다.',
10514
+ off: '사이트 기본 표(2줄 × 4칸, 넘겨 보기)로 보여 줍니다.',
10515
+ refresh: 'root'
10516
+ },
10456
10517
  {
10457
10518
  group: 'clean',
10458
10519
  key: 'hideAdSlots',
@@ -10745,6 +10806,10 @@
10745
10806
  repinReadColors();
10746
10807
  }
10747
10808
 
10809
+ if (item.key === 'mobileRecentStrip') {
10810
+ renderRecentStrip();
10811
+ }
10812
+
10748
10813
  if (item.key === 'alarmBubble') {
10749
10814
  paintAlarmBadge();
10750
10815
  }
@@ -13814,11 +13879,166 @@
13814
13879
  exportNativeRecent();
13815
13880
  };
13816
13881
 
13882
+ const fixNativeLatelyValue = value => {
13883
+ if (!recentOrder.fixed) {
13884
+ return value;
13885
+ }
13886
+
13887
+ try {
13888
+ const parts = unescape(String(value)).split(',').filter(Boolean);
13889
+ const index = new Map(recentGalls.map((item, i) => [item.id, i]));
13890
+ const seen = new Set();
13891
+ const unique = parts.filter(part => {
13892
+ const id = part.split('|')[0];
13893
+
13894
+ if (!id || seen.has(id)) {
13895
+ return false;
13896
+ }
13897
+
13898
+ seen.add(id);
13899
+ return true;
13900
+ });
13901
+ const known = unique.filter(part => index.has(part.split('|')[0])).sort((a, b) => index.get(a.split('|')[0]) - index.get(b.split('|')[0]));
13902
+ const fresh = unique.filter(part => !index.has(part.split('|')[0]));
13903
+
13904
+ return escape([...known, ...fresh].map(part => `${part},`).join(''));
13905
+ } catch {
13906
+ return value;
13907
+ }
13908
+ };
13909
+
13910
+ if (IS_MOBILE_SITE) {
13911
+ try {
13912
+ const proto = window.Storage && window.Storage.prototype;
13913
+ const original = proto && proto.setItem;
13914
+
13915
+ if (original && !original.__dcLately) {
13916
+ const patched = function (key, value) {
13917
+ if (this === window.localStorage && key === 'm_dcinside_lately') {
13918
+ return original.call(this, key, fixNativeLatelyValue(value));
13919
+ }
13920
+
13921
+ return original.call(this, key, value);
13922
+ };
13923
+
13924
+ patched.__dcLately = true;
13925
+ proto.setItem = patched;
13926
+ }
13927
+ } catch {
13928
+ }
13929
+ }
13930
+
13931
+ const renderRecentStrip = () => {
13932
+ const pop = document.getElementById('lately_pop');
13933
+
13934
+ if (!IS_MOBILE_SITE || !pop || settings.mobileRecentStrip === false) {
13935
+ document.documentElement.classList.remove('dc-recent-strip-on');
13936
+ pop?.querySelector('.dc-recent-strip')?.remove();
13937
+ return;
13938
+ }
13939
+
13940
+ const latelyTab = document.getElementById('lately_gall');
13941
+ const showing = !latelyTab || latelyTab.classList.contains('on');
13942
+ let strip = pop.querySelector('.dc-recent-strip');
13943
+
13944
+ if (!strip) {
13945
+ strip = h('div', 'dc-ui dc-recent-strip');
13946
+ strip.setAttribute('role', 'list');
13947
+ const slider = pop.querySelector('#swiper-lately') || pop.querySelector('.vst-slider');
13948
+
13949
+ (slider ? slider.parentNode : pop).insertBefore(strip, slider || null);
13950
+ }
13951
+
13952
+ document.documentElement.classList.add('dc-recent-strip-on');
13953
+ strip.hidden = !showing;
13954
+ pop.classList.toggle('dc-strip-lately', showing);
13955
+
13956
+ if (!showing) {
13957
+ return;
13958
+ }
13959
+
13960
+ const keepScroll = strip.scrollLeft;
13961
+
13962
+ strip.textContent = '';
13963
+
13964
+ if (!recentGalls.length) {
13965
+ strip.appendChild(h('span', 'dc-rs-empty', '최근 방문한 갤러리가 없습니다'));
13966
+ return;
13967
+ }
13968
+
13969
+ const here = (currentGalleryVisit() || {}).id;
13970
+
13971
+ recentGalls.forEach(item => {
13972
+ const chip = h('a', `dc-rs-chip${item.id === here ? ' here' : ''}`);
13973
+
13974
+ chip.href = recentUrl(item);
13975
+ chip.setAttribute('role', 'listitem');
13976
+ chip.appendChild(h('span', 'dc-rs-name', item.n || item.id));
13977
+
13978
+ if (RECENT_KIND_LABEL[item.k]) {
13979
+ chip.appendChild(h('span', 'dc-rs-kind', item.k === 'M' ? 'ⓜ' : item.k === 'N' ? 'ⓝ' : 'ⓟ'));
13980
+ }
13981
+
13982
+ const del = h('button', 'dc-rs-del', '×');
13983
+
13984
+ del.type = 'button';
13985
+ del.setAttribute('aria-label', `${item.n || item.id} 지우기`);
13986
+ del.addEventListener('click', event => {
13987
+ event.preventDefault();
13988
+ event.stopPropagation();
13989
+ recentGalls = recentGalls.filter(other => other !== item);
13990
+ saveRecent();
13991
+ exportNativeRecent();
13992
+ renderRecentStrip();
13993
+ });
13994
+ chip.appendChild(del);
13995
+ strip.appendChild(chip);
13996
+ });
13997
+
13998
+ if (recentOrder.fixed) {
13999
+ strip.appendChild(h('span', 'dc-rs-pin', '📌 순서 고정'));
14000
+ }
14001
+
14002
+ strip.scrollLeft = keepScroll;
14003
+
14004
+ strip.querySelector('.dc-rs-chip.here')?.scrollIntoView?.({ block: 'nearest', inline: 'nearest' });
14005
+ };
14006
+
14007
+ const watchRecentStrip = () => {
14008
+ const pop = document.getElementById('lately_pop');
14009
+
14010
+ if (!IS_MOBILE_SITE || !pop) {
14011
+ return;
14012
+ }
14013
+
14014
+ let timer = null;
14015
+ const refresh = () => {
14016
+ clearTimeout(timer);
14017
+ timer = setTimeout(() => {
14018
+ if (pop.style.display !== 'none') {
14019
+ importNativeRecent();
14020
+ renderRecentStrip();
14021
+ }
14022
+ }, 30);
14023
+ };
14024
+
14025
+ new MutationObserver(refresh).observe(pop, { attributes: true, attributeFilter: ['style'] });
14026
+
14027
+ const tabs = pop.querySelector('.tab-lst');
14028
+
14029
+ if (tabs) {
14030
+ new MutationObserver(refresh).observe(tabs, { attributes: true, subtree: true, attributeFilter: ['class'] });
14031
+ }
14032
+
14033
+ renderRecentStrip();
14034
+ };
14035
+
13817
14036
  const initRecent = () => {
13818
14037
  importNativeRecent();
13819
14038
  recordGalleryVisit();
13820
14039
  exportNativeRecent();
13821
14040
  watchNativeRecentDom();
14041
+ watchRecentStrip();
13822
14042
 
13823
14043
  const follow = () => {
13824
14044
  if (document.visibilityState === 'visible' && (importNativeRecent() || recentOrder.fixed)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kangsicj/dcinsideenhancer",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "DCInside filter & enhancer userscript for mobile and PC (디시인사이드 차단·편의 유저스크립트)",
5
5
  "license": "MIT",
6
6
  "author": "kangsicj",