ZHIWEI666 commited on
Commit
ac326f9
·
verified ·
1 Parent(s): 1cd3cbe

Upload 104 files

Browse files
前端页面/core/网络请求_缓存管理.js CHANGED
@@ -844,6 +844,7 @@ export function invalidateRelatedCache(endpoint, method = null) {
844
 
845
  // 获取缓存TTL
846
  export function _getCacheTTL(endpoint) {
 
847
  for (const [pattern, ttl] of Object.entries(CACHE_CONFIG)) {
848
  if (endpoint.startsWith(pattern)) {
849
  return ttl;
 
844
 
845
  // 获取缓存TTL
846
  export function _getCacheTTL(endpoint) {
847
+ if (endpoint.split(/[?#]/)[0] === CACHE.PUBLIC_LISTS.prompts.endpoint) return CACHE.TTL.DETAIL_DATA;
848
  for (const [pattern, ttl] of Object.entries(CACHE_CONFIG)) {
849
  if (endpoint.startsWith(pattern)) {
850
  return ttl;
前端页面/market/创作者卡片组件.js CHANGED
@@ -345,7 +345,7 @@ export function createCreatorCard(creatorData, currentUser = null) {
345
  let commentUI = null;
346
  let isDetailLoaded = false;
347
  let isChartRendered = false;
348
- let chartInstance = null;
349
  let generation = 0, detailPending = false, chartPending = false, detailRead = null;
350
  let widthObserver = null, widthTimer = null, resizeTimer = null;
351
  let stopTrackingRemoval = null, resizeSubscribed = false;
@@ -368,6 +368,9 @@ export function createCreatorCard(creatorData, currentUser = null) {
368
  });
369
  const current = token => scope.isCurrent() && card.isConnected && detailView.style.display !== 'none' && token === generation;
370
  const clearChartWork = () => {
 
 
 
371
  widthObserver?.disconnect(); widthObserver = null;
372
  clearTimeout(widthTimer); clearTimeout(resizeTimer); widthTimer = resizeTimer = null;
373
  chartPending = false;
@@ -487,40 +490,78 @@ export function createCreatorCard(creatorData, currentUser = null) {
487
  renderTrendChart(detailData.trendData);
488
  }
489
 
490
- // 📊 渲染趋势图表
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
  function _initTrendChart(chartDom, trendData, token) {
492
  if (!current(token)) return;
493
  loadECharts().then(echarts => {
494
  if (!current(token) || !chartDom.isConnected) return;
495
  chartDom.innerHTML = "";
496
- const theme = getComputedStyle(card);
497
- const textColor = theme.getPropertyValue('--rk-text').trim() || theme.color;
498
- const borderColor = theme.getPropertyValue('--rk-border').trim() || theme.borderColor;
499
  chartInstance = echarts.init(chartDom, null, { backgroundColor: 'transparent' });
500
-
501
- const data = trendData || { months: [], tools: [], skills: [], apps: [], recommends: [] };
502
-
503
- chartInstance.setOption({
504
- tooltip: { trigger: 'axis', textStyle: { fontSize: 11 } },
505
- grid: { top: 10, bottom: 20, left: 35, right: 10, containLabel: true },
506
- xAxis: { type: 'category', data: data.months, axisLabel: { color: textColor, fontSize: 10 }, axisLine: { lineStyle: { color: borderColor } } },
507
- yAxis: { type: 'value', splitLine: { lineStyle: { color: borderColor, type: 'dashed' } }, axisLabel: { color: textColor, fontSize: 10 }, minInterval: 1 },
508
- series: [
509
- { name: t('creator.chart.series.tools'), type: 'line', data: data.tools, smooth: true, itemStyle: { color: '#4CAF50' }, lineStyle: { width: 2, type: 'dashed' } },
510
- { name: t('creator.chart.series.skills'), type: 'line', data: data.skills || [], smooth: true, itemStyle: { color: '#7C4DFF' }, lineStyle: { width: 2, type: 'dashed' } },
511
- { name: t('creator.chart.series.apps'), type: 'line', data: data.apps, smooth: true, itemStyle: { color: '#2196F3' }, lineStyle: { width: 2, type: 'dashed' } },
512
- { name: t('creator.chart.series.recommends'), type: 'line', data: data.recommends, smooth: true, itemStyle: { color: '#FF9800' }, lineStyle: { width: 2, type: 'dashed' } }
513
- ]
514
- });
515
- isChartRendered = true;
516
-
517
- // 初始化完成后延迟调用resize确保正确渲染
518
  resizeTimer = setTimeout(() => {
519
  resizeTimer = null;
520
  if (current(token) && chartInstance) chartInstance.resize();
521
  }, 300);
522
  }).catch(() => {
523
  if (!current(token)) return;
 
524
  chartInstance?.dispose(); chartInstance = null; isChartRendered = false;
525
  chartDom.innerHTML = `<div class="creator-card__chart-status creator-card__chart-status--error" role="status">${t('creator.chart.load_failed')}</div>`;
526
  }).finally(() => { if (current(token)) chartPending = false; });
@@ -529,11 +570,11 @@ export function createCreatorCard(creatorData, currentUser = null) {
529
  function renderTrendChart(trendData) {
530
  const token = generation;
531
  if (!current(token)) return;
532
- if (isChartRendered) { chartInstance?.resize(); return; }
 
 
533
  if (chartPending) return;
534
  chartPending = true;
535
- const chartDom = detailView.querySelector(`#${chartContainerId}`);
536
- if (!chartDom) { chartPending = false; return; }
537
 
538
  chartDom.innerHTML = `<div class="creator-card__chart-status" role="status">${t('creator.chart.loading')}</div>`;
539
 
 
345
  let commentUI = null;
346
  let isDetailLoaded = false;
347
  let isChartRendered = false;
348
+ let chartInstance = null, removeChartAppearance = null;
349
  let generation = 0, detailPending = false, chartPending = false, detailRead = null;
350
  let widthObserver = null, widthTimer = null, resizeTimer = null;
351
  let stopTrackingRemoval = null, resizeSubscribed = false;
 
368
  });
369
  const current = token => scope.isCurrent() && card.isConnected && detailView.style.display !== 'none' && token === generation;
370
  const clearChartWork = () => {
371
+ removeChartAppearance?.(); removeChartAppearance = null;
372
+ const chartDom = detailView.querySelector('.creator-card__chart');
373
+ chartDom?.removeAttribute('role'); chartDom?.removeAttribute('aria-label');
374
  widthObserver?.disconnect(); widthObserver = null;
375
  clearTimeout(widthTimer); clearTimeout(resizeTimer); widthTimer = resizeTimer = null;
376
  chartPending = false;
 
490
  renderTrendChart(detailData.trendData);
491
  }
492
 
493
+ function bindChartAppearance(chartDom, trendData, token) {
494
+ removeChartAppearance?.(); removeChartAppearance = null;
495
+ const instance = chartInstance;
496
+ const data = trendData || { months: [], tools: [], skills: [], apps: [], recommends: [] };
497
+ const refresh = () => {
498
+ if (!current(token) || chartInstance !== instance || !chartDom.isConnected) return;
499
+ try {
500
+ const theme = getComputedStyle(chartDom);
501
+ const textColor = theme.getPropertyValue('--rk-text').trim() || theme.color;
502
+ const borderColor = theme.getPropertyValue('--rk-border').trim() || theme.borderColor;
503
+ const panelColor = theme.getPropertyValue('--rk-panel').trim() || theme.backgroundColor;
504
+ const measuredSize = parseFloat(theme.fontSize);
505
+ const fontSize = Number.isFinite(measuredSize) ? measuredSize : 12;
506
+ const series = [
507
+ { name: t('creator.chart.series.tools'), type: 'line', data: data.tools, smooth: true, itemStyle: { color: '#4CAF50' }, lineStyle: { width: 2, type: 'dashed' } },
508
+ { name: t('creator.chart.series.skills'), type: 'line', data: data.skills || [], smooth: true, itemStyle: { color: '#7C4DFF' }, lineStyle: { width: 2, type: 'dashed' } },
509
+ { name: t('creator.chart.series.apps'), type: 'line', data: data.apps, smooth: true, itemStyle: { color: '#2196F3' }, lineStyle: { width: 2, type: 'dashed' } },
510
+ { name: t('creator.chart.series.recommends'), type: 'line', data: data.recommends, smooth: true, itemStyle: { color: '#FF9800' }, lineStyle: { width: 2, type: 'dashed' } }
511
+ ];
512
+ instance.setOption({
513
+ animation: getSettings().enableAnimations !== false && !window.matchMedia('(prefers-reduced-motion: reduce)').matches,
514
+ textStyle: { color: textColor, fontFamily: theme.fontFamily, fontSize },
515
+ tooltip: { trigger: 'axis', backgroundColor: panelColor, borderColor, textStyle: { color: textColor, fontSize } },
516
+ grid: { top: fontSize, bottom: fontSize * 1.5, left: fontSize, right: fontSize, containLabel: true },
517
+ xAxis: { type: 'category', data: data.months, axisLabel: { color: textColor, fontSize, hideOverlap: true, formatter: value => String(value).replace('-', '\n'), showMinLabel: true, showMaxLabel: true, alignMinLabel: 'left', alignMaxLabel: 'right' }, axisLine: { lineStyle: { color: borderColor } } },
518
+ yAxis: { type: 'value', splitLine: { lineStyle: { color: borderColor, type: 'dashed' } }, axisLabel: { color: textColor, fontSize }, minInterval: 1 },
519
+ series
520
+ });
521
+ chartDom.setAttribute('role', 'img');
522
+ chartDom.setAttribute('aria-label', series.map(entry => t('chart.data_description', {
523
+ series: entry.name,
524
+ values: data.months.flatMap((month, index) => entry.data?.[index] == null ? [] : [t('chart.month_value', { month, value: entry.data[index] })]).join('; ')
525
+ })).join(' '));
526
+ instance.resize();
527
+ } catch (_) {
528
+ if (!current(token) || chartInstance !== instance) return;
529
+ clearChartWork(); instance.dispose(); chartInstance = null; isChartRendered = false;
530
+ chartDom.innerHTML = `<div class="creator-card__chart-status creator-card__chart-status--error" role="status">${t('creator.chart.load_failed')}</div>`;
531
+ }
532
+ };
533
+ refresh();
534
+ if (!current(token) || chartInstance !== instance) return false;
535
+ const observer = new MutationObserver(refresh);
536
+ for (let ancestor = chartDom.parentElement; ancestor; ancestor = ancestor.parentElement) {
537
+ observer.observe(ancestor, { attributes: true, attributeFilter: ['class', 'style', 'data-theme', 'data-color-scheme'] });
538
+ }
539
+ const queries = ['(prefers-color-scheme: dark)', '(forced-colors: active)', '(prefers-reduced-motion: reduce)'].map(query => window.matchMedia(query));
540
+ for (const query of queries) query.addEventListener('change', refresh);
541
+ window.addEventListener('comfy-settings-changed', refresh);
542
+ removeChartAppearance = () => {
543
+ observer.disconnect();
544
+ for (const query of queries) query.removeEventListener('change', refresh);
545
+ window.removeEventListener('comfy-settings-changed', refresh);
546
+ };
547
+ return true;
548
+ }
549
+
550
  function _initTrendChart(chartDom, trendData, token) {
551
  if (!current(token)) return;
552
  loadECharts().then(echarts => {
553
  if (!current(token) || !chartDom.isConnected) return;
554
  chartDom.innerHTML = "";
 
 
 
555
  chartInstance = echarts.init(chartDom, null, { backgroundColor: 'transparent' });
556
+ isChartRendered = bindChartAppearance(chartDom, trendData, token);
557
+ if (!isChartRendered) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  resizeTimer = setTimeout(() => {
559
  resizeTimer = null;
560
  if (current(token) && chartInstance) chartInstance.resize();
561
  }, 300);
562
  }).catch(() => {
563
  if (!current(token)) return;
564
+ clearChartWork();
565
  chartInstance?.dispose(); chartInstance = null; isChartRendered = false;
566
  chartDom.innerHTML = `<div class="creator-card__chart-status creator-card__chart-status--error" role="status">${t('creator.chart.load_failed')}</div>`;
567
  }).finally(() => { if (current(token)) chartPending = false; });
 
570
  function renderTrendChart(trendData) {
571
  const token = generation;
572
  if (!current(token)) return;
573
+ const chartDom = detailView.querySelector(`#${chartContainerId}`);
574
+ if (!chartDom) return;
575
+ if (isChartRendered) { bindChartAppearance(chartDom, trendData, token); return; }
576
  if (chartPending) return;
577
  chartPending = true;
 
 
578
 
579
  chartDom.innerHTML = `<div class="creator-card__chart-status" role="status">${t('creator.chart.loading')}</div>`;
580
 
前端页面/profile/个人中心视图.js CHANGED
@@ -141,12 +141,19 @@ export function showUserProfile(initialUserData, currentUser = null, isMe = true
141
  observer.observe(document.body, { childList: true, subtree: true });
142
  scope.addCleanup(() => { observer.disconnect(); releaseChild(); });
143
 
144
- function render() {
 
 
 
 
 
 
145
  if (!scope.isCurrent()) return;
146
  const focused = document.activeElement;
147
  const ownedFocus = document.hasFocus() && container.contains(focused);
148
  const focusedTab = ownedFocus && focused.matches('.profile-tab-btn') ? focused.dataset.id : null;
149
- const focusedCount = ownedFocus && ['btn-followers', 'btn-following'].includes(focused.id) ? focused.id : null;
 
150
  releaseChild();
151
  const generation = ++renderGeneration;
152
  const current = () => scope.isCurrent() && renderGeneration === generation;
@@ -175,20 +182,34 @@ export function showUserProfile(initialUserData, currentUser = null, isMe = true
175
  container.innerHTML = buildProfileHTML(userData, isMe, isSettingsView, isFollowing, followingCount, activeTab, tabs);
176
 
177
  if (isSettingsView && isMe) {
 
178
  const settingsContainer = createSettingsForm(
179
  userData,
180
- () => { if (!current()) return; isSettingsView = false; render(); },
181
  (newUserData) => {
182
  if (!current()) return;
183
  userData = normalizeProfileData(newUserData, userData.account, true);
184
  if (container.updateData) container.updateData(userData);
185
  isSettingsView = false;
186
- render();
187
  },
188
  { isCurrent: current }
189
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  container.appendChild(settingsContainer);
191
- container.querySelector("#btn-back-profile").onclick = () => { isSettingsView = false; render(); };
192
  } else {
193
  container.querySelector("#btn-back-profile").onclick = () => window.dispatchEvent(new CustomEvent("comfy-route-back"));
194
 
@@ -218,7 +239,7 @@ export function showUserProfile(initialUserData, currentUser = null, isMe = true
218
  });
219
  };
220
 
221
- container.querySelector("#btn-open-settings").onclick = () => { isSettingsView = true; render(); };
222
  container.querySelector("#btn-logout").onclick = async () => {
223
  if (!current() || logoutPending) return;
224
  const currentAccount = getCurrentAccount();
@@ -253,6 +274,7 @@ export function showUserProfile(initialUserData, currentUser = null, isMe = true
253
  btnFollow.onclick = async () => {
254
  if (!current()) return;
255
  if (!currentUser) return showToast(t('feedback.login_required'), "warning");
 
256
  btnFollow.innerHTML = `⏳ ${t('common.processing')}...`; btnFollow.disabled = true;
257
  try {
258
  const newStatus = !isFollowing;
@@ -264,9 +286,15 @@ export function showUserProfile(initialUserData, currentUser = null, isMe = true
264
  if (!userData.followers) userData.followers = [];
265
  if (isFollowing) { if (!userData.followers.includes(currentUser.account)) userData.followers.push(currentUser.account); }
266
  else { userData.followers = userData.followers.filter(a => a !== currentUser.account); }
267
- render();
 
268
  } catch (_) { if (!current()) return; showToast(t('feedback.operation_failed'), "error"); btnFollow.innerHTML = isFollowing ? `✔️ ${t('social.followed')}` : `➕ ${t('social.follow')}`; }
269
- finally { if (current()) btnFollow.disabled = false; }
 
 
 
 
 
270
  };
271
  }
272
 
@@ -299,12 +327,11 @@ export function showUserProfile(initialUserData, currentUser = null, isMe = true
299
  listDOM._profileAddCleanup = cleanup => scope.addCleanup(cleanup);
300
  renderProfileListContent(activeTab, listDOM, userData, currentUser, openOtherUserProfileModal);
301
  }
302
- if ((focusedTab || focusedCount) && current() && container.isConnected
303
- && document.hasFocus() && document.activeElement === document.body) {
304
- const target = focusedTab
305
- ? [...container.querySelectorAll('.profile-tab-btn')].find(button => button.dataset.id === focusedTab)
306
- : container.querySelector('#' + focusedCount);
307
- if (target && !target.disabled && target.getClientRects().length) target.focus({ preventScroll: true });
308
  }
309
  }
310
 
 
141
  observer.observe(document.body, { childList: true, subtree: true });
142
  scope.addCleanup(() => { observer.disconnect(); releaseChild(); });
143
 
144
+ function restoreOwnedFocus(target, owned) {
145
+ if (owned && scope.isCurrent() && container.isConnected && document.hasFocus()
146
+ && document.activeElement === document.body && target?.isConnected
147
+ && !target.disabled && target.getClientRects().length) target.focus({ preventScroll: true });
148
+ }
149
+
150
+ function render(focusTarget = null, allowBodyFocus = false) {
151
  if (!scope.isCurrent()) return;
152
  const focused = document.activeElement;
153
  const ownedFocus = document.hasFocus() && container.contains(focused);
154
  const focusedTab = ownedFocus && focused.matches('.profile-tab-btn') ? focused.dataset.id : null;
155
+ const focusedControl = ownedFocus && focused.matches('.profile-header button[id]') ? focused.id : null;
156
+ const actionFocus = focusTarget && (ownedFocus || (allowBodyFocus && focused === document.body));
157
  releaseChild();
158
  const generation = ++renderGeneration;
159
  const current = () => scope.isCurrent() && renderGeneration === generation;
 
182
  container.innerHTML = buildProfileHTML(userData, isMe, isSettingsView, isFollowing, followingCount, activeTab, tabs);
183
 
184
  if (isSettingsView && isMe) {
185
+ let settingsSubmitFocused = false;
186
  const settingsContainer = createSettingsForm(
187
  userData,
188
+ () => { if (!current()) return; isSettingsView = false; render('btn-open-settings'); },
189
  (newUserData) => {
190
  if (!current()) return;
191
  userData = normalizeProfileData(newUserData, userData.account, true);
192
  if (container.updateData) container.updateData(userData);
193
  isSettingsView = false;
194
+ render('btn-open-settings', settingsSubmitFocused);
195
  },
196
  { isCurrent: current }
197
  );
198
+ const captureSettingsSubmitFocus = event => {
199
+ if (!current() || !settingsContainer.isConnected) return;
200
+ const saveButton = settingsContainer.querySelector('#btn-save-settings');
201
+ if (saveButton?.contains(event.target)) {
202
+ settingsSubmitFocused = document.hasFocus() && settingsContainer.contains(document.activeElement);
203
+ }
204
+ };
205
+ settingsContainer.addEventListener('click', captureSettingsSubmitFocus, true);
206
+ const cleanupSettings = settingsContainer._cleanup;
207
+ settingsContainer._cleanup = () => {
208
+ settingsContainer.removeEventListener('click', captureSettingsSubmitFocus, true);
209
+ cleanupSettings();
210
+ };
211
  container.appendChild(settingsContainer);
212
+ container.querySelector("#btn-back-profile").onclick = () => { isSettingsView = false; render('btn-open-settings'); };
213
  } else {
214
  container.querySelector("#btn-back-profile").onclick = () => window.dispatchEvent(new CustomEvent("comfy-route-back"));
215
 
 
239
  });
240
  };
241
 
242
+ container.querySelector("#btn-open-settings").onclick = () => { isSettingsView = true; render('setting-name'); };
243
  container.querySelector("#btn-logout").onclick = async () => {
244
  if (!current() || logoutPending) return;
245
  const currentAccount = getCurrentAccount();
 
274
  btnFollow.onclick = async () => {
275
  if (!current()) return;
276
  if (!currentUser) return showToast(t('feedback.login_required'), "warning");
277
+ const followFocusOwned = document.hasFocus() && document.activeElement === btnFollow;
278
  btnFollow.innerHTML = `⏳ ${t('common.processing')}...`; btnFollow.disabled = true;
279
  try {
280
  const newStatus = !isFollowing;
 
286
  if (!userData.followers) userData.followers = [];
287
  if (isFollowing) { if (!userData.followers.includes(currentUser.account)) userData.followers.push(currentUser.account); }
288
  else { userData.followers = userData.followers.filter(a => a !== currentUser.account); }
289
+ const restoreFollow = followFocusOwned && [document.body, btnFollow].includes(document.activeElement);
290
+ render(restoreFollow ? 'btn-follow-user' : null, restoreFollow);
291
  } catch (_) { if (!current()) return; showToast(t('feedback.operation_failed'), "error"); btnFollow.innerHTML = isFollowing ? `✔️ ${t('social.followed')}` : `➕ ${t('social.follow')}`; }
292
+ finally {
293
+ if (current()) {
294
+ btnFollow.disabled = false;
295
+ restoreOwnedFocus(btnFollow, followFocusOwned);
296
+ }
297
+ }
298
  };
299
  }
300
 
 
327
  listDOM._profileAddCleanup = cleanup => scope.addCleanup(cleanup);
328
  renderProfileListContent(activeTab, listDOM, userData, currentUser, openOtherUserProfileModal);
329
  }
330
+ if (current()) {
331
+ const target = actionFocus ? container.querySelector('#' + focusTarget)
332
+ : focusedTab ? [...container.querySelectorAll('.profile-tab-btn')].find(button => button.dataset.id === focusedTab)
333
+ : focusedControl ? [...container.querySelectorAll('.profile-header button[id]')].find(button => button.id === focusedControl) : null;
334
+ restoreOwnedFocus(target, actionFocus || focusedTab || focusedControl);
 
335
  }
336
  }
337
 
前端页面/styles/主题基础.css CHANGED
@@ -353,15 +353,18 @@
353
  .comfy-ranking-root .top-nav button:focus-visible { outline: 2px solid var(--rk-focus); outline-offset: 2px; }
354
  @media (hover: hover) { .comfy-ranking-root .top-nav__icon:hover, .comfy-ranking-root .top-nav__identity:hover { border-color: var(--rk-focus); } .comfy-ranking-root .top-nav__brand:hover { opacity: .85; } }
355
 
 
 
356
  .comfy-ranking-root .top-banner {
357
  display: flex;
358
  flex: 0 0 100%;
359
  width: 100%;
360
  min-width: 0;
361
- max-height: 64px;
 
362
  justify-content: center;
363
  align-items: center;
364
- margin: 0;
365
  padding: 0;
366
  overflow: hidden;
367
  border: 0;
@@ -377,10 +380,10 @@
377
  }
378
 
379
  .comfy-ranking-root .top-banner__image {
380
- max-width: 100%;
381
  width: auto;
382
  height: auto;
383
- max-height: 64px;
384
  display: block;
385
  object-fit: contain;
386
  }
@@ -1581,7 +1584,7 @@ button.ranking-rating__star { cursor: pointer; min-width: 28px; min-height: 28px
1581
  .creator-card__detail[hidden] { display:none !important; }
1582
  .creator-card__section-label { font-size:12px; font-weight:700; margin-bottom:8px; }
1583
  .creator-card__section-label--comments { margin-top:16px; }
1584
- .creator-card__chart { width:100%; height:180px; background:var(--rk-bg,var(--comfy-menu-bg,#202020)); border-radius:6px; margin-bottom:12px; }
1585
  .creator-card__comments { height:250px; border:1px solid var(--rk-border,#454545); border-radius:6px; overflow:hidden; }
1586
  .creator-card__status { padding:32px 12px; text-align:center; color:var(--rk-muted,var(--descrip-text,#a4a4a4)); }
1587
  .creator-card__status--error, .creator-card__chart-status--error { color:var(--rk-danger,#ef5350); }
 
353
  .comfy-ranking-root .top-nav button:focus-visible { outline: 2px solid var(--rk-focus); outline-offset: 2px; }
354
  @media (hover: hover) { .comfy-ranking-root .top-nav__icon:hover, .comfy-ranking-root .top-nav__identity:hover { border-color: var(--rk-focus); } .comfy-ranking-root .top-nav__brand:hover { opacity: .85; } }
355
 
356
+ .comfy-ranking-root .top-nav:has(> .top-banner > .top-banner__image) { padding-bottom: 0; }
357
+
358
  .comfy-ranking-root .top-banner {
359
  display: flex;
360
  flex: 0 0 100%;
361
  width: 100%;
362
  min-width: 0;
363
+ min-height: 0;
364
+ max-height: calc(64px * 2 / 3);
365
  justify-content: center;
366
  align-items: center;
367
+ margin: -8px 0 0;
368
  padding: 0;
369
  overflow: hidden;
370
  border: 0;
 
380
  }
381
 
382
  .comfy-ranking-root .top-banner__image {
383
+ max-width: calc(100% * 2 / 3);
384
  width: auto;
385
  height: auto;
386
+ max-height: calc(64px * 2 / 3);
387
  display: block;
388
  object-fit: contain;
389
  }
 
1584
  .creator-card__detail[hidden] { display:none !important; }
1585
  .creator-card__section-label { font-size:12px; font-weight:700; margin-bottom:8px; }
1586
  .creator-card__section-label--comments { margin-top:16px; }
1587
+ .creator-card__chart { width:100%; height:max(180px,11.25rem); font-size:.75rem; background:var(--rk-bg,var(--comfy-menu-bg,#202020)); border-radius:6px; margin-bottom:12px; }
1588
  .creator-card__comments { height:250px; border:1px solid var(--rk-border,#454545); border-radius:6px; overflow:hidden; }
1589
  .creator-card__status { padding:32px 12px; text-align:center; color:var(--rk-muted,var(--descrip-text,#a4a4a4)); }
1590
  .creator-card__status--error, .creator-card__chart-status--error { color:var(--rk-danger,#ef5350); }