Skip to content

Commit 55fb844

Browse files
committed
💄 style(app): redesign new session + home with frontend-design principles
New Session view: - Centered vertically instead of bottom-left aligned - Project info shown as pill-shaped badges with subtle backgrounds - Staggered fade-up entrance animations (0.1s, 0.2s, 0.3s, 0.4s) Home page: - Logo with entrance fade-up animation - Recent projects in card-style bordered list instead of bare buttons - Each project row staggered with 50ms delay between items - 'Recent Projects' label styled as uppercase tracking-wider section header - Empty state with contained icon in rounded square - Better vertical rhythm with responsive top padding
1 parent 5fb61bd commit 55fb844

2 files changed

Lines changed: 116 additions & 59 deletions

File tree

‎packages/app/src/components/session/session-new-view.tsx‎

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { getDirectory, getFilename } from "@opencode-ai/util/path"
99
const MAIN_WORKTREE = "main"
1010
const CREATE_WORKTREE = "create"
1111
const ROOT_CLASS =
12-
"size-full flex flex-col justify-end items-start gap-4 flex-[1_0_0] self-stretch max-w-200 mx-auto 2xl:max-w-[1000px] px-6 pb-16"
12+
"size-full flex flex-col justify-center items-center gap-6 flex-[1_0_0] self-stretch max-w-200 mx-auto 2xl:max-w-[1000px] px-6 pb-24"
1313

