替换客户端首页左侧导航
按 GameAgent 需求落地首页、项目、帮助三类左侧入口 项目页复用本地最近工作区记录并提供打开和显示目录操作 帮助入口改为浮层菜单并接入使用指南、联系我们、最新动态页面 为独立客户端声明 lucide-react 图标依赖
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-react": "^5.0.4",
|
||||
"lucide-react": "^0.546.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"vite": "^6.2.0"
|
||||
|
||||
@@ -9,6 +9,17 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
Bell,
|
||||
BookOpen,
|
||||
CircleHelp,
|
||||
Contact,
|
||||
FolderKanban,
|
||||
Home,
|
||||
Plus,
|
||||
Settings,
|
||||
Upload,
|
||||
} from 'lucide-react';
|
||||
|
||||
import {
|
||||
createGameCreationAppManifest,
|
||||
@@ -49,6 +60,7 @@ const AGENT_RUN_HISTORY_INITIAL_VISIBLE_COUNT = 20;
|
||||
const AGENT_RUN_HISTORY_VISIBLE_STEP = 20;
|
||||
const CONVERSATION_INITIAL_VISIBLE_COUNT = 20;
|
||||
const CONVERSATION_VISIBLE_STEP = 20;
|
||||
type LauncherView = 'home' | 'projects' | 'guide' | 'contact' | 'news';
|
||||
|
||||
interface InitLocalProjectResult {
|
||||
projectPath: string;
|
||||
@@ -1387,6 +1399,7 @@ function RuntimeConfigDialog({
|
||||
export function WorkspaceLauncher() {
|
||||
const [projectPath, setProjectPath] = useState(defaultProjectPath);
|
||||
const [status, setStatus] = useState('请选择项目');
|
||||
const [launcherView, setLauncherView] = useState<LauncherView>('home');
|
||||
const [recentWorkspaces, setRecentWorkspaces] =
|
||||
useState<string[]>(readRecentWorkspaces);
|
||||
const [recentWorkspaceStatuses, setRecentWorkspaceStatuses] = useState<
|
||||
@@ -1590,6 +1603,49 @@ export function WorkspaceLauncher() {
|
||||
}
|
||||
}
|
||||
|
||||
const projectRows = recentWorkspaces.map((workspace) => {
|
||||
const directoryStatus = recentWorkspaceStatuses[workspace];
|
||||
return {
|
||||
path: workspace,
|
||||
name:
|
||||
directoryStatus?.projectName ||
|
||||
workspace.split(/[\\/]/).filter(Boolean).pop() ||
|
||||
workspace,
|
||||
status:
|
||||
directoryStatus === null
|
||||
? '检查失败'
|
||||
: directoryStatus?.exists === false
|
||||
? '未找到'
|
||||
: directoryStatus?.isDirectory === false
|
||||
? '不是文件夹'
|
||||
: directoryStatus?.isGameCreatorProject === false
|
||||
? '未初始化'
|
||||
: directoryStatus?.recentRunStatus
|
||||
? `最近运行:${directoryStatus.recentRunStatus}`
|
||||
: recentWorkspaceRefreshing
|
||||
? '检查中'
|
||||
: '本地项目',
|
||||
canOpen:
|
||||
!recentWorkspaceRefreshing &&
|
||||
directoryStatus !== null &&
|
||||
directoryStatus?.exists !== false &&
|
||||
directoryStatus?.isDirectory !== false &&
|
||||
directoryStatus?.isGameCreatorProject !== false,
|
||||
};
|
||||
});
|
||||
const currentHelpTitle =
|
||||
launcherView === 'guide'
|
||||
? '使用指南'
|
||||
: launcherView === 'contact'
|
||||
? '联系我们'
|
||||
: '最新动态';
|
||||
const currentHelpItems =
|
||||
launcherView === 'guide'
|
||||
? ['创建游戏项目', '上传参考素材', '选择审批档位', '生成试玩原型']
|
||||
: launcherView === 'contact'
|
||||
? ['产品反馈', '商务合作', '账号与充值支持']
|
||||
: ['GameAgent V1.0 首页改版', '项目工作区和 Agent 会话建设中', '美术生成将接入平台 API'];
|
||||
|
||||
return (
|
||||
<main className="launcher-shell" aria-label="项目启动器">
|
||||
<aside className="launcher-sidebar">
|
||||
@@ -1597,29 +1653,73 @@ export function WorkspaceLauncher() {
|
||||
<span className="launcher-logo">tn</span>
|
||||
</div>
|
||||
<nav aria-label="启动器导航">
|
||||
<button type="button" aria-label="新建">
|
||||
+
|
||||
<button
|
||||
type="button"
|
||||
aria-label="首页"
|
||||
title="首页"
|
||||
className={launcherView === 'home' ? 'launcher-nav-active' : ''}
|
||||
onClick={() => setLauncherView('home')}
|
||||
>
|
||||
<Home size={17} aria-hidden="true" />
|
||||
</button>
|
||||
<button type="button" className="launcher-nav-active">
|
||||
⌂
|
||||
</button>
|
||||
<button type="button" aria-label="项目">
|
||||
□
|
||||
</button>
|
||||
<button type="button" aria-label="帮助">
|
||||
◇
|
||||
</button>
|
||||
<button type="button" aria-label="账户">
|
||||
♙
|
||||
<button
|
||||
type="button"
|
||||
aria-label="项目"
|
||||
title="项目"
|
||||
className={launcherView === 'projects' ? 'launcher-nav-active' : ''}
|
||||
onClick={() => setLauncherView('projects')}
|
||||
>
|
||||
<FolderKanban size={17} aria-hidden="true" />
|
||||
</button>
|
||||
<div className="launcher-help-nav">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="帮助"
|
||||
title="帮助"
|
||||
className={
|
||||
['guide', 'contact', 'news'].includes(launcherView)
|
||||
? 'launcher-nav-active'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<CircleHelp size={17} aria-hidden="true" />
|
||||
</button>
|
||||
<div className="launcher-help-menu" role="menu">
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
onClick={() => setLauncherView('guide')}
|
||||
>
|
||||
<BookOpen size={14} aria-hidden="true" />
|
||||
使用指南
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
onClick={() => setLauncherView('contact')}
|
||||
>
|
||||
<Contact size={14} aria-hidden="true" />
|
||||
联系我们
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
onClick={() => setLauncherView('news')}
|
||||
>
|
||||
<Bell size={14} aria-hidden="true" />
|
||||
最新动态
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<button
|
||||
type="button"
|
||||
className="launcher-config-button"
|
||||
onClick={() => setRuntimeConfigOpen(true)}
|
||||
aria-label="帮助"
|
||||
aria-label="配置"
|
||||
title="配置"
|
||||
>
|
||||
?
|
||||
<Settings size={16} aria-hidden="true" />
|
||||
</button>
|
||||
</aside>
|
||||
<section className="launcher-main">
|
||||
@@ -1649,88 +1749,169 @@ export function WorkspaceLauncher() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<section className="launcher-hero" aria-label="首页输入">
|
||||
<div className="launcher-hero-title">
|
||||
<span className="launcher-hero-logo">tn</span>
|
||||
<div>
|
||||
<h1>Lovart 让设计更简单</h1>
|
||||
<p>懂你的设计代理,帮你搞定一切</p>
|
||||
</div>
|
||||
</div>
|
||||
<form className="launcher-prompt-card" onSubmit={handleSubmit}>
|
||||
<textarea
|
||||
aria-label="创作想法"
|
||||
placeholder="让 Lovart 打造引人注目的社交"
|
||||
readOnly
|
||||
/>
|
||||
<div className="launcher-prompt-actions">
|
||||
<button type="button" aria-label="添加">
|
||||
+
|
||||
</button>
|
||||
<span aria-hidden="true">◇</span>
|
||||
<button type="button" aria-label="发送">
|
||||
↑
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div className="launcher-model-pills" aria-label="模型选择">
|
||||
<button type="button" className="launcher-pill-active">
|
||||
◉ GPT Image 2
|
||||
</button>
|
||||
<button type="button">▣ Seedance 2.0</button>
|
||||
<button type="button">◈ Nano Banana Pro</button>
|
||||
<button type="button">▧ Design</button>
|
||||
<button type="button">☆ Branding</button>
|
||||
<button type="button">▦ E-Commerce</button>
|
||||
<button type="button">▣ Video</button>
|
||||
</div>
|
||||
</section>
|
||||
{launcherView === 'home' ? (
|
||||
<>
|
||||
<section className="launcher-hero" aria-label="首页输入">
|
||||
<div className="launcher-hero-title">
|
||||
<span className="launcher-hero-logo">tn</span>
|
||||
<div>
|
||||
<h1>陶泥儿 GameAgent</h1>
|
||||
<p>描述你的想法,让 Agent 生成可试玩游戏原型</p>
|
||||
</div>
|
||||
</div>
|
||||
<form className="launcher-prompt-card" onSubmit={handleSubmit}>
|
||||
<textarea
|
||||
aria-label="创作想法"
|
||||
placeholder="输入你想创作的游戏、素材或策划案"
|
||||
readOnly
|
||||
/>
|
||||
<div className="launcher-prompt-actions">
|
||||
<button type="button" aria-label="上传素材">
|
||||
<Upload size={15} aria-hidden="true" />
|
||||
</button>
|
||||
<span aria-hidden="true">预算 100 点 · 严格审批</span>
|
||||
<button type="submit" aria-label="开启创作">
|
||||
<Plus size={16} aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div className="launcher-model-pills" aria-label="Agent 分类">
|
||||
<button type="button" className="launcher-pill-active">
|
||||
游戏创作
|
||||
</button>
|
||||
<button type="button">素材设计</button>
|
||||
<button type="button">策划案设计</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="launcher-project-list" aria-label="最近项目">
|
||||
<header>
|
||||
<h2>最近项目</h2>
|
||||
<button type="button">查看全部 〉</button>
|
||||
</header>
|
||||
<div className="launcher-project-grid">
|
||||
<article className="launcher-project-card launcher-project-create">
|
||||
<div>+</div>
|
||||
<strong>新建项目</strong>
|
||||
</article>
|
||||
<article className="launcher-project-card launcher-card-medical">
|
||||
<div className="launcher-card-collage">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<section className="launcher-project-list" aria-label="最近项目">
|
||||
<header>
|
||||
<h2>最近项目</h2>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLauncherView('projects')}
|
||||
>
|
||||
查看全部 〉
|
||||
</button>
|
||||
</header>
|
||||
<div className="launcher-project-grid">
|
||||
<article className="launcher-project-card launcher-project-create">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void handlePickProjectDirectory();
|
||||
}}
|
||||
>
|
||||
<Plus size={30} aria-hidden="true" />
|
||||
</button>
|
||||
<strong>新建项目</strong>
|
||||
</article>
|
||||
{projectRows.slice(0, 4).map((project) => (
|
||||
<article className="launcher-project-card" key={project.path}>
|
||||
<button
|
||||
type="button"
|
||||
className="launcher-project-thumb"
|
||||
disabled={!project.canOpen}
|
||||
onClick={() => {
|
||||
setProjectPath(project.path);
|
||||
void openProject(project.path, 'open');
|
||||
}}
|
||||
>
|
||||
<span>{project.name.slice(0, 2)}</span>
|
||||
</button>
|
||||
<strong>{project.name}</strong>
|
||||
<small>{project.status}</small>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
<strong>医疗游戏</strong>
|
||||
</article>
|
||||
<article className="launcher-project-card launcher-card-ui">
|
||||
</section>
|
||||
</>
|
||||
) : launcherView === 'projects' ? (
|
||||
<section className="launcher-page launcher-projects-page">
|
||||
<header>
|
||||
<div>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<h1>项目</h1>
|
||||
<p>{recentWorkspaceRefreshing ? '正在检查项目状态' : status}</p>
|
||||
</div>
|
||||
<strong>game agent界面</strong>
|
||||
</article>
|
||||
<article className="launcher-project-card launcher-card-pattern">
|
||||
<div className="launcher-page-actions">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void handlePickProjectDirectory();
|
||||
}}
|
||||
>
|
||||
选择目录
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void openProject(projectPath, 'create');
|
||||
}}
|
||||
>
|
||||
新建当前目录
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div className="launcher-project-table">
|
||||
{projectRows.length > 0 ? (
|
||||
projectRows.map((project) => (
|
||||
<article key={project.path}>
|
||||
<div>
|
||||
<strong>{project.name}</strong>
|
||||
<small>{project.path}</small>
|
||||
</div>
|
||||
<span>{project.status}</span>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!project.canOpen}
|
||||
onClick={() => {
|
||||
setProjectPath(project.path);
|
||||
void openProject(project.path, 'open');
|
||||
}}
|
||||
>
|
||||
打开
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void handleRevealProjectDirectory(project.path);
|
||||
}}
|
||||
>
|
||||
显示
|
||||
</button>
|
||||
</article>
|
||||
))
|
||||
) : (
|
||||
<div className="launcher-empty-projects">
|
||||
<FolderKanban size={28} aria-hidden="true" />
|
||||
<strong>暂无项目</strong>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void handlePickProjectDirectory();
|
||||
}}
|
||||
>
|
||||
选择本地项目目录
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
) : (
|
||||
<section className="launcher-page launcher-help-page">
|
||||
<header>
|
||||
<div>
|
||||
<span>✦</span>
|
||||
<span>◇</span>
|
||||
<span>▧</span>
|
||||
<span>✺</span>
|
||||
<h1>{currentHelpTitle}</h1>
|
||||
<p>陶泥儿 GameAgent</p>
|
||||
</div>
|
||||
<strong>Untitled</strong>
|
||||
</article>
|
||||
<article className="launcher-project-card launcher-card-orange">
|
||||
<div>
|
||||
<span>福利</span>
|
||||
<span>陶</span>
|
||||
</div>
|
||||
<strong>陶泥儿ip角色</strong>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</header>
|
||||
<div className="launcher-help-cards">
|
||||
{currentHelpItems.map((item) => (
|
||||
<article key={item}>{item}</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</section>
|
||||
{runtimeConfigOpen ? (
|
||||
<RuntimeConfigDialog onClose={() => setRuntimeConfigOpen(false)} />
|
||||
|
||||
@@ -91,6 +91,58 @@ textarea {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
.launcher-help-nav {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.launcher-help-menu {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: 36px;
|
||||
display: grid;
|
||||
min-width: 138px;
|
||||
gap: 4px;
|
||||
padding: 7px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
box-shadow: 0 14px 34px rgb(15 23 42 / 14%);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateX(-4px);
|
||||
transition:
|
||||
opacity 0.16s ease,
|
||||
transform 0.16s ease;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.launcher-help-nav:hover .launcher-help-menu,
|
||||
.launcher-help-nav:focus-within .launcher-help-menu {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.launcher-sidebar .launcher-help-menu button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
padding: 0 9px;
|
||||
border-radius: 7px;
|
||||
background: transparent;
|
||||
color: #1f2937;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.launcher-sidebar .launcher-help-menu button:hover,
|
||||
.launcher-sidebar .launcher-help-menu button:focus-visible {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
.launcher-config-button {
|
||||
margin-top: auto;
|
||||
font-size: 15px;
|
||||
@@ -260,10 +312,11 @@ textarea {
|
||||
|
||||
.launcher-prompt-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: #777;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.launcher-prompt-actions button {
|
||||
@@ -347,7 +400,8 @@ textarea {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.launcher-project-card > div {
|
||||
.launcher-project-card > div,
|
||||
.launcher-project-card > button {
|
||||
position: relative;
|
||||
height: 116px;
|
||||
overflow: hidden;
|
||||
@@ -356,6 +410,15 @@ textarea {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
.launcher-project-card > button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.launcher-project-card > button:disabled {
|
||||
cursor: default;
|
||||
opacity: 0.62;
|
||||
}
|
||||
|
||||
.launcher-project-card strong {
|
||||
min-width: 0;
|
||||
color: #111;
|
||||
@@ -366,14 +429,165 @@ textarea {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.launcher-project-create > div {
|
||||
.launcher-project-card small {
|
||||
margin-top: -5px;
|
||||
color: #8b8b8b;
|
||||
font-size: 11px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.launcher-project-create > button {
|
||||
display: grid;
|
||||
border-style: dashed;
|
||||
color: #777;
|
||||
font-size: 28px;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.launcher-project-thumb {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.launcher-project-thumb span {
|
||||
display: grid;
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
border-radius: 14px;
|
||||
background: #fff;
|
||||
color: #f26d2d;
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
place-items: center;
|
||||
box-shadow: 0 8px 22px rgb(242 109 45 / 16%);
|
||||
}
|
||||
|
||||
.launcher-page {
|
||||
display: grid;
|
||||
gap: 22px;
|
||||
width: min(1054px, calc(100vw - 122px));
|
||||
margin: 0 auto;
|
||||
padding: 118px 0 0;
|
||||
}
|
||||
|
||||
.launcher-page header {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.launcher-page h1 {
|
||||
margin: 0;
|
||||
color: #111827;
|
||||
font-size: 28px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.launcher-page p {
|
||||
margin: 8px 0 0;
|
||||
color: #8b8b8b;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.launcher-page-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.launcher-page-actions button,
|
||||
.launcher-project-table article > button,
|
||||
.launcher-empty-projects button {
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #d8dde5;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
color: #111827;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.launcher-page-actions button:last-child {
|
||||
border-color: #111827;
|
||||
background: #111827;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.launcher-project-table {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.launcher-project-table article {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto auto auto;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-height: 62px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.launcher-project-table article strong {
|
||||
display: block;
|
||||
color: #111827;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.launcher-project-table article small {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
color: #8b8b8b;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.launcher-project-table article span {
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.launcher-empty-projects {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
gap: 12px;
|
||||
min-height: 220px;
|
||||
padding: 44px;
|
||||
border: 1px dashed #d8dde5;
|
||||
border-radius: 8px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.launcher-empty-projects strong {
|
||||
color: #111827;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.launcher-help-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.launcher-help-cards article {
|
||||
display: grid;
|
||||
min-height: 118px;
|
||||
padding: 18px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
color: #111827;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
align-content: end;
|
||||
}
|
||||
|
||||
.launcher-card-collage {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
|
||||
Reference in New Issue
Block a user