完善创作主页精选素材预览
新增精选素材分组模型,按素材包、角色、UI、音乐、美宣等分类组合展示素材 补齐素材卡片和预览弹窗中的提示词、作者与生成总成本展示 新增接近全屏的素材预览弹窗,支持底部外置缩略图导航切换 让私有图片、视频和音频预览携带 objectKey 换签读取 补充创作主页素材展示文档和聚焦测试
This commit is contained in:
@@ -83,7 +83,14 @@ Tab:
|
||||
- UI:每个列表项展示游戏 UI 图和该 UI 图拆出的子素材组合。
|
||||
- 音乐:展示背景音乐和音效。
|
||||
- 美宣:展示美宣类图片和视频素材。
|
||||
- 特效、场景、游戏:v1 只在有真实数据时展示,无数据时不造假卡片。
|
||||
- 特效、场景、游戏:v1 暂不实装生成链路,只在有真实数据时展示,无数据时不造假卡片,也不在 UI 中写“暂不实装”说明。
|
||||
|
||||
交互口径:
|
||||
|
||||
- 点击任一精选列表项后打开接近全屏的素材预览弹窗。
|
||||
- 弹窗中部展示当前素材,图片使用 contain 完整展示,视频和音频展示可播放控件。
|
||||
- 弹窗底部固定显示当前组合的缩略图导航,点击缩略图切换当前素材。
|
||||
- 弹窗底部同时展示该列表项的提示词、作者名和生成总成本。
|
||||
|
||||
每个列表项需要标注:
|
||||
|
||||
|
||||
@@ -8,12 +8,14 @@ type ResolvedAssetImageProps = Omit<
|
||||
'src'
|
||||
> & {
|
||||
src?: string | null;
|
||||
objectKey?: string | null;
|
||||
fallbackSrc?: string | null;
|
||||
refreshKey?: string | number | null;
|
||||
};
|
||||
|
||||
export function ResolvedAssetImage({
|
||||
src,
|
||||
objectKey,
|
||||
fallbackSrc,
|
||||
alt,
|
||||
refreshKey,
|
||||
@@ -21,6 +23,7 @@ export function ResolvedAssetImage({
|
||||
...rest
|
||||
}: ResolvedAssetImageProps) {
|
||||
const { resolvedUrl, isResolving, shouldResolve } = useResolvedAssetReadUrl(src, {
|
||||
objectKey,
|
||||
refreshKey,
|
||||
});
|
||||
const normalizedSource = src?.trim() ?? '';
|
||||
|
||||
@@ -7,17 +7,20 @@ type ResolvedAssetVideoProps = Omit<
|
||||
'src'
|
||||
> & {
|
||||
src?: string | null;
|
||||
objectKey?: string | null;
|
||||
fallbackSrc?: string | null;
|
||||
refreshKey?: string | number | null;
|
||||
};
|
||||
|
||||
export function ResolvedAssetVideo({
|
||||
src,
|
||||
objectKey,
|
||||
fallbackSrc,
|
||||
refreshKey,
|
||||
...rest
|
||||
}: ResolvedAssetVideoProps) {
|
||||
const { resolvedUrl } = useResolvedAssetReadUrl(src, {
|
||||
objectKey,
|
||||
refreshKey,
|
||||
});
|
||||
const finalSrc = resolvedUrl || fallbackSrc?.trim() || '';
|
||||
|
||||
@@ -258,6 +258,80 @@ describe('CreationLandingView', () => {
|
||||
expect(within(card as HTMLElement).getByText('20泥点')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('opens a fullscreen asset preview modal with thumbnail navigation', async () => {
|
||||
const user = userEvent.setup();
|
||||
listEditorProjectsMock.mockResolvedValueOnce([]);
|
||||
loadEditorAssetLibraryMock.mockResolvedValueOnce({
|
||||
folders: [],
|
||||
assets: [
|
||||
{
|
||||
assetId: 'asset-character',
|
||||
folderId: 'folder-1',
|
||||
label: '角色英雄',
|
||||
imageSrc: 'data:image/png;base64,character',
|
||||
width: 512,
|
||||
height: 512,
|
||||
sourceType: 'generated',
|
||||
prompt: 'character hero prompt',
|
||||
actualPrompt: null,
|
||||
model: 'character-model',
|
||||
provider: 'provider',
|
||||
taskId: 'task-1',
|
||||
assetKind: 'character',
|
||||
authorName: '创作者A',
|
||||
priceMudPoints: 20,
|
||||
},
|
||||
{
|
||||
assetId: 'asset-animation',
|
||||
folderId: 'folder-1',
|
||||
label: '待机动画',
|
||||
imageSrc: 'data:image/png;base64,animation',
|
||||
width: 512,
|
||||
height: 512,
|
||||
sourceType: 'generated',
|
||||
prompt: 'idle animation prompt',
|
||||
actualPrompt: null,
|
||||
model: 'seedance2.0-fast',
|
||||
provider: 'ark',
|
||||
taskId: 'task-2',
|
||||
assetKind: 'character-animation',
|
||||
priceMudPoints: 40,
|
||||
generationInputs: {
|
||||
fields: [{ title: '动作描述', value: '待机动作' }],
|
||||
references: [
|
||||
{
|
||||
title: '角色图片',
|
||||
label: '角色英雄',
|
||||
refType: 'asset',
|
||||
refId: 'asset-character',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
renderCreationLanding();
|
||||
|
||||
await user.click(screen.getByRole('tab', { name: '角色' }));
|
||||
await user.click(await screen.findByText('角色英雄'));
|
||||
|
||||
const dialog = await screen.findByRole('dialog', { name: '角色英雄' });
|
||||
expect(within(dialog).getByText('生成总成本')).toBeTruthy();
|
||||
expect(within(dialog).getByText('60泥点')).toBeTruthy();
|
||||
expect(within(dialog).getByText('创作者A')).toBeTruthy();
|
||||
expect(
|
||||
within(dialog).getByLabelText('素材缩略图导航'),
|
||||
).toBeTruthy();
|
||||
|
||||
await user.click(within(dialog).getByRole('button', { name: '查看待机动画' }));
|
||||
expect(
|
||||
within(dialog).getByRole('button', { name: '查看待机动画' }).getAttribute(
|
||||
'aria-current',
|
||||
),
|
||||
).toBe('true');
|
||||
});
|
||||
|
||||
it('uses Taonier featured as the account asset waterfall instead of creation entries', async () => {
|
||||
listEditorProjectsMock.mockResolvedValueOnce([]);
|
||||
loadEditorAssetLibraryMock.mockResolvedValueOnce({
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,358 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import type {
|
||||
EditorAssetLibrarySnapshot,
|
||||
EditorAssetSnapshot,
|
||||
} from '../../services/image-editor/editorProjectClient';
|
||||
import { buildCreationShowcaseItems } from './creationShowcaseModel';
|
||||
|
||||
function createAsset(
|
||||
overrides: Partial<EditorAssetSnapshot> & Record<string, unknown>,
|
||||
): EditorAssetSnapshot {
|
||||
return {
|
||||
assetId: 'asset-1',
|
||||
folderId: 'folder-1',
|
||||
label: '素材',
|
||||
imageSrc: 'data:image/png;base64,asset',
|
||||
width: 512,
|
||||
height: 512,
|
||||
sourceType: 'generated',
|
||||
prompt: null,
|
||||
actualPrompt: null,
|
||||
model: null,
|
||||
provider: null,
|
||||
taskId: null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function createLibrary(assets: EditorAssetSnapshot[]): EditorAssetLibrarySnapshot {
|
||||
return {
|
||||
folders: [],
|
||||
assets,
|
||||
};
|
||||
}
|
||||
|
||||
describe('creationShowcaseModel', () => {
|
||||
it('groups pack items by the same referenced spec image and sums known cost', () => {
|
||||
const items = buildCreationShowcaseItems({
|
||||
activeTab: 'packs',
|
||||
assetLibrary: createLibrary([
|
||||
createAsset({
|
||||
assetId: 'spec-1',
|
||||
label: '角色规范图',
|
||||
assetKind: 'spec',
|
||||
prompt: '规范提示词',
|
||||
priceMudPoints: 5,
|
||||
authorName: '创作者A',
|
||||
}),
|
||||
createAsset({
|
||||
assetId: 'character-1',
|
||||
label: '角色主图',
|
||||
assetKind: 'character',
|
||||
priceMudPoints: 12,
|
||||
generationInputs: {
|
||||
fields: [{ title: '角色设定', value: '角色提示词' }],
|
||||
references: [
|
||||
{
|
||||
title: '角色规范',
|
||||
label: '角色规范图',
|
||||
refType: 'asset',
|
||||
refId: 'spec-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
]),
|
||||
publicGalleryEntries: [],
|
||||
});
|
||||
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]?.label).toBe('角色规范图');
|
||||
expect(items[0]?.previews.map((preview) => preview.label)).toEqual([
|
||||
'角色规范图',
|
||||
'角色主图',
|
||||
]);
|
||||
expect(items[0]?.prompt).toBe('规范提示词');
|
||||
expect(items[0]?.author).toBe('创作者A');
|
||||
expect(items[0]?.cost).toBe('17泥点');
|
||||
});
|
||||
|
||||
it('groups assets when references point to the source project resource', () => {
|
||||
const items = buildCreationShowcaseItems({
|
||||
activeTab: 'packs',
|
||||
assetLibrary: createLibrary([
|
||||
createAsset({
|
||||
assetId: 'spec-1',
|
||||
label: '项目规范图',
|
||||
assetKind: 'spec',
|
||||
sourceResourceId: 'resource-spec-1',
|
||||
}),
|
||||
createAsset({
|
||||
assetId: 'character-1',
|
||||
label: '项目角色图',
|
||||
assetKind: 'character',
|
||||
generationInputs: {
|
||||
fields: [{ title: '角色设定', value: '项目角色提示词' }],
|
||||
references: [
|
||||
{
|
||||
title: '角色规范',
|
||||
label: '项目规范图',
|
||||
refType: 'project-resource',
|
||||
refId: 'resource-spec-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
]),
|
||||
publicGalleryEntries: [],
|
||||
});
|
||||
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]?.previews.map((preview) => preview.label)).toEqual([
|
||||
'项目规范图',
|
||||
'项目角色图',
|
||||
]);
|
||||
});
|
||||
|
||||
it('groups character animations under the referenced character image', () => {
|
||||
const items = buildCreationShowcaseItems({
|
||||
activeTab: 'characters',
|
||||
assetLibrary: createLibrary([
|
||||
createAsset({
|
||||
assetId: 'character-1',
|
||||
label: '游戏角色图',
|
||||
assetKind: 'character',
|
||||
prompt: '角色提示词',
|
||||
}),
|
||||
createAsset({
|
||||
assetId: 'animation-1',
|
||||
label: '待机动画',
|
||||
assetKind: 'character-animation',
|
||||
imageSrc: 'data:image/png;base64,animation',
|
||||
generationInputs: {
|
||||
fields: [{ title: '动作描述', value: '待机动作' }],
|
||||
references: [
|
||||
{
|
||||
title: '角色图片',
|
||||
label: '游戏角色图',
|
||||
refType: 'asset',
|
||||
refId: 'character-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
]),
|
||||
publicGalleryEntries: [],
|
||||
});
|
||||
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]?.label).toBe('游戏角色图');
|
||||
expect(items[0]?.previews.map((preview) => preview.label)).toEqual([
|
||||
'游戏角色图',
|
||||
'待机动画',
|
||||
]);
|
||||
});
|
||||
|
||||
it('groups UI design extraction assets under the referenced UI image', () => {
|
||||
const items = buildCreationShowcaseItems({
|
||||
activeTab: 'ui',
|
||||
assetLibrary: createLibrary([
|
||||
createAsset({
|
||||
assetId: 'ui-design-1',
|
||||
label: '游戏UI图',
|
||||
assetKind: 'ui-design',
|
||||
prompt: 'UI提示词',
|
||||
}),
|
||||
createAsset({
|
||||
assetId: 'icon-sheet-1',
|
||||
label: 'UI子素材图集',
|
||||
assetKind: 'icon-spritesheet',
|
||||
generationInputs: {
|
||||
fields: [{ title: '提取提示词', value: '提取UI子素材' }],
|
||||
references: [
|
||||
{
|
||||
title: 'UI设计图',
|
||||
label: '游戏UI图',
|
||||
refType: 'asset',
|
||||
refId: 'ui-design-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
createAsset({
|
||||
assetId: 'icon-1',
|
||||
label: '开始按钮',
|
||||
assetKind: 'icon',
|
||||
generationInputs: {
|
||||
fields: [{ title: '提取提示词', value: '提取UI子素材' }],
|
||||
references: [
|
||||
{
|
||||
title: 'UI设计图',
|
||||
label: '游戏UI图',
|
||||
refType: 'asset',
|
||||
refId: 'ui-design-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
]),
|
||||
publicGalleryEntries: [],
|
||||
});
|
||||
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]?.label).toBe('游戏UI图');
|
||||
expect(items[0]?.previews.map((preview) => preview.label)).toEqual([
|
||||
'游戏UI图',
|
||||
'UI子素材图集',
|
||||
'开始按钮',
|
||||
]);
|
||||
});
|
||||
|
||||
it('counts a spritesheet task only once when UI children share the same task', () => {
|
||||
const items = buildCreationShowcaseItems({
|
||||
activeTab: 'ui',
|
||||
assetLibrary: createLibrary([
|
||||
createAsset({
|
||||
assetId: 'ui-design-1',
|
||||
label: '游戏UI图',
|
||||
assetKind: 'ui-design',
|
||||
model: 'gemini-3.1-flash-image-preview',
|
||||
taskId: 'task-ui-design',
|
||||
}),
|
||||
createAsset({
|
||||
assetId: 'icon-sheet-1',
|
||||
label: 'UI子素材图集',
|
||||
assetKind: 'icon-spritesheet',
|
||||
model: 'gpt-image-2',
|
||||
taskId: 'task-ui-extract',
|
||||
generationInputs: {
|
||||
fields: [{ title: '提取提示词', value: '提取UI子素材' }],
|
||||
references: [
|
||||
{
|
||||
title: 'UI设计图',
|
||||
label: '游戏UI图',
|
||||
refType: 'asset',
|
||||
refId: 'ui-design-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
createAsset({
|
||||
assetId: 'icon-1',
|
||||
label: '开始按钮',
|
||||
assetKind: 'icon',
|
||||
model: 'gpt-image-2',
|
||||
taskId: 'task-ui-extract',
|
||||
generationInputs: {
|
||||
fields: [{ title: '提取提示词', value: '提取UI子素材' }],
|
||||
references: [
|
||||
{
|
||||
title: 'UI设计图',
|
||||
label: '游戏UI图',
|
||||
refType: 'asset',
|
||||
refId: 'ui-design-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
]),
|
||||
publicGalleryEntries: [],
|
||||
});
|
||||
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]?.cost).toBe('32泥点');
|
||||
});
|
||||
|
||||
it('keeps music assets as audio preview items', () => {
|
||||
const items = buildCreationShowcaseItems({
|
||||
activeTab: 'music',
|
||||
assetLibrary: createLibrary([
|
||||
createAsset({
|
||||
assetId: 'music-1',
|
||||
label: '背景音乐',
|
||||
imageSrc: '/generated-editor-audios/music.mp3',
|
||||
assetKind: 'background-music',
|
||||
prompt: '舒缓背景音乐',
|
||||
priceMudPoints: '5',
|
||||
}),
|
||||
]),
|
||||
publicGalleryEntries: [],
|
||||
fallbackAuthorName: '当前用户',
|
||||
});
|
||||
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]?.previews[0]?.mediaType).toBe('audio');
|
||||
expect(items[0]?.author).toBe('当前用户');
|
||||
expect(items[0]?.cost).toBe('5泥点');
|
||||
});
|
||||
|
||||
it('keeps character animation videos out of the marketing tab', () => {
|
||||
const items = buildCreationShowcaseItems({
|
||||
activeTab: 'marketing',
|
||||
assetLibrary: createLibrary([
|
||||
createAsset({
|
||||
assetId: 'animation-1',
|
||||
label: '角色动画',
|
||||
assetKind: 'character-animation',
|
||||
imageSrc: '/generated-editor-videos/animation.mp4',
|
||||
}),
|
||||
createAsset({
|
||||
assetId: 'poster-1',
|
||||
label: '上线美宣视频',
|
||||
assetKind: 'publication-material',
|
||||
imageSrc: '/generated-editor-videos/poster.mp4',
|
||||
}),
|
||||
]),
|
||||
publicGalleryEntries: [],
|
||||
});
|
||||
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]?.label).toBe('上线美宣视频');
|
||||
expect(items[0]?.previews[0]?.mediaType).toBe('video');
|
||||
});
|
||||
|
||||
it('infers costs from generated asset kinds when stored price is absent', () => {
|
||||
const items = buildCreationShowcaseItems({
|
||||
activeTab: 'characters',
|
||||
assetLibrary: createLibrary([
|
||||
createAsset({
|
||||
assetId: 'character-1',
|
||||
label: '游戏角色图',
|
||||
imageSrc: '/generated-editor-assets/character.png',
|
||||
objectKey: 'generated-editor-assets/character.png',
|
||||
assetKind: 'character',
|
||||
model: 'gpt-image-2',
|
||||
prompt: '角色提示词',
|
||||
}),
|
||||
createAsset({
|
||||
assetId: 'animation-1',
|
||||
label: '待机动画',
|
||||
assetKind: 'character-animation',
|
||||
imageSrc: '/generated-editor-videos/animation.mp4',
|
||||
objectKey: 'generated-editor-videos/animation.mp4',
|
||||
model: 'seedance2.0-fast',
|
||||
durationSeconds: 6,
|
||||
generationInputs: {
|
||||
fields: [{ title: '清晰度', value: '720p' }],
|
||||
references: [
|
||||
{
|
||||
title: '角色图片',
|
||||
label: '游戏角色图',
|
||||
refType: 'asset',
|
||||
refId: 'character-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
]),
|
||||
publicGalleryEntries: [],
|
||||
});
|
||||
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]?.cost).toBe('140泥点');
|
||||
expect(items[0]?.previews[0]?.objectKey).toBe(
|
||||
'generated-editor-assets/character.png',
|
||||
);
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
+315
@@ -3617,10 +3617,20 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock {
|
||||
border: 1px solid rgba(220, 189, 163, 0.78);
|
||||
border-radius: 1rem;
|
||||
background: rgba(255, 254, 251, 0.88);
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
box-shadow: 0 16px 32px rgba(124, 72, 35, 0.1);
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.creation-landing__asset-card:hover {
|
||||
border-color: rgba(195, 104, 48, 0.46);
|
||||
box-shadow: 0 20px 40px rgba(124, 72, 35, 0.14);
|
||||
}
|
||||
|
||||
.creation-landing__asset-preview {
|
||||
position: relative;
|
||||
display: grid;
|
||||
min-height: 9.5rem;
|
||||
max-height: 23rem;
|
||||
@@ -3629,6 +3639,64 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock {
|
||||
background: linear-gradient(180deg, #fffdfa, #fff4ea);
|
||||
}
|
||||
|
||||
.creation-landing__asset-preview--bundle {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
align-items: stretch;
|
||||
gap: 0.18rem;
|
||||
padding: 0.18rem;
|
||||
}
|
||||
|
||||
.creation-landing__asset-preview-tile {
|
||||
position: relative;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 5rem;
|
||||
overflow: hidden;
|
||||
place-items: center;
|
||||
border-radius: 0.72rem;
|
||||
background: rgba(255, 255, 255, 0.66);
|
||||
}
|
||||
|
||||
.creation-landing__asset-preview-more {
|
||||
position: absolute;
|
||||
right: 0.65rem;
|
||||
bottom: 0.65rem;
|
||||
display: inline-flex;
|
||||
min-width: 2rem;
|
||||
height: 2rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 999px;
|
||||
background: rgba(44, 31, 22, 0.72);
|
||||
color: #fffdf9;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
.creation-landing__asset-audio-preview {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 8rem;
|
||||
place-items: center;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
background:
|
||||
linear-gradient(135deg, rgba(244, 187, 129, 0.4), rgba(255, 247, 236, 0.92)),
|
||||
radial-gradient(circle at 30% 24%, rgba(255, 255, 255, 0.9), transparent 42%);
|
||||
color: #9a5428;
|
||||
}
|
||||
|
||||
.creation-landing__asset-audio-preview svg {
|
||||
width: 2.6rem;
|
||||
height: 2.6rem;
|
||||
}
|
||||
|
||||
.creation-landing__asset-audio-preview audio {
|
||||
width: min(100%, 28rem);
|
||||
}
|
||||
|
||||
.creation-landing__asset-meta {
|
||||
display: grid;
|
||||
gap: 0.55rem;
|
||||
@@ -3675,6 +3743,215 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-modal-overlay {
|
||||
padding: 0.5rem;
|
||||
background: rgba(11, 8, 6, 0.82);
|
||||
}
|
||||
|
||||
.creation-landing__showcase-modal-panel {
|
||||
width: min(100vw - 1rem, 88rem);
|
||||
height: min(96vh, 64rem);
|
||||
max-height: calc(100vh - 1rem);
|
||||
overflow: hidden;
|
||||
border-radius: 1.2rem;
|
||||
background: #100d0b;
|
||||
box-shadow: 0 30px 90px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.creation-landing__showcase-modal-body {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-modal {
|
||||
position: relative;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
grid-template-rows: minmax(0, 1fr) auto;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(circle at 18% 12%, rgba(186, 117, 65, 0.18), transparent 32%),
|
||||
#100d0b;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-modal-close {
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-modal-stage {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
place-items: center;
|
||||
padding: clamp(1rem, 3vw, 2.5rem);
|
||||
}
|
||||
|
||||
.creation-landing__showcase-current {
|
||||
position: relative;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-current > img,
|
||||
.creation-landing__showcase-current > video,
|
||||
.creation-landing__showcase-current .creation-landing__asset-audio-preview {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
border-radius: 0.9rem;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-current > img,
|
||||
.creation-landing__showcase-current > video {
|
||||
width: auto;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
object-position: center;
|
||||
box-shadow: 0 18px 52px rgba(0, 0, 0, 0.32);
|
||||
}
|
||||
|
||||
.creation-landing__showcase-current > .creation-landing__showcase-media {
|
||||
display: block;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-dock {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
display: grid;
|
||||
gap: 0.85rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(24, 18, 14, 0.94);
|
||||
backdrop-filter: none;
|
||||
padding: 0.85rem;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-dock-meta {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(13rem, 0.44fr);
|
||||
gap: 0.75rem 1rem;
|
||||
align-items: start;
|
||||
color: #fff8ef;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-dock-meta h3 {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
color: #fff8ef;
|
||||
font-size: 1rem;
|
||||
font-weight: 860;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-dock-meta p {
|
||||
grid-column: 1 / 2;
|
||||
display: -webkit-box;
|
||||
min-height: 2.6em;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
color: rgba(255, 248, 239, 0.72);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 620;
|
||||
line-height: 1.32;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-dock-meta dl {
|
||||
display: grid;
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 1 / span 2;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.55rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-dock-meta div {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-dock-meta dt {
|
||||
color: rgba(255, 248, 239, 0.5);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 780;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-dock-meta dd {
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
color: #fff8ef;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 820;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-thumbs {
|
||||
display: flex;
|
||||
gap: 0.55rem;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 0.1rem;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-thumb {
|
||||
display: grid;
|
||||
flex: 0 0 6.5rem;
|
||||
min-width: 0;
|
||||
gap: 0.38rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 0.72rem;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
padding: 0.38rem;
|
||||
color: rgba(255, 248, 239, 0.72);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 760;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-thumb--active {
|
||||
border-color: rgba(242, 184, 124, 0.8);
|
||||
background: rgba(242, 184, 124, 0.18);
|
||||
color: #fff8ef;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-thumb-media {
|
||||
display: grid;
|
||||
height: 4.1rem;
|
||||
overflow: hidden;
|
||||
place-items: center;
|
||||
border-radius: 0.52rem;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.creation-landing__showcase-thumb-media svg {
|
||||
width: 1.45rem;
|
||||
height: 1.45rem;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-thumb > span:last-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.creation-landing__hero,
|
||||
.creation-landing__section {
|
||||
@@ -3718,6 +3995,21 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock {
|
||||
.creation-landing__asset-waterfall {
|
||||
column-count: 2;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-modal-panel {
|
||||
width: calc(100vw - 0.7rem);
|
||||
height: calc(100vh - 0.7rem);
|
||||
}
|
||||
|
||||
.creation-landing__showcase-dock-meta {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-dock-meta p,
|
||||
.creation-landing__showcase-dock-meta dl {
|
||||
grid-column: auto;
|
||||
grid-row: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
@@ -3763,6 +4055,29 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock {
|
||||
.creation-landing__asset-waterfall {
|
||||
column-count: 1;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-modal-overlay {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-modal-panel {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
max-height: 100vh;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-modal-stage {
|
||||
padding: 3.2rem 0.75rem 0.75rem;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-dock {
|
||||
padding: 0.72rem;
|
||||
}
|
||||
|
||||
.creation-landing__showcase-thumb {
|
||||
flex-basis: 5.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
.image-canvas-editor {
|
||||
|
||||
Reference in New Issue
Block a user