Skip to content

Commit 448a7ce

Browse files
aster-voidclaude
andcommitted
treewide: redesign UI with Developer Terminal aesthetic
Design Philosophy: - Near-black base (#0a0a0b) with cyan primary accents - Monospace fonts for code and timestamps - Subtle borders with custom border-subtle class - Compact, information-dense but scannable layouts Changes: - app.css: Custom Prism theme with OKLCH colors, CSS variables - OrganizationSidebar: Simplified header with icon buttons - ChannelList: Section headers, # prefix icons, unread badges - Channel: Collapsible search, cleaner header - MessageItem: Compact layout, grouped metadata - MessageInput: Reorganized with clear sections UX Improvements: - Consistent spacing using 4px scale - Custom scrollbar styling - Focus states with primary color outline - Hover states for interactive elements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 47b5595 commit 448a7ce

File tree

9 files changed

+547
-306
lines changed

9 files changed

+547
-306
lines changed

apps/desktop/src/app.css

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,170 @@
44
/* biome-ignore lint/correctness/noUnknownProperty: daisyui config */
55
logs: false;
66
}
7+
8+
/* Prism Theme: Developer Terminal × Minimal Chat */
9+
@theme {
10+
/* Color System - Near-black base with cyan accents */
11+
--color-base-100: oklch(12% 0.01 260);
12+
--color-base-200: oklch(14% 0.01 260);
13+
--color-base-300: oklch(18% 0.015 260);
14+
--color-base-content: oklch(92% 0.01 260);
15+
16+
--color-primary: oklch(70% 0.15 195);
17+
--color-primary-content: oklch(15% 0.02 195);
18+
19+
--color-secondary: oklch(65% 0.12 280);
20+
--color-secondary-content: oklch(95% 0.01 280);
21+
22+
--color-accent: oklch(75% 0.18 160);
23+
--color-accent-content: oklch(15% 0.02 160);
24+
25+
--color-neutral: oklch(22% 0.015 260);
26+
--color-neutral-content: oklch(85% 0.01 260);
27+
28+
--color-success: oklch(72% 0.19 145);
29+
--color-warning: oklch(80% 0.16 85);
30+
--color-error: oklch(65% 0.2 25);
31+
--color-info: oklch(70% 0.15 230);
32+
33+
/* Subtle muted variants for UI chrome */
34+
--color-muted: oklch(45% 0.02 260);
35+
--color-border: oklch(25% 0.015 260);
36+
37+
/* Typography */
38+
--font-sans: "Inter Variable", "Inter", system-ui, sans-serif;
39+
--font-mono: "JetBrains Mono", "Fira Code", ui-monospace, monospace;
40+
41+
/* Spacing scale (4px base) */
42+
--spacing-px: 1px;
43+
--spacing-0: 0;
44+
--spacing-1: 0.25rem;
45+
--spacing-2: 0.5rem;
46+
--spacing-3: 0.75rem;
47+
--spacing-4: 1rem;
48+
--spacing-6: 1.5rem;
49+
--spacing-8: 2rem;
50+
51+
/* Border radius - subtle */
52+
--radius-sm: 0.25rem;
53+
--radius-md: 0.375rem;
54+
--radius-lg: 0.5rem;
55+
56+
/* Transitions */
57+
--duration-fast: 100ms;
58+
--duration-normal: 150ms;
59+
}
60+
61+
/* Base styles */
62+
html {
63+
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
64+
-webkit-font-smoothing: antialiased;
65+
-moz-osx-font-smoothing: grayscale;
66+
}
67+
68+
body {
69+
background-color: var(--color-base-100);
70+
color: var(--color-base-content);
71+
line-height: 1.5;
72+
}
73+
74+
/* Scrollbar styling */
75+
::-webkit-scrollbar {
76+
width: 8px;
77+
height: 8px;
78+
}
79+
80+
::-webkit-scrollbar-track {
81+
background: transparent;
82+
}
83+
84+
::-webkit-scrollbar-thumb {
85+
background: var(--color-border);
86+
border-radius: var(--radius-lg);
87+
}
88+
89+
::-webkit-scrollbar-thumb:hover {
90+
background: var(--color-muted);
91+
}
92+
93+
/* Focus states - subtle but visible */
94+
:focus-visible {
95+
outline: 2px solid var(--color-primary);
96+
outline-offset: 2px;
97+
}
98+
99+
/* Code blocks */
100+
pre,
101+
code {
102+
font-family: var(--font-mono);
103+
}
104+
105+
code {
106+
background: var(--color-base-300);
107+
padding: 0.125rem 0.375rem;
108+
border-radius: var(--radius-sm);
109+
font-size: 0.875em;
110+
}
111+
112+
pre code {
113+
background: transparent;
114+
padding: 0;
115+
}
116+
117+
/* Selection */
118+
::selection {
119+
background: oklch(70% 0.15 195 / 30%);
120+
}
121+
122+
/* Utility classes for the design system */
123+
.text-muted {
124+
color: var(--color-muted);
125+
}
126+
127+
.border-subtle {
128+
border-color: var(--color-border);
129+
}
130+
131+
/* Channel indicator prefix */
132+
.channel-prefix {
133+
color: var(--color-muted);
134+
font-family: var(--font-mono);
135+
font-weight: 500;
136+
}
137+
138+
/* Message timestamp styling */
139+
.timestamp {
140+
color: var(--color-muted);
141+
font-size: 0.75rem;
142+
font-family: var(--font-mono);
143+
font-variant-numeric: tabular-nums;
144+
}
145+
146+
/* Hover states for interactive elements */
147+
.hover-highlight {
148+
transition: background-color var(--duration-fast) ease;
149+
}
150+
151+
.hover-highlight:hover {
152+
background-color: var(--color-base-300);
153+
}
154+
155+
/* Unread indicator */
156+
.unread-dot {
157+
width: 8px;
158+
height: 8px;
159+
border-radius: 50%;
160+
background: var(--color-primary);
161+
}
162+
163+
/* Badge override for unread counts */
164+
.badge-unread {
165+
background: var(--color-primary);
166+
color: var(--color-primary-content);
167+
font-family: var(--font-mono);
168+
font-size: 0.625rem;
169+
font-weight: 600;
170+
min-width: 1.25rem;
171+
height: 1.25rem;
172+
padding: 0 0.375rem;
173+
}