1414
interface NewSessionViewProps {
1515
worktree: string
@@ -50,33 +50,54 @@ export function NewSessionView(props: NewSessionViewProps) {
5050

5151
return (
5252
<div class={ROOT_CLASS}>
53-
<div class="text-20-medium text-text-weaker">{language.t("command.session.new")}</div>
54-
<div class="flex justify-center items-center gap-3">
55-
<Icon name="folder" size="small" />
56-
<div class="text-12-medium text-text-weak select-text">
57-
{getDirectory(projectRoot())}
58-
<span class="text-text-strong">{getFilename(projectRoot())}</span>
59-
</div>
60-
</div>
61-
<div class="flex justify-center items-center gap-1">
62-
<Icon name="branch" size="small" />
63-
<div class="text-12-medium text-text-weak select-text ml-2">{label(current())}</div>
53+
{/* Greeting — large, centered, with subtle fade-in */}
54+
<div
55+
class="text-20-medium text-text-weaker opacity-0"
56+
style="animation: fadeUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.1s forwards"
57+
>
58+
{language.t("command.session.new")}
6459
</div>
65-
<Show when={sync.project}>
66-
{(project) => (
67-
<div class="flex justify-center items-center gap-3">
68-
<Icon name="pencil-line" size="small" />
69-
<div class="text-12-medium text-text-weak">
70-
{language.t("session.new.lastModified")}&nbsp;
71-
<span class="text-text-strong">
72-
{DateTime.fromMillis(project().time.updated ?? project().time.created)
73-
.setLocale(language.locale())
74-
.toRelative()}
75-
</span>
76-
</div>
60+
61+
{/* Project info badges — compact, pill-shaped, staggered entrance */}
62+
<div class="flex flex-col items-center gap-2">
63+
<div
64+
class="flex items-center gap-2 px-3 py-1.5 rounded-full opacity-0"
65+
style="background: var(--surface-base); border: 1px solid var(--border-weaker-base); animation: fadeUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.2s forwards"
66+
>
67+
<Icon name="folder" size="small" />
68+
<div class="text-12-medium text-text-weak select-text">
69+
{getDirectory(projectRoot())}
70+
<span class="text-text-strong">{getFilename(projectRoot())}</span>
7771
</div>
78-
)}
79-
</Show>
72+
</div>
73+
74+
<div
75+
class="flex items-center gap-2 px-3 py-1.5 rounded-full opacity-0"
76+
style="background: var(--surface-base); border: 1px solid var(--border-weaker-base); animation: fadeUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.3s forwards"
77+
>
78+
<Icon name="branch" size="small" />
79+
<div class="text-12-medium text-text-weak select-text">{label(current())}</div>
80+
</div>
81+
82+
<Show when={sync.project}>
83+
{(project) => (
84+
<div
85+
class="flex items-center gap-2 px-3 py-1.5 rounded-full opacity-0"
86+
style="background: var(--surface-base); border: 1px solid var(--border-weaker-base); animation: fadeUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.4s forwards"
87+
>
88+
<Icon name="pencil-line" size="small" />
89+
<div class="text-12-medium text-text-weak">
90+
{language.t("session.new.lastModified")}&nbsp;
91+
<span class="text-text-strong">
92+
{DateTime.fromMillis(project().time.updated ?? project().time.created)
93+
.setLocale(language.locale())
94+
.toRelative()}
95+
</span>
96+
</div>
97+
</div>
98+
)}
99+
</Show>
100+
</div>
80101
</div>
81102
)
82103
}

‎packages/app/src/pages/home.tsx‎

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -69,58 +69,94 @@ export default function Home() {
6969
}
7070

7171
return (
72-
<div class="mx-auto mt-55 w-full md:w-auto px-4">
73-
<Logo class="md:w-xl opacity-12" />
74-
<Button
75-
size="large"
76-
variant="ghost"
77-
class="mt-4 mx-auto text-14-regular text-text-weak"
78-
onClick={() => dialog.show(() => <DialogSelectServer />)}
72+
<div class="mx-auto w-full max-w-lg px-6 flex flex-col items-center" style="padding-top: min(20vh, 160px)">
73+
{/* Logo with entrance animation */}
74+
<div
75+
class="opacity-0"
76+
style="animation: fadeUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) 0.05s forwards"
7977
>
80-
<div
81-
classList={{
82-
"size-2 rounded-full": true,
83-
[serverDotClass()]: true,
84-
}}
85-
/>
86-
{server.name}
87-
</Button>
78+
<Logo class="w-40 md:w-56 opacity-20" />
79+
</div>
80+
81+
{/* Server status badge */}
82+
<div
83+
class="opacity-0"
84+
style="animation: fadeUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.15s forwards"
85+
>
86+
<Button
87+
size="large"
88+
variant="ghost"
89+
class="mt-4 mx-auto text-14-regular text-text-weak"
90+
onClick={() => dialog.show(() => <DialogSelectServer />)}
91+
>
92+
<div
93+
classList={{
94+
"size-2 rounded-full": true,
95+
[serverDotClass()]: true,
96+
}}
97+
/>
98+
{server.name}
99+
</Button>
100+
</div>
101+
88102
<Switch>
89103
<Match when={sync.data.project.length > 0}>
90-
<div class="mt-20 w-full flex flex-col gap-4">
91-
<div class="flex gap-2 items-center justify-between pl-3">
92-
<div class="text-14-medium text-text-strong">{language.t("home.recentProjects")}</div>
104+
<div
105+
class="mt-12 w-full flex flex-col gap-3 opacity-0"
106+
style="animation: fadeUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.25s forwards"
107+
>
108+
<div class="flex gap-2 items-center justify-between pl-1 mb-1">
109+
<div class="text-12-medium text-text-weak uppercase tracking-wider">
110+
{language.t("home.recentProjects")}
111+
</div>
93112
<Button icon="folder-add-left" size="normal" class="pl-2 pr-3" onClick={chooseProject}>
94113
{language.t("command.project.open")}
95114
</Button>
96115
</div>
97-
<ul class="flex flex-col gap-2">
116+
<ul
117+
class="flex flex-col rounded-lg overflow-hidden"
118+
style="border: 1px solid var(--border-weaker-base); background: var(--surface-base)"
119+
>
98120
<For each={recent()}>
99-
{(project) => (
100-
<Button
101-
size="large"
102-
variant="ghost"
103-
class="text-14-mono text-left justify-between px-3"
104-
onClick={() => openProject(project.worktree)}
121+
{(project, i) => (
122+
<li
123+
class="opacity-0"
124+
style={`animation: fadeUp 0.4s cubic-bezier(0.16, 1, 0.3, 1) ${0.3 + i() * 0.05}s forwards`}
105125
>
106-
{project.worktree.replace(homedir(), "~")}
107-
<div class="text-14-regular text-text-weak">
108-
{DateTime.fromMillis(project.time.updated ?? project.time.created).toRelative()}
109-
</div>
110-
</Button>
126+
<Button
127+
size="large"
128+
variant="ghost"
129+
class="text-14-mono text-left justify-between px-4 py-2.5 w-full rounded-none"
130+
style={i() < recent().length - 1 ? "border-bottom: 1px solid var(--border-weaker-base)" : ""}
131+
onClick={() => openProject(project.worktree)}
132+
>
133+
<span class="truncate">{project.worktree.replace(homedir(), "~")}</span>
134+
<span class="text-12-regular text-text-weaker flex-shrink-0 ml-4">
135+
{DateTime.fromMillis(project.time.updated ?? project.time.created).toRelative()}
136+
</span>
137+
</Button>
138+
</li>
111139
)}
112140
</For>
113141
</ul>
114142
</div>
115143
</Match>
116144
<Match when={true}>
117-
<div class="mt-30 mx-auto flex flex-col items-center gap-3">
118-
<Icon name="folder-add-left" size="large" />
145+
<div
146+
class="mt-20 mx-auto flex flex-col items-center gap-4 opacity-0"
147+
style="animation: fadeUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.25s forwards"
148+
>
149+
<div
150+
class="w-16 h-16 rounded-2xl flex items-center justify-center"
151+
style="background: var(--surface-base); border: 1px solid var(--border-weaker-base)"
152+
>
153+
<Icon name="folder-add-left" size="large" />
154+
</div>
119155
<div class="flex flex-col gap-1 items-center justify-center">
120156
<div class="text-14-medium text-text-strong">{language.t("home.empty.title")}</div>
121157
<div class="text-12-regular text-text-weak">{language.t("home.empty.description")}</div>
122158
</div>
123-
<Button class="px-3 mt-1" onClick={chooseProject}>
159+
<Button class="px-4 mt-1" onClick={chooseProject}>
124160
{language.t("command.project.open")}
125161
</Button>
126162
</div>

0 commit comments

Comments
 (0)