合并 origin/master:保留 3D 契约与 provider checkpoint,旧玩法表随主线退役
- server-rs/crates/shared-contracts/src/lib.rs:采用主线的模块裁剪(删掉 cfg(any()) 遗留模块、补上 agc_analytics 与 game_distribution),同时保留本分支的 editor_canvas、model3d 模块与 EDITOR_GENERATION_OPERATION_KINDS 导出 - server-rs/crates/spacetime-module/src/migration.rs:主线删除的 410 行旧玩法表 normalize 段保持删除,保留本分支的 provider_kind / provider_task_id 兼容段与对应用例 - docs/【开发运维】本地开发验证与生产运维-2026-05-15.md:校验清单同时保留 Tripo 3D 生成任务与 editor_background_music_generation / model3d_text_to_model / model3d_image_to_model - .gitignore:补回本分支新增的 3D 模型文件忽略规则(*.glb / *.gltf 等 12 行),压测数据段随主线一并删除 - 共享记忆:本分支的 3D 决策与踩坑条目保留在主线重排后的 decision-log.md 与 pitfalls.md 中
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Search, Tag } from 'lucide-react';
|
||||
import type { Ref } from 'react';
|
||||
|
||||
import { getPlatformCategoryChipClassName } from './platformCategoryChipModel';
|
||||
import { PlatformSegmentedTabs } from './PlatformSegmentedTabs';
|
||||
|
||||
export type PlatformResourceFilterOption<TId extends string = string> = {
|
||||
@@ -53,15 +54,6 @@ export type PlatformResourceFilterBarProps<TId extends string = string> =
|
||||
onToggleTag: (tag: string) => void;
|
||||
});
|
||||
|
||||
function tagChipClassName(active: boolean) {
|
||||
return [
|
||||
'platform-category-chip gap-1.5 px-2.5 text-xs font-bold',
|
||||
active ? 'platform-category-chip--active' : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源搜索 + 功能分类 + 标签的筛选条。
|
||||
*
|
||||
@@ -115,6 +107,9 @@ export function PlatformResourceFilterBar<TId extends string = string>({
|
||||
frame="bare"
|
||||
surface="transparent"
|
||||
size="sm"
|
||||
// 选中项与下面的标签 chip 用同一套实心口径:筛选条里「哪个条件在生效」
|
||||
// 只允许一种视觉语言。
|
||||
tone="accent"
|
||||
className="platform-theme platform-theme--light min-w-0 flex-none"
|
||||
/>
|
||||
{tagOptions.length > 0 ? (
|
||||
@@ -130,7 +125,7 @@ export function PlatformResourceFilterBar<TId extends string = string>({
|
||||
key={option.tag}
|
||||
type="button"
|
||||
aria-pressed={active}
|
||||
className={tagChipClassName(active)}
|
||||
className={getPlatformCategoryChipClassName(active)}
|
||||
// 类型上 `tagItems` 必然带 `onToggleTag`;这一层兜底是给不带类型检查的
|
||||
// 调用方(JS / 动态构造的 props):没有处理函数就不该渲染成可点按钮。
|
||||
disabled={!onToggleTag}
|
||||
@@ -138,7 +133,12 @@ export function PlatformResourceFilterBar<TId extends string = string>({
|
||||
>
|
||||
<Tag className="h-3 w-3" aria-hidden="true" />
|
||||
<span>{option.tag}</span>
|
||||
<span aria-hidden="true">{option.assetCount}</span>
|
||||
<span
|
||||
className="platform-category-chip__count"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{option.assetCount}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -19,6 +19,7 @@ export type PlatformSegmentedTabsTone =
|
||||
| 'neutral'
|
||||
| 'warm'
|
||||
| 'rose'
|
||||
| 'accent'
|
||||
| 'underline';
|
||||
export type PlatformSegmentedTabsFrame = 'panel' | 'bare';
|
||||
export type PlatformSegmentedTabsSemantics = 'segment' | 'tabs';
|
||||
@@ -127,6 +128,12 @@ const PLATFORM_SEGMENTED_TABS_TONE_CLASS: Record<
|
||||
'border border-[#ff7890] bg-[linear-gradient(180deg,#ff7890_0%,#ff4f6a_100%)] text-white shadow-[0_8px_18px_rgba(244,63,94,0.16)]',
|
||||
idle: 'border border-[var(--platform-subpanel-border)] bg-white/76 text-[var(--platform-text-strong)] hover:border-[var(--platform-surface-hover-border)] hover:bg-white',
|
||||
},
|
||||
// 与筛选 chip 的选中态共用同一组语义色:实心填充 + 反白文字。
|
||||
// 未选中的悬停只加淡暖底 + 加深文字,不换成品牌色 —— 品牌色是「已选中」专用的。
|
||||
accent: {
|
||||
active: 'platform-segmented-tabs__item--solid',
|
||||
idle: 'text-[var(--platform-text-base)] hover:bg-[var(--platform-warm-bg)] hover:text-[var(--platform-text-strong)]',
|
||||
},
|
||||
underline: {
|
||||
active: 'text-[var(--platform-text-strong)]',
|
||||
idle: 'text-[var(--platform-text-muted)] hover:text-[var(--platform-text-base)]',
|
||||
|
||||
@@ -17,6 +17,7 @@ export type {
|
||||
export { PlatformBackActionButton } from './PlatformBackActionButton';
|
||||
export type { PlatformBadgeProps, PlatformBadgeTone } from './PlatformBadge';
|
||||
export { PlatformBadge } from './PlatformBadge';
|
||||
export { getPlatformCategoryChipClassName } from './platformCategoryChipModel';
|
||||
export type {
|
||||
PlatformEmptyStateProps,
|
||||
PlatformEmptyStateSize,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { getPlatformCategoryChipClassName } from './platformCategoryChipModel';
|
||||
|
||||
describe('getPlatformCategoryChipClassName', () => {
|
||||
it('returns the shared chip chrome and only marks the active state when selected', () => {
|
||||
const idle = getPlatformCategoryChipClassName(false);
|
||||
expect(idle).toContain('platform-category-chip');
|
||||
expect(idle).not.toContain('platform-category-chip--active');
|
||||
|
||||
const active = getPlatformCategoryChipClassName(true);
|
||||
expect(active).toContain('platform-category-chip');
|
||||
expect(active).toContain('platform-category-chip--active');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* 分类 / 标签 chip 的类名口径。
|
||||
*
|
||||
* `platform-category-chip` 的公共表现(高度、圆角、hover、focus ring)挂在共享样式表里,
|
||||
* 这里只负责「选中态」这一处分支,让资源画布、参考图弹窗、模板库这些宿主共用同一份定义,
|
||||
* 不再各抄一遍类名字符串。
|
||||
*/
|
||||
export function getPlatformCategoryChipClassName(active: boolean): string {
|
||||
return [
|
||||
'platform-category-chip gap-1.5 px-2.5 text-xs font-bold',
|
||||
active ? 'platform-category-chip--active' : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
}
|
||||
@@ -540,7 +540,7 @@
|
||||
justify-content: center;
|
||||
border: 1px solid var(--platform-subpanel-border);
|
||||
border-radius: 0.78rem;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
background: var(--platform-chip-idle-fill, rgba(255, 253, 250, 0.72));
|
||||
color: var(--platform-text-base);
|
||||
padding: 0 0.92rem;
|
||||
font-size: 0.88rem;
|
||||
@@ -548,10 +548,59 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 状态阶梯(三个状态必须一眼分得开):
|
||||
静止 = 浅底 + 中性描边;悬停 = 中性加描边 + 极淡暖底 + 常规文字(**不能**用品牌色,
|
||||
否则一颗未选中的 chip 悬停起来就像已选中,等于把「选中」这个强状态用掉了);
|
||||
按下 = 再压一层;选中 = 实心品牌填充 + 反白文字,是唯一的强状态。 */
|
||||
.platform-category-chip:not(.platform-category-chip--active):hover {
|
||||
border-color: var(--platform-surface-hover-border, rgba(204, 117, 76, 0.42));
|
||||
background: var(--platform-warm-bg, rgba(234, 204, 179, 0.32));
|
||||
color: var(--platform-text-strong);
|
||||
}
|
||||
|
||||
.platform-category-chip:not(.platform-category-chip--active):active {
|
||||
border-color: var(--platform-warm-border, rgba(204, 117, 76, 0.28));
|
||||
background: var(--platform-warm-bg, rgba(234, 204, 179, 0.32));
|
||||
}
|
||||
|
||||
.platform-category-chip--active {
|
||||
border-color: var(--platform-cool-border);
|
||||
background: var(--platform-cool-bg);
|
||||
color: var(--platform-cool-text);
|
||||
border-color: var(--platform-chip-active-border, #7d3719);
|
||||
background: var(
|
||||
--platform-chip-active-fill,
|
||||
linear-gradient(135deg, #b3542f, #8f3f22)
|
||||
);
|
||||
color: var(--platform-chip-active-text, #fffaf5);
|
||||
box-shadow: var(
|
||||
--platform-chip-active-shadow,
|
||||
0 6px 14px rgba(150, 71, 39, 0.24)
|
||||
);
|
||||
}
|
||||
|
||||
/* chip 里的计数(标签命中数量):次要信息,比标签文字轻一档;
|
||||
选中态跟着标签文字一起变白,不参与不透明度衰减,避免落在 AA 以下。 */
|
||||
.platform-category-chip__count {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
color: var(--platform-text-soft);
|
||||
}
|
||||
|
||||
.platform-category-chip--active .platform-category-chip__count {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* 分段页签的「实心选中」项:与筛选 chip 共用同一组语义色,
|
||||
避免两处各写一套「选中长什么样」。 */
|
||||
.platform-segmented-tabs__item--solid {
|
||||
border-color: var(--platform-chip-active-border, #7d3719);
|
||||
background: var(
|
||||
--platform-chip-active-fill,
|
||||
linear-gradient(135deg, #b3542f, #8f3f22)
|
||||
);
|
||||
color: var(--platform-chip-active-text, #fffaf5);
|
||||
box-shadow: var(
|
||||
--platform-chip-active-shadow,
|
||||
0 6px 14px rgba(150, 71, 39, 0.24)
|
||||
);
|
||||
}
|
||||
|
||||
.platform-category-sort-button {
|
||||
@@ -586,8 +635,11 @@
|
||||
.platform-category-filter-button:focus-visible,
|
||||
.platform-category-chip:focus-visible,
|
||||
.platform-category-sort-button:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px var(--platform-input-focus-ring);
|
||||
/* 焦点环用 outline 而不是 box-shadow:选中态自己带 box-shadow(实心 chip 的投影),
|
||||
用 box-shadow 画环会被状态投影覆盖,选中的 chip 就完全没有焦点提示。outline 不参与
|
||||
box-shadow 的层叠,也不会撑开布局(offset 2px 落在元素外),选中态自己的投影照旧。 */
|
||||
outline: 2px solid var(--platform-input-focus-ring);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.platform-navigable-list-item {
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
export const GAME_DISTRIBUTION_CATEGORIES = [
|
||||
'休闲',
|
||||
'益智',
|
||||
'动作',
|
||||
'冒险',
|
||||
'模拟',
|
||||
'策略',
|
||||
'其他',
|
||||
] as const;
|
||||
|
||||
export type GameDistributionCategory =
|
||||
(typeof GAME_DISTRIBUTION_CATEGORIES)[number];
|
||||
|
||||
export type GameDistributionPublishMetadataSuggestionRequest = {
|
||||
name: string;
|
||||
goal?: string | null;
|
||||
/** 脱敏且有界的项目上下文摘要。 */
|
||||
context?: string | null;
|
||||
};
|
||||
|
||||
export type GameDistributionPublishMetadataSuggestion = {
|
||||
summary: string;
|
||||
category: GameDistributionCategory;
|
||||
};
|
||||
|
||||
export type GameDistributionDeviceSupport = {
|
||||
desktop: boolean;
|
||||
mobile: boolean;
|
||||
touch: boolean;
|
||||
};
|
||||
|
||||
export type GameDistributionInputMode = 'keyboard' | 'mouse' | 'touch';
|
||||
export type GameDistributionOrientation =
|
||||
| 'landscape'
|
||||
| 'portrait'
|
||||
| 'responsive';
|
||||
|
||||
export type GameDistributionAuthor = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl?: string | null;
|
||||
};
|
||||
|
||||
export type GameDistributionVersionStatus =
|
||||
| 'awaiting_upload'
|
||||
| 'uploaded'
|
||||
| 'validating'
|
||||
| 'pending_review'
|
||||
| 'published'
|
||||
| 'upload_failed'
|
||||
| 'validation_failed'
|
||||
| 'rejected'
|
||||
| 'cancelled'
|
||||
| 'revoked';
|
||||
|
||||
export type GameDistributionGameVisibility =
|
||||
| 'unpublished'
|
||||
| 'published'
|
||||
| 'suspended';
|
||||
|
||||
export type GameDistributionVersionSummary = {
|
||||
id: string;
|
||||
version: string;
|
||||
entryUrl: string;
|
||||
sha256: string;
|
||||
publishedAt: string;
|
||||
controls: string[];
|
||||
};
|
||||
|
||||
export type GameDistributionGame = {
|
||||
id: string;
|
||||
title: string;
|
||||
summary: string;
|
||||
description: string;
|
||||
category: GameDistributionCategory;
|
||||
tags: string[];
|
||||
coverColor: string;
|
||||
icon: string;
|
||||
/**
|
||||
* 公开封面的 OSS objectKey;未上传封面时为空,展示层需回退到 coverColor/icon 占位。
|
||||
* 通过 `/api/assets/read-url` 换签名 URL 读取,不接受直连外链。
|
||||
*/
|
||||
coverObjectKey?: string | null;
|
||||
/** 公开截图的 OSS objectKey 列表,最多 6 张,随版本冻结。 */
|
||||
screenshots?: string[];
|
||||
author: GameDistributionAuthor;
|
||||
deviceSupport: GameDistributionDeviceSupport;
|
||||
inputModes?: GameDistributionInputMode[];
|
||||
orientation?: GameDistributionOrientation;
|
||||
status: Extract<GameDistributionGameVisibility, 'published' | 'unpublished'>;
|
||||
/** 公开切换 CAS 版本号;作者下架与管理员审核都必须回传当前值。 */
|
||||
publicationRevision: number;
|
||||
currentVersion: GameDistributionVersionSummary | null;
|
||||
playCount: number;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
export type GameDistributionListResponse = {
|
||||
games: GameDistributionGame[];
|
||||
nextCursor?: string | null;
|
||||
};
|
||||
|
||||
export type GameDistributionCreateGameRequest = {
|
||||
/** 发布方本地项目标识;同一作者重复发布会复用既有 gameId。 */
|
||||
localProjectId?: string | null;
|
||||
title: string;
|
||||
summary: string;
|
||||
description?: string;
|
||||
category: GameDistributionCategory;
|
||||
tags?: string[];
|
||||
coverAssetId?: string;
|
||||
/** 截图素材 ID(最多 6 张,复用平台图片上传与归属校验)。 */
|
||||
screenshots?: string[];
|
||||
deviceSupport: GameDistributionDeviceSupport;
|
||||
inputModes: GameDistributionInputMode[];
|
||||
orientation: GameDistributionOrientation;
|
||||
};
|
||||
|
||||
export type GameDistributionCreateVersionRequest = {
|
||||
localProjectId?: string | null;
|
||||
packageSha256: string;
|
||||
packageBytes: number;
|
||||
packageFileCount: number;
|
||||
packageEntryPath: 'index.html';
|
||||
gameMetadata: GameDistributionCreateGameRequest;
|
||||
};
|
||||
|
||||
export type GameDistributionPrivateVersion = {
|
||||
versionId: string;
|
||||
gameId: string;
|
||||
versionNumber: number;
|
||||
packageSha256: string;
|
||||
packageBytes: number;
|
||||
status: GameDistributionVersionStatus;
|
||||
/** 游戏公开修订号;撤回与审核动作都必须回传当前值做 CAS。 */
|
||||
publicationRevision: number;
|
||||
reviewReason?: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* 版本状态的下一步动作,由服务端派生;客户端只按它渲染主行动作,不自行推断状态。
|
||||
*/
|
||||
export type GameDistributionRecoveryAction =
|
||||
| 'upload'
|
||||
| 'submit'
|
||||
| 'wait'
|
||||
| 'none'
|
||||
| 'reupload'
|
||||
| 'fix_package'
|
||||
| 'fix_metadata';
|
||||
|
||||
/** 冻结资料里的截图:同时保留素材 ID(作者续发可复用)与对象键(展示换签用)。 */
|
||||
export type GameDistributionFrozenScreenshot = {
|
||||
assetId: string;
|
||||
objectKey: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* 随版本冻结的游戏资料快照。
|
||||
*
|
||||
* 只有作者本人(版本回读)与管理员(审核回读)会拿到素材 ID;公开投影只给对象键。
|
||||
* 历史版本可能没有快照,读取方必须按空值处理。
|
||||
*/
|
||||
export type GameDistributionVersionFrozenMetadata = {
|
||||
title?: string;
|
||||
summary?: string;
|
||||
description?: string;
|
||||
category?: GameDistributionCategory;
|
||||
tags?: string[];
|
||||
coverAssetId?: string | null;
|
||||
coverObjectKey?: string | null;
|
||||
screenshots?: GameDistributionFrozenScreenshot[];
|
||||
deviceSupport?: GameDistributionDeviceSupport;
|
||||
inputModes?: GameDistributionInputMode[];
|
||||
orientation?: GameDistributionOrientation;
|
||||
};
|
||||
|
||||
export type GameDistributionVersionDetail = {
|
||||
game: GameDistributionGame;
|
||||
version: GameDistributionPrivateVersion & {
|
||||
recoveryAction: GameDistributionRecoveryAction;
|
||||
frozenMetadata?: GameDistributionVersionFrozenMetadata | null;
|
||||
};
|
||||
};
|
||||
|
||||
export type GameDistributionCancelVersionRequest = {
|
||||
expectedPublicationRevision: number;
|
||||
reason?: string;
|
||||
};
|
||||
|
||||
export type GameDistributionCancelVersionResponse = {
|
||||
game: GameDistributionGame;
|
||||
version: GameDistributionPrivateVersion;
|
||||
replayed: boolean;
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './editorAgent';
|
||||
export type * from './editorAudio';
|
||||
export * from './gameCreationApp';
|
||||
export * from './gameDistribution';
|
||||
export * from './hostBridge';
|
||||
export type * from './hyper3d';
|
||||
export type * from './model3d';
|
||||
|
||||
@@ -5,6 +5,7 @@ export * from './contracts/common';
|
||||
export type * from './contracts/editorAudio';
|
||||
export * from './contracts/editorScene';
|
||||
export * from './contracts/externalGeneration';
|
||||
export * from './contracts/gameDistribution';
|
||||
export type * from './contracts/hyper3d';
|
||||
export type * from './contracts/model3d';
|
||||
export * from './contracts/runtime';
|
||||
|
||||
@@ -75,6 +75,15 @@
|
||||
--platform-cool-border: rgba(199, 117, 76, 0.24);
|
||||
--platform-cool-bg: rgba(238, 208, 183, 0.26);
|
||||
--platform-cool-text: #b76038;
|
||||
/* 筛选 chip 的两态:未选是浅底描边,选中是**实心填充**。选中填充必须深到
|
||||
白字仍过 WCAG AA,并且与未选底色的对比 ≥ 3:1 —— 否则「选中 / 未选中」
|
||||
在米色页面上肉眼分不出来(旧口径两态对比只有 1.09:1)。
|
||||
口径由 apps/ai-game-creator-shell/tests/workbenchThemeContrast.test.ts 钉住。 */
|
||||
--platform-chip-idle-fill: rgba(255, 253, 250, 0.72);
|
||||
--platform-chip-active-fill: linear-gradient(135deg, #b3542f, #8f3f22);
|
||||
--platform-chip-active-border: #7d3719;
|
||||
--platform-chip-active-text: #fffaf5;
|
||||
--platform-chip-active-shadow: 0 6px 14px rgba(150, 71, 39, 0.24);
|
||||
--platform-neutral-border: rgba(226, 203, 184, 0.44);
|
||||
--platform-neutral-bg: rgba(255, 253, 250, 0.7);
|
||||
--platform-neutral-text: #7b6150;
|
||||
@@ -150,7 +159,10 @@
|
||||
--platform-input-fill: rgba(255, 253, 250, 0.94);
|
||||
--platform-input-fill-focus: rgba(255, 254, 252, 0.96);
|
||||
--platform-input-highlight: rgba(255, 255, 255, 0.9);
|
||||
--platform-input-focus-ring: rgba(204, 117, 76, 0.15);
|
||||
/* 焦点环必须**看得见**:原来 15% 透明度合成到页面底色后只有 1.17:1,键盘用户
|
||||
根本看不到焦点在哪。改成实心品牌色,对页面底色 4.3:1(≥ WCAG 非文本对比 3:1),
|
||||
由 tests/workbenchThemeContrast.test.ts 钉住。 */
|
||||
--platform-input-focus-ring: #b6623f;
|
||||
--platform-nav-item-text: #80695a;
|
||||
--platform-nav-item-text-active: #3d1f10;
|
||||
--platform-nav-item-hover-fill: rgba(253, 248, 243, 0.94);
|
||||
@@ -343,6 +355,14 @@
|
||||
--platform-cool-border: rgba(103, 232, 249, 0.22);
|
||||
--platform-cool-bg: rgba(8, 145, 178, 0.14);
|
||||
--platform-cool-text: rgb(236 254 255);
|
||||
/* 同浅色主题:选中态是实心填充。深色皮肤里「填充比底色更亮」才是可分辨的选中,
|
||||
所以这里用亮靛蓝填充 + 深色文字,同时在渐变两端都满足
|
||||
「文字 ≥ 4.5:1」「与未选底色 ≥ 3:1」。 */
|
||||
--platform-chip-idle-fill: rgba(255, 255, 255, 0.05);
|
||||
--platform-chip-active-fill: linear-gradient(135deg, #9fb0ff, #7b8cff);
|
||||
--platform-chip-active-border: #b7c4ff;
|
||||
--platform-chip-active-text: #0b1030;
|
||||
--platform-chip-active-shadow: 0 8px 18px rgba(77, 92, 245, 0.32);
|
||||
--platform-neutral-border: rgba(255, 255, 255, 0.08);
|
||||
--platform-neutral-bg: rgba(255, 255, 255, 0.05);
|
||||
--platform-neutral-text: rgb(228 228 231);
|
||||
@@ -414,7 +434,8 @@
|
||||
--platform-input-fill: rgba(255, 255, 255, 0.05);
|
||||
--platform-input-fill-focus: rgba(255, 255, 255, 0.08);
|
||||
--platform-input-highlight: rgba(255, 255, 255, 0.06);
|
||||
--platform-input-focus-ring: rgba(91, 108, 255, 0.22);
|
||||
/* 同浅色主题:深色皮肤用亮靛蓝焦点环,对深色底色 6.5:1。 */
|
||||
--platform-input-focus-ring: #9fb0ff;
|
||||
--platform-nav-item-text: rgb(161 161 170);
|
||||
--platform-nav-item-text-active: rgb(238 248 255);
|
||||
--platform-nav-item-hover-fill: rgba(91, 108, 255, 0.08);
|
||||
|
||||
Reference in New Issue
Block a user