Make Chaturbate thumbnail grids responsive
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Chaturbate 缩略图放大 2 倍
|
// @name Chaturbate 缩略图放大 2 倍
|
||||||
// @namespace https://chaturbate.com/
|
// @namespace https://chaturbate.com/
|
||||||
// @version 0.13.0
|
// @version 0.13.9
|
||||||
// @description 放大当前 Chaturbate 房间列表, 发现页轮播, 关注下拉与悬停预览缩略图, 并中文化标签页
|
// @description 放大当前 Chaturbate 房间列表, 发现页轮播, 关注下拉与悬停预览缩略图, 并中文化标签页
|
||||||
// @match https://chaturbate.com/*
|
// @match https://chaturbate.com/*
|
||||||
// @match https://*.chaturbate.com/*
|
// @match https://*.chaturbate.com/*
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
const DEBUG = true;
|
const DEBUG = true;
|
||||||
// ===========================
|
// ===========================
|
||||||
|
|
||||||
const VER = "0.13.0";
|
const VER = "0.13.9";
|
||||||
// 旧版本用过 localStorage 尺寸缓存.当前版本不再读写缓存, 避免旧探测值污染新布局.
|
// 旧版本用过 localStorage 尺寸缓存.当前版本不再读写缓存, 避免旧探测值污染新布局.
|
||||||
const LEGACY_CACHE_PREFIX = "tm-thumb-scale:size-cache:";
|
const LEGACY_CACHE_PREFIX = "tm-thumb-scale:size-cache:";
|
||||||
// 标签页中文化表: key 使用站点 tag slug 或英文显示文本归一化后的结果.
|
// 标签页中文化表: key 使用站点 tag slug 或英文显示文本归一化后的结果.
|
||||||
@@ -946,7 +946,8 @@
|
|||||||
* home 模块:
|
* home 模块:
|
||||||
* - 适用范围: 主页, 分类页等普通 #roomlist_root 房间列表.
|
* - 适用范围: 主页, 分类页等普通 #roomlist_root 房间列表.
|
||||||
* - 探测值: 站点 CSS 里的 grid 最小列宽 + 卡片原始高度 + 缩略图原始高度.
|
* - 探测值: 站点 CSS 里的 grid 最小列宽 + 卡片原始高度 + 缩略图原始高度.
|
||||||
* - 放大方式: 原站列数除以 THUMBNAIL_SCALE; 每列仍用 1fr 均分, 保留原站对齐方式.
|
* - 放大方式: 只把 grid 最小列宽乘 THUMBNAIL_SCALE; 列宽仍用 1fr 自适应.
|
||||||
|
* - 这样窗口缩小时会自动减列, 不会把固定列数硬压窄.
|
||||||
* - 注意: 关注页列表单独用 followingList 模块, 不和 home 共用探测结果.
|
* - 注意: 关注页列表单独用 followingList 模块, 不和 home 共用探测结果.
|
||||||
*/
|
*/
|
||||||
html[data-tm-thumb-scale-home="1"] #main #roomlist_root ul.RoomCardGrid,
|
html[data-tm-thumb-scale-home="1"] #main #roomlist_root ul.RoomCardGrid,
|
||||||
@@ -955,8 +956,8 @@
|
|||||||
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) {
|
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) {
|
||||||
/* 改成 grid, 是为了让放大后的卡片按新宽度自然换行. */
|
/* 改成 grid, 是为了让放大后的卡片按新宽度自然换行. */
|
||||||
display: grid !important;
|
display: grid !important;
|
||||||
/* --tm-thumb-home-columns 来自原站自然列数, JS 写入时已经除过倍率. */
|
/* --tm-thumb-home-min-width 来自原站最小列宽, JS 写入时已经乘过倍率. */
|
||||||
grid-template-columns: repeat(var(--tm-thumb-home-columns), minmax(0, 1fr)) !important;
|
grid-template-columns: repeat(auto-fill, minmax(var(--tm-thumb-home-min-width), 1fr)) !important;
|
||||||
justify-content: stretch !important;
|
justify-content: stretch !important;
|
||||||
align-items: start !important;
|
align-items: start !important;
|
||||||
/* 保留一个稳定间距, 避免放大后卡片互相贴住. */
|
/* 保留一个稳定间距, 避免放大后卡片互相贴住. */
|
||||||
@@ -969,14 +970,14 @@
|
|||||||
justify-self: stretch !important;
|
justify-self: stretch !important;
|
||||||
min-width: 0 !important;
|
min-width: 0 !important;
|
||||||
max-width: none !important;
|
max-width: none !important;
|
||||||
height: var(--tm-thumb-home-card-height) !important;
|
height: auto !important;
|
||||||
min-height: var(--tm-thumb-home-card-height) !important;
|
min-height: 0 !important;
|
||||||
max-height: var(--tm-thumb-home-card-height) !important;
|
max-height: none !important;
|
||||||
}
|
}
|
||||||
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail,
|
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail,
|
||||||
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container {
|
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: var(--tm-thumb-home-thumb-height) !important;
|
height: auto !important;
|
||||||
display: block !important;
|
display: block !important;
|
||||||
max-width: none !important;
|
max-width: none !important;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
@@ -985,7 +986,7 @@
|
|||||||
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container img,
|
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container img,
|
||||||
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail {
|
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: var(--tm-thumb-home-thumb-height) !important;
|
height: auto !important;
|
||||||
/* block 可以消掉图片 inline baseline 带来的细小空隙. */
|
/* block 可以消掉图片 inline baseline 带来的细小空隙. */
|
||||||
display: block !important;
|
display: block !important;
|
||||||
max-width: none !important;
|
max-width: none !important;
|
||||||
@@ -999,14 +1000,14 @@
|
|||||||
* - 为什么和 home 分开: 两者 DOM 选择器很像, 但站点可能给不同页面不同原始列宽.
|
* - 为什么和 home 分开: 两者 DOM 选择器很像, 但站点可能给不同页面不同原始列宽.
|
||||||
* - followingList 单独探测, 避免首页探测值污染关注页.
|
* - followingList 单独探测, 避免首页探测值污染关注页.
|
||||||
* - 探测值: 站点 CSS 里的 grid 最小列宽 + 卡片原始高度 + 缩略图原始高度.
|
* - 探测值: 站点 CSS 里的 grid 最小列宽 + 卡片原始高度 + 缩略图原始高度.
|
||||||
* - 和 home 一样, 原站列数除以 THUMBNAIL_SCALE; 每列仍用 1fr 均分, 保留原站对齐方式.
|
* - 和 home 一样, 只放大最小列宽, 列数交给 auto-fill 随窗口自动变化.
|
||||||
*/
|
*/
|
||||||
html[data-tm-thumb-scale-following-list="1"] #main #roomlist_root ul.RoomCardGrid,
|
html[data-tm-thumb-scale-following-list="1"] #main #roomlist_root ul.RoomCardGrid,
|
||||||
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.RoomCardGrid,
|
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.RoomCardGrid,
|
||||||
html[data-tm-thumb-scale-following-list="1"] #main #roomlist_root ul.list:has(li.roomCard),
|
html[data-tm-thumb-scale-following-list="1"] #main #roomlist_root ul.list:has(li.roomCard),
|
||||||
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) {
|
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) {
|
||||||
display: grid !important;
|
display: grid !important;
|
||||||
grid-template-columns: repeat(var(--tm-thumb-following-list-columns), minmax(0, 1fr)) !important;
|
grid-template-columns: repeat(auto-fill, minmax(var(--tm-thumb-following-list-min-width), 1fr)) !important;
|
||||||
justify-content: stretch !important;
|
justify-content: stretch !important;
|
||||||
align-items: start !important;
|
align-items: start !important;
|
||||||
gap: 0.6em 0.75em !important;
|
gap: 0.6em 0.75em !important;
|
||||||
@@ -1017,14 +1018,14 @@
|
|||||||
justify-self: stretch !important;
|
justify-self: stretch !important;
|
||||||
min-width: 0 !important;
|
min-width: 0 !important;
|
||||||
max-width: none !important;
|
max-width: none !important;
|
||||||
height: var(--tm-thumb-following-list-card-height) !important;
|
height: auto !important;
|
||||||
min-height: var(--tm-thumb-following-list-card-height) !important;
|
min-height: 0 !important;
|
||||||
max-height: var(--tm-thumb-following-list-card-height) !important;
|
max-height: none !important;
|
||||||
}
|
}
|
||||||
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail,
|
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail,
|
||||||
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container {
|
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: var(--tm-thumb-following-list-thumb-height) !important;
|
height: auto !important;
|
||||||
display: block !important;
|
display: block !important;
|
||||||
max-width: none !important;
|
max-width: none !important;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
@@ -1033,7 +1034,7 @@
|
|||||||
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container img,
|
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container img,
|
||||||
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail {
|
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: var(--tm-thumb-following-list-thumb-height) !important;
|
height: auto !important;
|
||||||
display: block !important;
|
display: block !important;
|
||||||
max-width: none !important;
|
max-width: none !important;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
@@ -1043,37 +1044,34 @@
|
|||||||
* related 模块:
|
* related 模块:
|
||||||
* - 适用范围: 直播间页面底部"更多这样的房间".
|
* - 适用范围: 直播间页面底部"更多这样的房间".
|
||||||
* - 探测值: 卡片原始宽度 + 卡片原始高度 + 缩略图原始高度.
|
* - 探测值: 卡片原始宽度 + 卡片原始高度 + 缩略图原始高度.
|
||||||
* - 放大方式: 卡片宽度按 THUMBNAIL_SCALE 固定放大; 缩略图宽度跟随卡片宽度.
|
* - 放大方式: 把卡片原始宽度乘 THUMBNAIL_SCALE 作为最小列宽, 列宽仍用 1fr 均分填满.
|
||||||
* - 为什么不按列数: 这个区域是频道页里的局部推荐列表, 容器宽度和首页/关注页不同,
|
* - 这样右侧不会留空, 窗口变窄时也会自动减列.
|
||||||
* 用"列数除倍率"会把卡片拉成容器均分宽度, 导致"更多这样的房间"宽高比例失真.
|
|
||||||
*/
|
*/
|
||||||
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.RoomCardGrid,
|
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.RoomCardGrid,
|
||||||
html[data-tm-thumb-scale-related="1"] #main.roomPage ul.list:has(li.roomCard),
|
html[data-tm-thumb-scale-related="1"] #main.roomPage ul.list:has(li.roomCard),
|
||||||
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.list:has(li.roomCard) {
|
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.list:has(li.roomCard) {
|
||||||
display: grid !important;
|
display: grid !important;
|
||||||
grid-template-columns: repeat(auto-fill, var(--tm-thumb-related-width)) !important;
|
grid-template-columns: repeat(auto-fill, minmax(var(--tm-thumb-related-width), 1fr)) !important;
|
||||||
justify-content: start !important;
|
justify-content: stretch !important;
|
||||||
align-items: start !important;
|
align-items: start !important;
|
||||||
gap: 0.6em 0.75em !important;
|
gap: 0.6em 0.75em !important;
|
||||||
}
|
}
|
||||||
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.RoomCardGrid > li.RoomCard,
|
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.RoomCardGrid > li.RoomCard,
|
||||||
html[data-tm-thumb-scale-related="1"] #main.roomPage ul.list:has(li.roomCard) li.roomCard,
|
html[data-tm-thumb-scale-related="1"] #main.roomPage ul.list:has(li.roomCard) li.roomCard,
|
||||||
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.list:has(li.roomCard) li.roomCard {
|
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.list:has(li.roomCard) li.roomCard {
|
||||||
width: var(--tm-thumb-related-width) !important;
|
width: auto !important;
|
||||||
justify-self: start !important;
|
justify-self: stretch !important;
|
||||||
min-width: 0 !important;
|
min-width: 0 !important;
|
||||||
max-width: var(--tm-thumb-related-width) !important;
|
max-width: none !important;
|
||||||
/* 卡片整体高度也要放大; 只放大缩略图高度时, 下方用户名/人数栏可能被压住. */
|
height: auto !important;
|
||||||
height: var(--tm-thumb-related-card-height) !important;
|
min-height: 0 !important;
|
||||||
min-height: var(--tm-thumb-related-card-height) !important;
|
max-height: none !important;
|
||||||
max-height: var(--tm-thumb-related-card-height) !important;
|
|
||||||
}
|
}
|
||||||
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.RoomCardGrid .RoomCardThumbnail,
|
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.RoomCardGrid .RoomCardThumbnail,
|
||||||
html[data-tm-thumb-scale-related="1"] #main.roomPage ul.list:has(li.roomCard) .room_thumbnail_container,
|
html[data-tm-thumb-scale-related="1"] #main.roomPage ul.list:has(li.roomCard) .room_thumbnail_container,
|
||||||
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.list:has(li.roomCard) .room_thumbnail_container {
|
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.list:has(li.roomCard) .room_thumbnail_container {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
/* --tm-thumb-related-height 来自原始缩略图容器高度. */
|
height: auto !important;
|
||||||
height: var(--tm-thumb-related-height) !important;
|
|
||||||
display: block !important;
|
display: block !important;
|
||||||
max-width: none !important;
|
max-width: none !important;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
@@ -1084,8 +1082,7 @@
|
|||||||
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.list:has(li.roomCard) .room_thumbnail_container img,
|
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.list:has(li.roomCard) .room_thumbnail_container img,
|
||||||
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.list:has(li.roomCard) .room_thumbnail {
|
html[data-tm-thumb-scale-related="1"] .BaseRoomContents ul.list:has(li.roomCard) .room_thumbnail {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
/* 图片和容器使用同一个高度, 避免图片溢出或下方信息栏错位. */
|
height: auto !important;
|
||||||
height: var(--tm-thumb-related-height) !important;
|
|
||||||
display: block !important;
|
display: block !important;
|
||||||
max-width: none !important;
|
max-width: none !important;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
@@ -1171,6 +1168,8 @@
|
|||||||
* - 箭头高度: 跟对应轮播高度同步, 避免右侧箭头与轮播内容错位.
|
* - 箭头高度: 跟对应轮播高度同步, 避免右侧箭头与轮播内容错位.
|
||||||
* - 启用条件: 卡片和缩略图探测到就先启用; 某种行数的轮播不存在时跳过对应高度变量.
|
* - 启用条件: 卡片和缩略图探测到就先启用; 某种行数的轮播不存在时跳过对应高度变量.
|
||||||
* 不能强制要求 triple/double/single 全部存在, 否则页面少一种轮播就会导致发现页完全不放大.
|
* 不能强制要求 triple/double/single 全部存在, 否则页面少一种轮播就会导致发现页完全不放大.
|
||||||
|
* - 关注页底部"为您推荐"也使用 .carousel-root, 但它不是发现页完整轮播排版.
|
||||||
|
* 该区域放大后可能从一行变多行, 所以下面另有 following-page 覆盖规则释放高度.
|
||||||
*/
|
*/
|
||||||
html[data-tm-thumb-scale-discover="1"] .carousel-root .triple-rows ul.list,
|
html[data-tm-thumb-scale-discover="1"] .carousel-root .triple-rows ul.list,
|
||||||
html[data-tm-thumb-scale-discover="1"] #discover_root .triple-rows ul.list {
|
html[data-tm-thumb-scale-discover="1"] #discover_root .triple-rows ul.list {
|
||||||
@@ -1232,6 +1231,189 @@
|
|||||||
max-width: none !important;
|
max-width: none !important;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 关注页底部"为您推荐" */
|
||||||
|
/*
|
||||||
|
* followedPage 模块:
|
||||||
|
* - 适用范围: /followed-cams/ 页面底部"为您推荐"推荐区.
|
||||||
|
* - 这个推荐区复用 .carousel-root / .room-list-carousel, 因此会吃到发现页放大规则.
|
||||||
|
* - 和发现页不同的是, 关注页推荐区放大后常会从原来的一行变成多行.
|
||||||
|
* - 如果继续使用发现页的 fixed height / 横向 track, 第二行卡片会被裁成一条条横带.
|
||||||
|
* - 这里不硬写列数; 用 auto-fill + 探测出的放大后卡片宽度让浏览器自然计算列数.
|
||||||
|
* - 也释放外层和中间 viewport/track 的高度与裁切, 避免换行后的第二行被裁成细条.
|
||||||
|
*/
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root div,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root > div,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .room-list-carousel,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .room-list-carousel > div,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .single-row,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .double-rows,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .triple-rows {
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .single-row ul.list,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .double-rows ul.list,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .triple-rows ul.list {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(var(--tm-thumb-discover-width), 1fr)) !important;
|
||||||
|
grid-auto-rows: auto !important;
|
||||||
|
justify-content: stretch !important;
|
||||||
|
align-items: start !important;
|
||||||
|
gap: 0.6em 0.75em !important;
|
||||||
|
width: auto !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
position: relative !important;
|
||||||
|
left: auto !important;
|
||||||
|
top: auto !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .room-list-carousel ul.list > li {
|
||||||
|
width: auto !important;
|
||||||
|
min-width: 0 !important;
|
||||||
|
max-width: none !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
max-height: none !important;
|
||||||
|
flex: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
justify-self: stretch !important;
|
||||||
|
position: relative !important;
|
||||||
|
left: auto !important;
|
||||||
|
top: auto !important;
|
||||||
|
right: auto !important;
|
||||||
|
bottom: auto !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .room-list-carousel ul.list > li > a,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .room-list-carousel ul.list > li > div {
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .room-list-carousel .room_thumbnail_container {
|
||||||
|
width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .room-list-carousel .room_thumbnail_container img,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"][data-tm-thumb-scale-discover="1"] .carousel-root .room-list-carousel .room_thumbnail {
|
||||||
|
width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"] .followRecommendedContainer,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"] .followRecommendedContainer .followRoomTable,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"] .followRecommendedContainer .followRoomTable > div,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"] .followRecommendedContainer .followRoomTable > div > div {
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"] .followRecommendedContainer .followRoomTable {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(var(--tm-thumb-discover-width), 1fr)) !important;
|
||||||
|
grid-auto-rows: auto !important;
|
||||||
|
justify-content: stretch !important;
|
||||||
|
gap: 0.6em 0.75em !important;
|
||||||
|
align-items: start !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"] .followRecommendedContainer .followRoomTable > div,
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"] .followRecommendedContainer .followRoomTable > div > div {
|
||||||
|
display: contents !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"] .followRecommendedContainer .roomElement {
|
||||||
|
width: auto !important;
|
||||||
|
min-width: 0 !important;
|
||||||
|
max-width: none !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
position: relative !important;
|
||||||
|
left: auto !important;
|
||||||
|
top: auto !important;
|
||||||
|
right: auto !important;
|
||||||
|
bottom: auto !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"] .followRecommendedContainer .roomElementAnchor {
|
||||||
|
width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-page="1"] .followRecommendedContainer .room_thumbnail {
|
||||||
|
width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 关注页"为您推荐": 标题定位版 */
|
||||||
|
/*
|
||||||
|
* followedRecommendations 模块:
|
||||||
|
* - 当前站点的关注页推荐区不再稳定使用 .carousel-root 或 .followRecommendedContainer.
|
||||||
|
* - JS 先找中文标题"为您推荐", 再从标题后面的区域里找缩略图和卡片, 打上 data 标记.
|
||||||
|
* - CSS 只作用于这些 data 标记, 不依赖站点随机 class.
|
||||||
|
* - 这里只放大 grid 的最小列宽, 列宽仍然用 1fr 让浏览器按窗口自然分配.
|
||||||
|
*/
|
||||||
|
html[data-tm-thumb-scale-followed-recommendations="1"] [data-tm-thumb-followed-recommendations="1"],
|
||||||
|
html[data-tm-thumb-scale-followed-recommendations="1"] [data-tm-thumb-followed-recommendations="1"] > *,
|
||||||
|
html[data-tm-thumb-scale-followed-recommendations="1"] [data-tm-thumb-followed-recommendations="1"] > * > * {
|
||||||
|
height: auto !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-recommendations="1"] [data-tm-thumb-followed-recommendations-list="1"] {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(var(--tm-thumb-followed-recommendations-width), 1fr)) !important;
|
||||||
|
grid-auto-rows: auto !important;
|
||||||
|
justify-content: stretch !important;
|
||||||
|
align-items: start !important;
|
||||||
|
gap: 0.6em 0.75em !important;
|
||||||
|
width: auto !important;
|
||||||
|
height: auto !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-recommendations="1"] [data-tm-thumb-followed-recommendations-card="1"] {
|
||||||
|
width: auto !important;
|
||||||
|
min-width: 0 !important;
|
||||||
|
max-width: none !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
position: relative !important;
|
||||||
|
left: auto !important;
|
||||||
|
top: auto !important;
|
||||||
|
right: auto !important;
|
||||||
|
bottom: auto !important;
|
||||||
|
transform: none !important;
|
||||||
|
flex: none !important;
|
||||||
|
justify-self: stretch !important;
|
||||||
|
}
|
||||||
|
html[data-tm-thumb-scale-followed-recommendations="1"] [data-tm-thumb-followed-recommendations-thumb="1"] {
|
||||||
|
width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
display: block !important;
|
||||||
|
max-width: none !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLE_ID = "tm-thumb-scale-style";
|
const STYLE_ID = "tm-thumb-scale-style";
|
||||||
@@ -1257,6 +1439,7 @@
|
|||||||
const moduleReady = {
|
const moduleReady = {
|
||||||
home: false,
|
home: false,
|
||||||
followingList: false,
|
followingList: false,
|
||||||
|
followedRecommendations: false,
|
||||||
related: false,
|
related: false,
|
||||||
follow: false,
|
follow: false,
|
||||||
tooltip: false,
|
tooltip: false,
|
||||||
@@ -1477,35 +1660,138 @@
|
|||||||
style.setProperty(arrowName, value);
|
style.setProperty(arrowName, value);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
const setListScaleVars = (columnsName, cardHeightName, thumbHeightName, metrics, cardWidth, cardHeight, thumbHeight) => {
|
const setListScaleVars = (minWidthName, cardHeightName, thumbHeightName, metrics, cardWidth, cardHeight, thumbHeight) => {
|
||||||
const style = rootStyle();
|
const style = rootStyle();
|
||||||
if (
|
if (
|
||||||
!style
|
!style
|
||||||
|| !metrics
|
|| !metrics
|
||||||
|| !Number.isFinite(metrics.columns)
|
|
||||||
|| !Number.isFinite(cardWidth)
|
|| !Number.isFinite(cardWidth)
|
||||||
|| !Number.isFinite(cardHeight)
|
|| !Number.isFinite(cardHeight)
|
||||||
|| !Number.isFinite(thumbHeight)
|
|| !Number.isFinite(thumbHeight)
|
||||||
|| metrics.columns <= 0
|
|
||||||
|| cardWidth <= 0
|
|| cardWidth <= 0
|
||||||
|| cardHeight <= 0
|
|| cardHeight <= 0
|
||||||
|| thumbHeight <= 0
|
|| thumbHeight <= 0
|
||||||
) return false;
|
) return false;
|
||||||
const scaledColumns = Math.max(1, Math.floor(metrics.columns / THUMBNAIL_SCALE));
|
const originalMinWidth = Number.isFinite(metrics.minColumn) && metrics.minColumn > 0
|
||||||
const targetCardWidth = (
|
? metrics.minColumn
|
||||||
Number.isFinite(metrics.width)
|
: cardWidth;
|
||||||
&& metrics.width > 0
|
style.setProperty(minWidthName, scaledPx(originalMinWidth));
|
||||||
&& Number.isFinite(metrics.columnGap)
|
style.setProperty(cardHeightName, scaledCardHeightFromThumbPx(cardHeight, thumbHeight));
|
||||||
&& metrics.columnGap >= 0
|
style.setProperty(thumbHeightName, scaledPx(thumbHeight));
|
||||||
)
|
return true;
|
||||||
? (metrics.width - (metrics.columnGap * Math.max(0, scaledColumns - 1))) / scaledColumns
|
};
|
||||||
: null;
|
const clearFollowedRecommendationMarkers = () => {
|
||||||
const effectiveScale = targetCardWidth && targetCardWidth > 0
|
try {
|
||||||
? targetCardWidth / cardWidth
|
document.querySelectorAll(
|
||||||
: THUMBNAIL_SCALE;
|
"[data-tm-thumb-followed-recommendations],"
|
||||||
style.setProperty(columnsName, String(scaledColumns));
|
+ "[data-tm-thumb-followed-recommendations-list],"
|
||||||
style.setProperty(cardHeightName, scaledCardHeightFromThumbWithScalePx(cardHeight, thumbHeight, effectiveScale));
|
+ "[data-tm-thumb-followed-recommendations-card],"
|
||||||
style.setProperty(thumbHeightName, `${Math.round(thumbHeight * effectiveScale)}px`);
|
+ "[data-tm-thumb-followed-recommendations-thumb]",
|
||||||
|
).forEach((el) => {
|
||||||
|
el.removeAttribute("data-tm-thumb-followed-recommendations");
|
||||||
|
el.removeAttribute("data-tm-thumb-followed-recommendations-list");
|
||||||
|
el.removeAttribute("data-tm-thumb-followed-recommendations-card");
|
||||||
|
el.removeAttribute("data-tm-thumb-followed-recommendations-thumb");
|
||||||
|
});
|
||||||
|
} catch (_) {}
|
||||||
|
};
|
||||||
|
const directText = (el) => {
|
||||||
|
try {
|
||||||
|
return Array.from(el.childNodes || [])
|
||||||
|
.filter((node) => node.nodeType === 3)
|
||||||
|
.map((node) => node.nodeValue || "")
|
||||||
|
.join("")
|
||||||
|
.trim();
|
||||||
|
} catch (_) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const followedRecommendationHeadings = () => {
|
||||||
|
if (!isFollowingListPage() || !document.querySelectorAll) return [];
|
||||||
|
const result = [];
|
||||||
|
try {
|
||||||
|
document.querySelectorAll("h1,h2,h3,h4,strong,b,div,span").forEach((el) => {
|
||||||
|
const text = (directText(el) || el.textContent || "").trim();
|
||||||
|
if (/^(为您推荐|Recommended)$/i.test(text)) result.push(el);
|
||||||
|
});
|
||||||
|
} catch (_) {}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const hasRecommendationMedia = (el) => {
|
||||||
|
try {
|
||||||
|
return !!(el && el.querySelector && el.querySelector(
|
||||||
|
'img[src*="live.mmcdn"], img[src*="thumb.live.mmcdn"], img.room_thumbnail, .room_thumbnail img, .room_thumbnail_container img',
|
||||||
|
));
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const firstFollowingRecommendationRoot = () => {
|
||||||
|
const headings = followedRecommendationHeadings();
|
||||||
|
for (const heading of headings) {
|
||||||
|
const probes = [];
|
||||||
|
let node = heading;
|
||||||
|
for (let depth = 0; node && depth < 6; depth += 1) {
|
||||||
|
if (node.nextElementSibling) probes.push(node.nextElementSibling);
|
||||||
|
if (node.parentElement && node.parentElement.nextElementSibling) probes.push(node.parentElement.nextElementSibling);
|
||||||
|
probes.push(node.parentElement);
|
||||||
|
node = node.parentElement;
|
||||||
|
}
|
||||||
|
const root = probes.find((candidate) => candidate && candidate !== document.body && hasRecommendationMedia(candidate));
|
||||||
|
if (root) return root;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
const markFollowedRecommendations = () => {
|
||||||
|
clearFollowedRecommendationMarkers();
|
||||||
|
const root = firstFollowingRecommendationRoot();
|
||||||
|
if (!root) return null;
|
||||||
|
root.setAttribute("data-tm-thumb-followed-recommendations", "1");
|
||||||
|
let list = null;
|
||||||
|
try {
|
||||||
|
list = Array.from(root.querySelectorAll("ul,ol,div"))
|
||||||
|
.filter(hasRecommendationMedia)
|
||||||
|
.sort((a, b) => {
|
||||||
|
const aCount = a.querySelectorAll("img").length;
|
||||||
|
const bCount = b.querySelectorAll("img").length;
|
||||||
|
return bCount - aCount;
|
||||||
|
})[0] || root;
|
||||||
|
} catch (_) {
|
||||||
|
list = root;
|
||||||
|
}
|
||||||
|
list.setAttribute("data-tm-thumb-followed-recommendations-list", "1");
|
||||||
|
const thumbs = Array.from(root.querySelectorAll(
|
||||||
|
'img[src*="live.mmcdn"], img[src*="thumb.live.mmcdn"], img.room_thumbnail, .room_thumbnail img, .room_thumbnail_container img',
|
||||||
|
));
|
||||||
|
let cards = [];
|
||||||
|
try {
|
||||||
|
cards = Array.from(root.querySelectorAll("li.RoomCard, li.roomCard, .roomElement"));
|
||||||
|
} catch (_) {
|
||||||
|
cards = [];
|
||||||
|
}
|
||||||
|
thumbs.forEach((img) => {
|
||||||
|
const thumb = img.closest(".room_thumbnail_container, .RoomCardThumbnail, .room_thumbnail") || img;
|
||||||
|
thumb.setAttribute("data-tm-thumb-followed-recommendations-thumb", "1");
|
||||||
|
const finalCard = img.closest("li.RoomCard, li.roomCard, .roomElement, li, a") || thumb.parentElement || img.parentElement;
|
||||||
|
if (finalCard && !cards.includes(finalCard)) {
|
||||||
|
cards.push(finalCard);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cards.forEach((card) => {
|
||||||
|
card.setAttribute("data-tm-thumb-followed-recommendations-card", "1");
|
||||||
|
});
|
||||||
|
return { root, list, cards, thumbs };
|
||||||
|
};
|
||||||
|
const setFollowedRecommendationVars = (marked) => {
|
||||||
|
const style = rootStyle();
|
||||||
|
if (!style || !marked || !marked.cards.length || !marked.thumbs.length) return false;
|
||||||
|
const cardRect = rectOf(marked.cards[0]);
|
||||||
|
const thumbRect = rectOf(marked.thumbs[0]);
|
||||||
|
if (!isRect(cardRect) || !isRect(thumbRect)) return false;
|
||||||
|
style.setProperty("--tm-thumb-followed-recommendations-width", scaledPx(cardRect.width));
|
||||||
|
style.setProperty("--tm-thumb-followed-recommendations-card-height", "auto");
|
||||||
|
style.setProperty("--tm-thumb-followed-recommendations-thumb-width", scaledPx(thumbRect.width));
|
||||||
|
style.setProperty("--tm-thumb-followed-recommendations-thumb-height", "auto");
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
// 每个模块的 CSS 都由 html[data-tm-thumb-scale-模块名="1"] 控制.
|
// 每个模块的 CSS 都由 html[data-tm-thumb-scale-模块名="1"] 控制.
|
||||||
@@ -1645,6 +1931,7 @@
|
|||||||
columns: Math.max(1, Math.floor((rect.width + columnGap) / (minColumn + columnGap))),
|
columns: Math.max(1, Math.floor((rect.width + columnGap) / (minColumn + columnGap))),
|
||||||
width: rect.width,
|
width: rect.width,
|
||||||
columnGap,
|
columnGap,
|
||||||
|
minColumn,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const columns = computedGridColumnCount(selector);
|
const columns = computedGridColumnCount(selector);
|
||||||
@@ -1652,6 +1939,7 @@
|
|||||||
columns,
|
columns,
|
||||||
width: rect && rect.width,
|
width: rect && rect.width,
|
||||||
columnGap,
|
columnGap,
|
||||||
|
minColumn,
|
||||||
} : null;
|
} : null;
|
||||||
};
|
};
|
||||||
const detectAndApplySizes = (stage = "detect") => {
|
const detectAndApplySizes = (stage = "detect") => {
|
||||||
@@ -1664,6 +1952,10 @@
|
|||||||
* 4) 锁住后的模块后续不再探测, 避免把已放大的尺寸再次当成原始尺寸.
|
* 4) 锁住后的模块后续不再探测, 避免把已放大的尺寸再次当成原始尺寸.
|
||||||
*/
|
*/
|
||||||
const followingListPage = isFollowingListPage();
|
const followingListPage = isFollowingListPage();
|
||||||
|
// followedPage 只代表 URL 处在关注页, 用于限制底部"为您推荐"的轮播高度覆盖规则.
|
||||||
|
// 它不依赖尺寸探测; 具体是否放大仍由 discover 模块探测成功后决定.
|
||||||
|
setModuleReady("followedPage", followingListPage);
|
||||||
|
const followedRecommendations = followingListPage ? markFollowedRecommendations() : null;
|
||||||
const homeMetrics = moduleReady.home || followingListPage ? null : naturalGridMetrics("#roomlist_root ul.RoomCardGrid, #roomlist_root ul.list:has(li.roomCard)");
|
const homeMetrics = moduleReady.home || followingListPage ? null : naturalGridMetrics("#roomlist_root ul.RoomCardGrid, #roomlist_root ul.list:has(li.roomCard)");
|
||||||
const followingListMetrics = moduleReady.followingList || !followingListPage ? null : naturalGridMetrics("#roomlist_root ul.RoomCardGrid, #roomlist_root ul.list:has(li.roomCard)");
|
const followingListMetrics = moduleReady.followingList || !followingListPage ? null : naturalGridMetrics("#roomlist_root ul.RoomCardGrid, #roomlist_root ul.list:has(li.roomCard)");
|
||||||
const measured = {
|
const measured = {
|
||||||
@@ -1675,6 +1967,15 @@
|
|||||||
followingListMetrics,
|
followingListMetrics,
|
||||||
followingListCard: moduleReady.followingList || !followingListPage ? null : firstRect("#roomlist_root li.RoomCard, #roomlist_root li.roomCard"),
|
followingListCard: moduleReady.followingList || !followingListPage ? null : firstRect("#roomlist_root li.RoomCard, #roomlist_root li.roomCard"),
|
||||||
followingListThumb: moduleReady.followingList || !followingListPage ? null : firstRect("#roomlist_root .RoomCardThumbnail, #roomlist_root .room_thumbnail_container"),
|
followingListThumb: moduleReady.followingList || !followingListPage ? null : firstRect("#roomlist_root .RoomCardThumbnail, #roomlist_root .room_thumbnail_container"),
|
||||||
|
// 关注页底部"为您推荐": 当前站点 class 不稳定, 使用标题定位后打 data 标记.
|
||||||
|
followedRecommendations: followedRecommendations ? {
|
||||||
|
root: rectOf(followedRecommendations.root),
|
||||||
|
list: rectOf(followedRecommendations.list),
|
||||||
|
card: followedRecommendations.cards[0] && rectOf(followedRecommendations.cards[0]),
|
||||||
|
thumb: followedRecommendations.thumbs[0] && rectOf(followedRecommendations.thumbs[0]),
|
||||||
|
cards: followedRecommendations.cards.length,
|
||||||
|
thumbs: followedRecommendations.thumbs.length,
|
||||||
|
} : null,
|
||||||
// 频道页"更多这样的房间": 使用局部推荐区自己的卡片原始宽度和高度, 不套用首页列数算法.
|
// 频道页"更多这样的房间": 使用局部推荐区自己的卡片原始宽度和高度, 不套用首页列数算法.
|
||||||
relatedCard: moduleReady.related ? null : firstRect(".BaseRoomContents ul.RoomCardGrid > li.RoomCard, #main.roomPage ul.list:has(li.roomCard) li.roomCard, .BaseRoomContents ul.list:has(li.roomCard) li.roomCard"),
|
relatedCard: moduleReady.related ? null : firstRect(".BaseRoomContents ul.RoomCardGrid > li.RoomCard, #main.roomPage ul.list:has(li.roomCard) li.roomCard, .BaseRoomContents ul.list:has(li.roomCard) li.roomCard"),
|
||||||
relatedThumb: moduleReady.related ? null : firstRect(".BaseRoomContents ul.RoomCardGrid .RoomCardThumbnail, #main.roomPage ul.list:has(li.roomCard) .room_thumbnail_container, .BaseRoomContents ul.list:has(li.roomCard) .room_thumbnail_container"),
|
relatedThumb: moduleReady.related ? null : firstRect(".BaseRoomContents ul.RoomCardGrid .RoomCardThumbnail, #main.roomPage ul.list:has(li.roomCard) .room_thumbnail_container, .BaseRoomContents ul.list:has(li.roomCard) .room_thumbnail_container"),
|
||||||
@@ -1695,7 +1996,7 @@
|
|||||||
if (!moduleReady.home && !followingListPage) {
|
if (!moduleReady.home && !followingListPage) {
|
||||||
// home 依赖当前页面的自然列数和原始高度; 每次加载重新探测.
|
// home 依赖当前页面的自然列数和原始高度; 每次加载重新探测.
|
||||||
moduleReady.home = setListScaleVars(
|
moduleReady.home = setListScaleVars(
|
||||||
"--tm-thumb-home-columns",
|
"--tm-thumb-home-min-width",
|
||||||
"--tm-thumb-home-card-height",
|
"--tm-thumb-home-card-height",
|
||||||
"--tm-thumb-home-thumb-height",
|
"--tm-thumb-home-thumb-height",
|
||||||
measured.homeMetrics,
|
measured.homeMetrics,
|
||||||
@@ -1709,7 +2010,7 @@
|
|||||||
if (!moduleReady.followingList && followingListPage) {
|
if (!moduleReady.followingList && followingListPage) {
|
||||||
// followingList 单独探测, 避免关注页拿到首页的列数.
|
// followingList 单独探测, 避免关注页拿到首页的列数.
|
||||||
moduleReady.followingList = setListScaleVars(
|
moduleReady.followingList = setListScaleVars(
|
||||||
"--tm-thumb-following-list-columns",
|
"--tm-thumb-following-list-min-width",
|
||||||
"--tm-thumb-following-list-card-height",
|
"--tm-thumb-following-list-card-height",
|
||||||
"--tm-thumb-following-list-thumb-height",
|
"--tm-thumb-following-list-thumb-height",
|
||||||
measured.followingListMetrics,
|
measured.followingListMetrics,
|
||||||
@@ -1720,6 +2021,13 @@
|
|||||||
setModuleReady("followingList", moduleReady.followingList);
|
setModuleReady("followingList", moduleReady.followingList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!moduleReady.followedRecommendations && followingListPage) {
|
||||||
|
moduleReady.followedRecommendations = setFollowedRecommendationVars(followedRecommendations);
|
||||||
|
setModuleReady("followedRecommendations", moduleReady.followedRecommendations);
|
||||||
|
} else if (moduleReady.followedRecommendations && followingListPage) {
|
||||||
|
setModuleReady("followedRecommendations", true);
|
||||||
|
}
|
||||||
|
|
||||||
if (!moduleReady.related) {
|
if (!moduleReady.related) {
|
||||||
// related 同时依赖卡片宽度, 卡片高度和缩略图高度; 三者缺一不可.
|
// related 同时依赖卡片宽度, 卡片高度和缩略图高度; 三者缺一不可.
|
||||||
moduleReady.related = [
|
moduleReady.related = [
|
||||||
@@ -1878,10 +2186,10 @@
|
|||||||
try {
|
try {
|
||||||
const style = getComputedStyle(document.documentElement);
|
const style = getComputedStyle(document.documentElement);
|
||||||
return {
|
return {
|
||||||
homeColumns: style.getPropertyValue("--tm-thumb-home-columns").trim(),
|
homeMinWidth: style.getPropertyValue("--tm-thumb-home-min-width").trim(),
|
||||||
homeCardHeight: style.getPropertyValue("--tm-thumb-home-card-height").trim(),
|
homeCardHeight: style.getPropertyValue("--tm-thumb-home-card-height").trim(),
|
||||||
homeThumbHeight: style.getPropertyValue("--tm-thumb-home-thumb-height").trim(),
|
homeThumbHeight: style.getPropertyValue("--tm-thumb-home-thumb-height").trim(),
|
||||||
followingListColumns: style.getPropertyValue("--tm-thumb-following-list-columns").trim(),
|
followingListMinWidth: style.getPropertyValue("--tm-thumb-following-list-min-width").trim(),
|
||||||
followingListCardHeight: style.getPropertyValue("--tm-thumb-following-list-card-height").trim(),
|
followingListCardHeight: style.getPropertyValue("--tm-thumb-following-list-card-height").trim(),
|
||||||
followingListThumbHeight: style.getPropertyValue("--tm-thumb-following-list-thumb-height").trim(),
|
followingListThumbHeight: style.getPropertyValue("--tm-thumb-following-list-thumb-height").trim(),
|
||||||
relatedWidth: style.getPropertyValue("--tm-thumb-related-width").trim(),
|
relatedWidth: style.getPropertyValue("--tm-thumb-related-width").trim(),
|
||||||
@@ -1894,6 +2202,10 @@
|
|||||||
discoverCardHeight: style.getPropertyValue("--tm-thumb-discover-card-height").trim(),
|
discoverCardHeight: style.getPropertyValue("--tm-thumb-discover-card-height").trim(),
|
||||||
discoverThumbWidth: style.getPropertyValue("--tm-thumb-discover-thumb-width").trim(),
|
discoverThumbWidth: style.getPropertyValue("--tm-thumb-discover-thumb-width").trim(),
|
||||||
discoverHeight: style.getPropertyValue("--tm-thumb-discover-height").trim(),
|
discoverHeight: style.getPropertyValue("--tm-thumb-discover-height").trim(),
|
||||||
|
followedRecommendationsWidth: style.getPropertyValue("--tm-thumb-followed-recommendations-width").trim(),
|
||||||
|
followedRecommendationsCardHeight: style.getPropertyValue("--tm-thumb-followed-recommendations-card-height").trim(),
|
||||||
|
followedRecommendationsThumbWidth: style.getPropertyValue("--tm-thumb-followed-recommendations-thumb-width").trim(),
|
||||||
|
followedRecommendationsThumbHeight: style.getPropertyValue("--tm-thumb-followed-recommendations-thumb-height").trim(),
|
||||||
};
|
};
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return {};
|
return {};
|
||||||
@@ -1931,6 +2243,105 @@
|
|||||||
return report;
|
return report;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const inspectFollowedRecommendations = () => {
|
||||||
|
/*
|
||||||
|
* 专门排查关注页底部"为您推荐"被压扁的问题.
|
||||||
|
* 输出重点:
|
||||||
|
* - 哪些推荐区候选容器存在;
|
||||||
|
* - 前几张卡片的真实矩形, 用来判断第 3 张是否已经被压成横条;
|
||||||
|
* - 被压扁卡片向上的祖先层高度/overflow/position/transform, 用来找真正裁切的节点.
|
||||||
|
*/
|
||||||
|
const rectInfo = (el) => {
|
||||||
|
if (!el || !el.getBoundingClientRect) return null;
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
x: Math.round(rect.x),
|
||||||
|
y: Math.round(rect.y),
|
||||||
|
width: Math.round(rect.width),
|
||||||
|
height: Math.round(rect.height),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const styleInfo = (el) => {
|
||||||
|
if (!el || typeof getComputedStyle !== "function") return {};
|
||||||
|
const style = getComputedStyle(el);
|
||||||
|
return {
|
||||||
|
display: style.display,
|
||||||
|
position: style.position,
|
||||||
|
overflow: style.overflow,
|
||||||
|
overflowX: style.overflowX,
|
||||||
|
overflowY: style.overflowY,
|
||||||
|
height: style.height,
|
||||||
|
minHeight: style.minHeight,
|
||||||
|
maxHeight: style.maxHeight,
|
||||||
|
gridTemplateColumns: style.gridTemplateColumns,
|
||||||
|
gridAutoRows: style.gridAutoRows,
|
||||||
|
transform: style.transform,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const nodeInfo = (el) => ({
|
||||||
|
tag: el && el.tagName,
|
||||||
|
id: el && el.id,
|
||||||
|
className: el && String(el.className || "").slice(0, 220),
|
||||||
|
rect: rectInfo(el),
|
||||||
|
style: styleInfo(el),
|
||||||
|
});
|
||||||
|
const ancestorChain = (el, stop) => {
|
||||||
|
const chain = [];
|
||||||
|
let current = el;
|
||||||
|
while (current && chain.length < 14) {
|
||||||
|
chain.push(nodeInfo(current));
|
||||||
|
if (current === stop || current === document.body || current === document.documentElement) break;
|
||||||
|
current = current.parentElement;
|
||||||
|
}
|
||||||
|
return chain;
|
||||||
|
};
|
||||||
|
const candidates = Array.from(document.querySelectorAll(
|
||||||
|
'[data-tm-thumb-followed-recommendations="1"], .followRecommendedContainer, .carousel-root .room-list-carousel, .carousel-root',
|
||||||
|
));
|
||||||
|
const reports = candidates.map((root, index) => {
|
||||||
|
const cards = Array.from(root.querySelectorAll(
|
||||||
|
"ul.list > li, .roomElement, li.RoomCard, li.roomCard",
|
||||||
|
)).slice(0, 8);
|
||||||
|
const targetCard = cards[2] || cards[0] || root;
|
||||||
|
return {
|
||||||
|
index,
|
||||||
|
root: nodeInfo(root),
|
||||||
|
cards: cards.map(nodeInfo),
|
||||||
|
targetAncestors: ancestorChain(targetCard, root),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const report = {
|
||||||
|
ver: VER,
|
||||||
|
href: typeof location === "undefined" ? "" : location.href,
|
||||||
|
htmlAttrs: document.documentElement ? {
|
||||||
|
followedPage: document.documentElement.getAttribute("data-tm-thumb-scale-followed-page"),
|
||||||
|
followedRecommendations: document.documentElement.getAttribute("data-tm-thumb-scale-followed-recommendations"),
|
||||||
|
discover: document.documentElement.getAttribute("data-tm-thumb-scale-discover"),
|
||||||
|
ver: document.documentElement.getAttribute("data-tm-thumb-scale-ver"),
|
||||||
|
} : {},
|
||||||
|
cssVars: (() => {
|
||||||
|
try {
|
||||||
|
const style = getComputedStyle(document.documentElement);
|
||||||
|
return {
|
||||||
|
discoverWidth: style.getPropertyValue("--tm-thumb-discover-width").trim(),
|
||||||
|
discoverCardHeight: style.getPropertyValue("--tm-thumb-discover-card-height").trim(),
|
||||||
|
discoverThumbWidth: style.getPropertyValue("--tm-thumb-discover-thumb-width").trim(),
|
||||||
|
discoverHeight: style.getPropertyValue("--tm-thumb-discover-height").trim(),
|
||||||
|
followedRecommendationsWidth: style.getPropertyValue("--tm-thumb-followed-recommendations-width").trim(),
|
||||||
|
followedRecommendationsCardHeight: style.getPropertyValue("--tm-thumb-followed-recommendations-card-height").trim(),
|
||||||
|
followedRecommendationsThumbWidth: style.getPropertyValue("--tm-thumb-followed-recommendations-thumb-width").trim(),
|
||||||
|
followedRecommendationsThumbHeight: style.getPropertyValue("--tm-thumb-followed-recommendations-thumb-height").trim(),
|
||||||
|
};
|
||||||
|
} catch (_) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
candidates: reports,
|
||||||
|
};
|
||||||
|
console.log("[tm-thumb-scale recommendations]", report);
|
||||||
|
return report;
|
||||||
|
};
|
||||||
|
|
||||||
const exposeDebugApi = () => {
|
const exposeDebugApi = () => {
|
||||||
// 手动调试入口: 控制台执行 tmThumbScaleDebug() 可立即生成报告.
|
// 手动调试入口: 控制台执行 tmThumbScaleDebug() 可立即生成报告.
|
||||||
const api = (stage = "manual") => reportDiagnostics(stage);
|
const api = (stage = "manual") => reportDiagnostics(stage);
|
||||||
@@ -1941,6 +2352,7 @@
|
|||||||
targets.forEach((target) => {
|
targets.forEach((target) => {
|
||||||
try {
|
try {
|
||||||
target.tmThumbScaleDebug = api;
|
target.tmThumbScaleDebug = api;
|
||||||
|
target.tmThumbScaleInspectRecommendations = inspectFollowedRecommendations;
|
||||||
target.tmThumbScaleLastReport = lastReport;
|
target.tmThumbScaleLastReport = lastReport;
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
});
|
});
|
||||||
@@ -2038,6 +2450,9 @@
|
|||||||
reportDiagnostics("start-before-inject");
|
reportDiagnostics("start-before-inject");
|
||||||
// 标签页中文化: document-start 时可能还没有标签 DOM, 后续 observer/定时器会继续补.
|
// 标签页中文化: document-start 时可能还没有标签 DOM, 后续 observer/定时器会继续补.
|
||||||
scheduleTagsTranslation("start", 0);
|
scheduleTagsTranslation("start", 0);
|
||||||
|
// 关注页底部"为您推荐"复用发现页轮播结构, 必须在注入 CSS 前先打页面标记.
|
||||||
|
// 否则它会短暂吃到发现页固定高度, 后续推荐卡片换行时容易被裁成横条.
|
||||||
|
setModuleReady("followedPage", isFollowingListPage());
|
||||||
|
|
||||||
// 注入 CSS.因为模块默认关闭, 即使 CSS 很早注入, 也不会改变原始布局.
|
// 注入 CSS.因为模块默认关闭, 即使 CSS 很早注入, 也不会改变原始布局.
|
||||||
inject();
|
inject();
|
||||||
|
|||||||
@@ -86,43 +86,43 @@ assert.match(
|
|||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
capturedCss,
|
capturedCss,
|
||||||
/data-tm-thumb-scale-following-list="1"[\s\S]*--tm-thumb-following-list-columns/,
|
/data-tm-thumb-scale-following-list="1"[\s\S]*--tm-thumb-following-list-min-width/,
|
||||||
"followed-cams list should be separated from homepage list column count",
|
"followed-cams list should use its own detected minimum column width",
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
capturedCss,
|
capturedCss,
|
||||||
/data-tm-thumb-scale-following-list="1"[\s\S]*grid-template-columns:\s*repeat\(var\(--tm-thumb-following-list-columns\),\s*minmax\(0,\s*1fr\)\)\s*!important/,
|
/data-tm-thumb-scale-following-list="1"[\s\S]*grid-template-columns:\s*repeat\(auto-fill,\s*minmax\(var\(--tm-thumb-following-list-min-width\),\s*1fr\)\)\s*!important/,
|
||||||
"followed-cams list should reduce the original column count and keep 1fr alignment",
|
"followed-cams list should reduce columns responsively using scaled minimum width",
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
capturedCss,
|
capturedCss,
|
||||||
/#main\.roomPage\s+ul\.list:has\(li\.roomCard\)\s+\.room_thumbnail_container[\s\S]*width:\s*100%\s*!important[\s\S]*height:\s*var\(--tm-thumb-related-height\)\s*!important/,
|
/#main\.roomPage\s+ul\.list:has\(li\.roomCard\)\s+\.room_thumbnail_container[\s\S]*width:\s*100%\s*!important[\s\S]*height:\s*auto\s*!important/,
|
||||||
"room page related rooms thumbnail containers should follow card width and use detected height",
|
"room page related rooms thumbnail containers should follow card width and automatic height",
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
capturedCss,
|
capturedCss,
|
||||||
/\.BaseRoomContents\s+ul\.list:has\(li\.roomCard\)\s+\.room_thumbnail[\s\S]*width:\s*100%\s*!important[\s\S]*height:\s*var\(--tm-thumb-related-height\)\s*!important/,
|
/\.BaseRoomContents\s+ul\.list:has\(li\.roomCard\)\s+\.room_thumbnail[\s\S]*width:\s*100%\s*!important[\s\S]*height:\s*auto\s*!important/,
|
||||||
"base room related thumbnails should follow card width and use detected height",
|
"base room related thumbnails should follow card width and automatic height",
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
capturedCss,
|
capturedCss,
|
||||||
/\.BaseRoomContents\s+ul\.RoomCardGrid[\s\S]*grid-template-columns:\s*repeat\(auto-fill,\s*var\(--tm-thumb-related-width\)\)\s*!important/,
|
/\.BaseRoomContents\s+ul\.RoomCardGrid[\s\S]*grid-template-columns:\s*repeat\(auto-fill,\s*minmax\(var\(--tm-thumb-related-width\),\s*1fr\)\)\s*!important/,
|
||||||
"base room RoomCardGrid related rooms should use detected card width",
|
"base room RoomCardGrid related rooms should fill the row using detected scaled minimum width",
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
capturedCss,
|
capturedCss,
|
||||||
/\.BaseRoomContents\s+ul\.RoomCardGrid\s+\.RoomCardThumbnail[\s\S]*width:\s*100%\s*!important[\s\S]*height:\s*var\(--tm-thumb-related-height\)\s*!important/,
|
/\.BaseRoomContents\s+ul\.RoomCardGrid\s+\.RoomCardThumbnail[\s\S]*width:\s*100%\s*!important[\s\S]*height:\s*auto\s*!important/,
|
||||||
"base room RoomCardGrid thumbnails should follow card width and use detected height",
|
"base room RoomCardGrid thumbnails should follow card width and automatic height",
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
capturedCss,
|
capturedCss,
|
||||||
/\.BaseRoomContents\s+ul\.RoomCardGrid\s*>\s*li\.RoomCard[\s\S]*height:\s*var\(--tm-thumb-related-card-height\)\s*!important/,
|
/\.BaseRoomContents\s+ul\.RoomCardGrid\s*>\s*li\.RoomCard[\s\S]*height:\s*auto\s*!important[\s\S]*max-height:\s*none\s*!important/,
|
||||||
"base room RoomCardGrid related cards should use detected card height",
|
"base room RoomCardGrid related cards should use automatic height",
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
capturedCss,
|
capturedCss,
|
||||||
/\.BaseRoomContents\s+ul\.RoomCardGrid\s*>\s*li\.RoomCard[\s\S]*width:\s*var\(--tm-thumb-related-width\)\s*!important/,
|
/\.BaseRoomContents\s+ul\.RoomCardGrid\s*>\s*li\.RoomCard[\s\S]*width:\s*auto\s*!important[\s\S]*justify-self:\s*stretch\s*!important[\s\S]*max-width:\s*none\s*!important/,
|
||||||
"base room RoomCardGrid related cards should use detected card width",
|
"base room RoomCardGrid related cards should stretch to fill responsive columns",
|
||||||
);
|
);
|
||||||
assert.doesNotMatch(capturedCss, /--tm-thumb-min-root:\s*\d+px;/, "script should not hard-code a root thumbnail fallback width");
|
assert.doesNotMatch(capturedCss, /--tm-thumb-min-root:\s*\d+px;/, "script should not hard-code a root thumbnail fallback width");
|
||||||
assert.doesNotMatch(capturedCss, /--tm-thumb-height-root:\s*\d+px;/, "script should not hard-code a root thumbnail fallback height");
|
assert.doesNotMatch(capturedCss, /--tm-thumb-height-root:\s*\d+px;/, "script should not hard-code a root thumbnail fallback height");
|
||||||
@@ -151,6 +151,36 @@ assert.match(
|
|||||||
/height:\s*var\(--tm-thumb-discover-double-arrow\)\s*!important/,
|
/height:\s*var\(--tm-thumb-discover-double-arrow\)\s*!important/,
|
||||||
"discover carousel arrow heights should use detected original heights",
|
"discover carousel arrow heights should use detected original heights",
|
||||||
);
|
);
|
||||||
|
assert.match(
|
||||||
|
capturedCss,
|
||||||
|
/data-tm-thumb-scale-followed-page="1"[\s\S]*\.carousel-root\s+\.single-row\s+ul\.list[\s\S]*height:\s*auto\s*!important[\s\S]*max-height:\s*none\s*!important[\s\S]*overflow:\s*visible\s*!important/,
|
||||||
|
"followed-cams recommendations should release carousel list height after scaling",
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
capturedCss,
|
||||||
|
/data-tm-thumb-scale-followed-page="1"[\s\S]*grid-template-columns:\s*repeat\(auto-fill,\s*minmax\(var\(--tm-thumb-discover-width\),\s*1fr\)\)\s*!important/,
|
||||||
|
"followed-cams legacy recommendations should use responsive scaled minimum width",
|
||||||
|
);
|
||||||
|
assert.doesNotMatch(
|
||||||
|
capturedCss,
|
||||||
|
/data-tm-thumb-scale-followed-page="1"[\s\S]*grid-template-columns:\s*repeat\(2,/,
|
||||||
|
"followed-cams recommendations should not force a hard-coded two-column layout",
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
capturedCss,
|
||||||
|
/data-tm-thumb-scale-followed-recommendations="1"[\s\S]*data-tm-thumb-followed-recommendations-list="1"[\s\S]*grid-template-columns:\s*repeat\(auto-fill,\s*minmax\(var\(--tm-thumb-followed-recommendations-width\),\s*1fr\)\)\s*!important[\s\S]*grid-auto-rows:\s*auto\s*!important/,
|
||||||
|
"title-detected followed recommendations should use responsive scaled minimum columns and automatic row height",
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
capturedCss,
|
||||||
|
/data-tm-thumb-scale-followed-recommendations="1"[\s\S]*data-tm-thumb-followed-recommendations-card="1"[\s\S]*width:\s*auto\s*!important[\s\S]*max-width:\s*none\s*!important[\s\S]*height:\s*auto\s*!important[\s\S]*max-height:\s*none\s*!important/,
|
||||||
|
"title-detected followed recommendation cards should stretch responsively and grow naturally",
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
capturedCss,
|
||||||
|
/data-tm-thumb-scale-followed-recommendations="1"[\s\S]*data-tm-thumb-followed-recommendations-thumb="1"[\s\S]*height:\s*auto\s*!important/,
|
||||||
|
"title-detected followed recommendation thumbnails should keep automatic height",
|
||||||
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
capturedCss,
|
capturedCss,
|
||||||
/#discover_root\s+\.room-list-carousel\s+\.room_thumbnail[\s\S]*height:\s*var\(--tm-thumb-discover-height\)\s*!important/,
|
/#discover_root\s+\.room-list-carousel\s+\.room_thumbnail[\s\S]*height:\s*var\(--tm-thumb-discover-height\)\s*!important/,
|
||||||
@@ -420,12 +450,12 @@ vm.runInNewContext(source, {
|
|||||||
cardHeightScale: 1.55,
|
cardHeightScale: 1.55,
|
||||||
modules: {
|
modules: {
|
||||||
home: {
|
home: {
|
||||||
homeColumns: 7,
|
homeMinWidth: 190,
|
||||||
homeCard: { width: 174, height: 120 },
|
homeCard: { width: 174, height: 120 },
|
||||||
homeThumb: { width: 174, height: 98 },
|
homeThumb: { width: 174, height: 98 },
|
||||||
},
|
},
|
||||||
followingList: {
|
followingList: {
|
||||||
followingListColumns: 7,
|
followingListMinWidth: 190,
|
||||||
followingListCard: { width: 190, height: 120 },
|
followingListCard: { width: 190, height: 120 },
|
||||||
followingListThumb: { width: 188, height: 106 },
|
followingListThumb: { width: 188, height: 106 },
|
||||||
},
|
},
|
||||||
@@ -453,8 +483,8 @@ vm.runInNewContext(source, {
|
|||||||
|
|
||||||
assert.equal(followingAttrs.get("data-tm-thumb-scale-following-list"), undefined, "followed-cams should not apply legacy cached list module");
|
assert.equal(followingAttrs.get("data-tm-thumb-scale-following-list"), undefined, "followed-cams should not apply legacy cached list module");
|
||||||
assert.equal(followingAttrs.get("data-tm-thumb-scale-home"), undefined, "followed-cams should not apply legacy homepage cache");
|
assert.equal(followingAttrs.get("data-tm-thumb-scale-home"), undefined, "followed-cams should not apply legacy homepage cache");
|
||||||
assert.equal(followingVars.get("--tm-thumb-following-list-columns"), undefined);
|
assert.equal(followingVars.get("--tm-thumb-following-list-min-width"), undefined);
|
||||||
assert.equal(followingVars.get("--tm-thumb-following-list-card-height"), undefined);
|
assert.equal(followingVars.get("--tm-thumb-following-list-card-height"), undefined);
|
||||||
assert.equal(followingVars.get("--tm-thumb-following-list-thumb-width"), undefined);
|
assert.equal(followingVars.get("--tm-thumb-following-list-thumb-width"), undefined);
|
||||||
assert.equal(followingVars.get("--tm-thumb-following-list-thumb-height"), undefined);
|
assert.equal(followingVars.get("--tm-thumb-following-list-thumb-height"), undefined);
|
||||||
assert.equal(followingVars.get("--tm-thumb-home-columns"), undefined);
|
assert.equal(followingVars.get("--tm-thumb-home-min-width"), undefined);
|
||||||
|
|||||||
Reference in New Issue
Block a user