apps/desktop/src/components/app/ChatApp.svelte

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,46 +25,43 @@
2525
const response = await getOrganization(api, organizationId).get();
2626
return unwrapResponse(response);
2727
});
28+
29+
function handleScreenModeChange(val: Selection) {
30+
if (val.type === "chat") {
31+
goto(`/orgs/${organizationId}/chat/${val.selectedChannelId}`);
32+
} else if (val.type === "personalization") {
33+
goto(`/orgs/${organizationId}/personalization`);
34+
}
35+
}
2836
</script>
2937

3038
<div class="bg-base-100 flex h-screen">
3139
<OrganizationSidebar organization={organization.data} {organizationId}>
3240
<ChannelList
3341
{organizationId}
34-
bind:screenMode={
35-
() => screenMode,
36-
(val) => {
37-
if (val.type === "chat") {
38-
goto(`/orgs/${organizationId}/chat/${val.selectedChannelId}`);
39-
} else if (val.type === "personalization") {
40-
goto(`/orgs/${organizationId}/personalization`);
41-
}
42-
}
43-
}
42+
bind:screenMode={() => screenMode, handleScreenModeChange}
4443
/>
4544
</OrganizationSidebar>
4645

47-
<div class="flex flex-1 flex-col">
48-
{#if screenMode.type == "chat"}
46+
<main class="flex flex-1 flex-col overflow-hidden">
47+
{#if screenMode.type === "chat"}
4948
{#if screenMode.selectedChannelId}
5049
<Channel
5150
{organizationId}
5251
selectedChannelId={screenMode.selectedChannelId}
5352
/>
5453
{:else}
55-
<div class="bg-base-200 flex flex-1 items-center justify-center">
54+
<div class="flex flex-1 items-center justify-center">
5655
<div class="text-center">
57-
<h2 class="text-base-content/60 mb-2 text-2xl font-semibold">
58-
{organization.data?.name || "組織"}へようこそ
59-
</h2>
60-
<p class="text-base-content/50">
61-
左からチャンネルを選択して会話を始めましょう
56+
<p class="text-muted text-lg">チャンネルを選択</p>
57+
<p class="text-muted mt-1 text-sm opacity-60">
58+
左のサイドバーからチャンネルを選んでください
6259
</p>
6360
</div>
6461
</div>
6562
{/if}
66-
{:else if screenMode.type == "personalization"}
63+
{:else if screenMode.type === "personalization"}
6764
<Personalization />
6865
{/if}
69-
</div>
66+
</main>
7067
</div>
Lines changed: 33 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<script lang="ts">
22
import type { Organization } from "@apps/api-client";
33
import type { Snippet } from "svelte";
4+
import MdiCog from "~icons/mdi/cog";
5+
import MdiSwapHorizontal from "~icons/mdi/swap-horizontal";
46
57
interface Props {
68
organization: Organization | undefined;
@@ -11,55 +13,36 @@
1113
const { organization, organizationId, children }: Props = $props();
1214
</script>
1315

14-
<div class="bg-base-200 border-base-300 flex h-full w-80 flex-col border-r">
15-
<div class="border-base-300 border-b p-4">
16-
<div class="flex items-center justify-between">
17-
<div>
18-
<h2 class="text-base-content text-lg font-bold">
19-
{organization?.name || "組織"}
20-
</h2>
21-
{#if organization?.description}
22-
<p class="text-base-content/70 text-sm">
23-
{organization.description}
24-
</p>
25-
{/if}
26-
</div>
27-
<div class="dropdown dropdown-end">
28-
<div tabindex="0" role="button" class="btn btn-ghost btn-sm btn-circle">
29-
<svg
30-
xmlns="http://www.w3.org/2000/svg"
31-
fill="none"
32-
viewBox="0 0 24 24"
33-
class="inline-block h-4 w-4 stroke-current"
34-
>
35-
<path
36-
stroke-linecap="round"
37-
stroke-linejoin="round"
38-
stroke-width="2"
39-
d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4"
40-
></path>
41-
</svg>
42-
</div>
43-
<ul
44-
role="menu"
45-
tabindex="0"
46-
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow"
47-
>
48-
<li role="menuitem">
49-
<a href="/orgs/{organizationId}/settings">組織設定</a>
50-
</li>
51-
<li role="menuitem">
52-
<a href="/">組織選択</a>
53-
</li>
54-
</ul>
55-
</div>
16+
<aside class="border-subtle bg-base-200 flex h-full w-72 flex-col border-r">
17+
<!-- Organization header -->
18+
<header
19+
class="border-subtle flex items-center justify-between border-b px-4 py-3"
20+
>
21+
<div class="min-w-0 flex-1">
22+
<h1 class="truncate font-semibold tracking-tight">
23+
{organization?.name ?? "Loading..."}
24+
</h1>
5625
</div>
57-
{#if organization?.permission}
58-
<div class="badge badge-outline mt-2 capitalize">
59-
{organization.permission}
60-
</div>
61-
{/if}
62-
</div>
26+
<div class="flex items-center gap-1">
27+
<a
28+
href="/orgs/{organizationId}/settings"
29+
class="btn btn-ghost btn-sm btn-square hover-highlight"
30+
title="組織設定"
31+
>
32+
<MdiCog class="text-muted h-4 w-4" />
33+
</a>
34+
<a
35+
href="/"
36+
class="btn btn-ghost btn-sm btn-square hover-highlight"
37+
title="組織を切り替え"
38+
>
39+
<MdiSwapHorizontal class="text-muted h-4 w-4" />
40+
</a>
41+
</div>
42+
</header>
6343

64-
{@render children()}
65-
</div>
44+
<!-- Channel list (children) -->
45+
<div class="flex-1 overflow-hidden">
46+
{@render children()}
47+
</div>
48+
</aside>

0 commit comments

Comments
 (0)