Skip to content

Commit 4567dcc

Browse files
committed
feat: enhance gateway terminal and chat input UI
- Updated the Gateway Terminal component with improved styling and functionality, including a new focus button and connection status indicators. - Enhanced chat input bar with refined styles, added access token input for authentication, and improved layout for better user experience. - Introduced a typography scale in global CSS for consistent text sizing across components. - Updated various components to reflect the new "Gateway Terminal" label for clarity.
1 parent 4c33773 commit 4567dcc

9 files changed

Lines changed: 340 additions & 93 deletions

File tree

app/globals.css

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,18 @@
118118
--shell-chip-hover: color-mix(in srgb, var(--text-primary, #ededed) 6%, transparent);
119119
--shell-status-bg: color-mix(in srgb, var(--bg-elevated, #141414) 88%, transparent);
120120
--shell-status-border: color-mix(in srgb, var(--border, #262626) 86%, transparent);
121+
122+
/* Typography scale */
123+
--font-ui: var(--font-sans);
124+
--font-technical: var(--font-mono);
125+
--text-xs: 0.75rem;
126+
--text-sm: 0.875rem;
127+
--text-md: 0.9375rem;
128+
--text-lg: 1rem;
129+
--text-xl: 1.125rem;
130+
--leading-tight-ui: 1.35;
131+
--leading-ui: 1.55;
132+
--leading-relaxed-ui: 1.68;
121133
}
122134

123135
/* Consistent keyboard focus across the app */
@@ -6466,7 +6478,9 @@ a:active,
64666478
border-radius: inherit;
64676479
padding: 1px;
64686480
background: linear-gradient(135deg, var(--brand, #3b82f6), var(--accent, #8b5cf6));
6469-
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
6481+
-webkit-mask:
6482+
linear-gradient(#fff 0 0) content-box,
6483+
linear-gradient(#fff 0 0);
64706484
-webkit-mask-composite: xor;
64716485
mask-composite: exclude;
64726486
opacity: 0;
@@ -6531,3 +6545,95 @@ a:active,
65316545
color: black !important;
65326546
}
65336547
}
6548+
6549+
/* ──────────────────────────────────────────────────────────────────
6550+
Gateway + Chat Polish Pass
6551+
────────────────────────────────────────────────────────────────── */
6552+
.view-router-frame {
6553+
background:
6554+
radial-gradient(
6555+
circle at top,
6556+
color-mix(in srgb, var(--brand) 4%, transparent),
6557+
transparent 42%
6558+
),
6559+
linear-gradient(180deg, color-mix(in srgb, var(--bg) 96%, transparent), var(--bg));
6560+
}
6561+
6562+
.chat-input-shell {
6563+
background: color-mix(in srgb, var(--bg) 94%, var(--bg-elevated));
6564+
border-color: color-mix(in srgb, var(--border) 94%, transparent);
6565+
box-shadow:
6566+
inset 0 1px 0 color-mix(in srgb, white 4%, transparent),
6567+
0 14px 36px -24px rgba(0, 0, 0, 0.42);
6568+
}
6569+
6570+
.chat-input-shell:hover {
6571+
border-color: color-mix(in srgb, var(--border-hover) 88%, var(--border));
6572+
}
6573+
6574+
.chat-input-shell:focus-within {
6575+
border-color: color-mix(in srgb, var(--brand) 16%, var(--border));
6576+
background: color-mix(in srgb, var(--bg) 90%, var(--bg-elevated));
6577+
box-shadow:
6578+
inset 0 0 0 1px color-mix(in srgb, var(--brand) 8%, transparent),
6579+
0 0 0 2px color-mix(in srgb, var(--brand) 4%, transparent),
6580+
0 16px 38px -26px color-mix(in srgb, var(--brand) 14%, rgba(0, 0, 0, 0.45));
6581+
}
6582+
6583+
.chat-input-textarea {
6584+
font-family: var(--font-ui);
6585+
font-size: var(--text-lg);
6586+
line-height: var(--leading-relaxed-ui);
6587+
letter-spacing: -0.012em;
6588+
}
6589+
6590+
.chat-input-textarea::selection,
6591+
.gateway-credential-input::selection,
6592+
.gateway-connect-url-input::selection {
6593+
background-color: color-mix(in srgb, var(--brand) 18%, transparent);
6594+
}
6595+
6596+
.gateway-connect-url-input,
6597+
.gateway-credential-input {
6598+
font-family: var(--font-technical);
6599+
font-size: var(--text-md);
6600+
line-height: 1.45;
6601+
letter-spacing: -0.01em;
6602+
}
6603+
6604+
.gateway-connect-url-input:hover,
6605+
.gateway-credential-input:hover {
6606+
border-color: color-mix(in srgb, var(--border-hover) 92%, var(--border));
6607+
}
6608+
6609+
.gateway-connect-url-input:focus,
6610+
.gateway-credential-input:focus {
6611+
outline: none;
6612+
border-color: color-mix(in srgb, var(--brand) 18%, var(--border));
6613+
box-shadow:
6614+
inset 0 0 0 1px color-mix(in srgb, var(--brand) 8%, transparent),
6615+
0 0 0 2px color-mix(in srgb, var(--brand) 4%, transparent);
6616+
}
6617+
6618+
.gateway-credential-input {
6619+
-webkit-text-security: disc;
6620+
}
6621+
6622+
.terminal-md,
6623+
.terminal-md .prose-chat,
6624+
.terminal-md .md-preview {
6625+
font-family: var(--font-ui);
6626+
font-size: 15px;
6627+
line-height: 1.72;
6628+
letter-spacing: -0.012em;
6629+
}
6630+
6631+
.terminal-md .prose-chat :where(code, pre),
6632+
.terminal-md .md-preview :where(code, pre) {
6633+
font-family: var(--font-technical);
6634+
}
6635+
6636+
.terminal-md .prose-chat code,
6637+
.terminal-md .md-preview code {
6638+
font-size: 0.92em;
6639+
}

app/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const VIEW_ICONS: Record<string, { icon: string; label: string }> = {
8383
skills: { icon: 'lucide:sparkles', label: 'Skills' },
8484
prompts: { icon: 'lucide:book-open', label: 'Prompts' },
8585
settings: { icon: 'lucide:settings', label: 'Settings' },
86-
terminal: { icon: 'lucide:terminal', label: 'Terminal' },
86+
terminal: { icon: 'lucide:terminal', label: 'Gateway Terminal' },
8787
kanban: { icon: 'lucide:kanban', label: 'Kanban' },
8888
mcp: { icon: 'lucide:plug', label: 'MCP' },
8989
workshop: { icon: 'lucide:hammer', label: 'Workshop' },
@@ -515,7 +515,7 @@ export default function EditorLayout() {
515515
<div className="flex items-center gap-2">
516516
<div className="min-w-0 flex-1">
517517
<div className="flex items-center gap-1.5">
518-
<span className="text-[17px] font-semibold text-[var(--text-primary)] tracking-tight">
518+
<span className="text-[18px] font-semibold tracking-[-0.025em] text-[var(--text-primary)]">
519519
{workspaceLabel === 'KnotCode' ? 'Knot Code' : workspaceLabel}
520520
</span>
521521
<span
@@ -664,7 +664,7 @@ export default function EditorLayout() {
664664
<div className="w-9 h-1 rounded-full bg-[var(--text-disabled)] opacity-40" />
665665
</div>
666666
<div className="h-11 flex items-center justify-between px-4 border-b border-[var(--border)] bg-[var(--bg-secondary)]">
667-
<span className="text-[13px] font-semibold text-[var(--text-primary)] flex items-center gap-2.5">
667+
<span className="flex items-center gap-2.5 text-[14px] font-semibold tracking-[-0.015em] text-[var(--text-primary)]">
668668
<Icon
669669
icon="lucide:terminal"
670670
width={16}

components/agent-panel.tsx

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,17 @@ function AgentConnectPrompt() {
7070
const isMobileDevice = typeof window !== 'undefined' && window.innerWidth <= 768
7171
const [url, setUrl] = useState(isMobileDevice ? '' : 'ws://localhost:18789')
7272
const [password, setPassword] = useState('')
73+
const [accessToken, setAccessToken] = useState('')
7374

7475
const isConnecting = status === 'connecting' || status === 'authenticating'
7576

7677
const handleConnect = () => {
7778
if (!url.trim()) return
78-
connect(url.trim(), password)
79+
connect(url.trim(), accessToken.trim() || password)
7980
}
8081

8182
return (
82-
<div className="flex flex-1 flex-col items-center justify-center text-center px-6">
83+
<div className="flex flex-1 flex-col items-center justify-center px-6 text-center">
8384
{/* Animated connection icon */}
8485
<div className="relative mb-6">
8586
<div
@@ -102,21 +103,44 @@ function AgentConnectPrompt() {
102103

103104
{isConnecting ? (
104105
<>
105-
<h3 className="text-[17px] font-semibold text-[var(--text-primary)] mb-1">Connecting…</h3>
106-
<p className="text-[13px] text-[var(--text-tertiary)]">Looking for your gateway</p>
106+
<h3 className="mb-1 text-[19px] font-semibold tracking-[-0.02em] text-[var(--text-primary)]">
107+
Connecting…
108+
</h3>
109+
<p className="text-[14px] leading-[1.65] text-[var(--text-tertiary)]">
110+
Looking for your gateway
111+
</p>
107112
</>
108113
) : (
109114
<>
110-
<h3 className="text-[17px] font-semibold text-[var(--text-primary)] mb-1">
115+
<h3 className="mb-1 text-[20px] font-semibold tracking-[-0.025em] text-[var(--text-primary)]">
111116
Connect to Gateway
112117
</h3>
113-
<p className="text-[13px] text-[var(--text-tertiary)] leading-relaxed mb-6 max-w-[280px]">
118+
<p className="mb-4 max-w-[360px] text-[14px] leading-[1.7] text-[var(--text-tertiary)]">
114119
{isMobileDevice
115-
? 'Enter your gateway address to start chatting.'
116-
: 'Make sure OpenClaw is running on this machine.'}
120+
? 'Enter your gateway address, then use a token or password if your gateway requires auth.'
121+
: 'Paste your gateway URL here. If auth is enabled, the token/password fields live directly below.'}
117122
</p>
118123

119-
<div className="w-full max-w-[340px] space-y-3">
124+
<div className="mb-4 w-full max-w-[360px] rounded-2xl border border-[color-mix(in_srgb,var(--brand)_18%,var(--border))] bg-[color-mix(in_srgb,var(--brand)_4%,transparent)] px-4 py-3.5 text-left shadow-[0_18px_40px_-28px_color-mix(in_srgb,var(--brand)_26%,transparent)]">
125+
<div className="flex items-start gap-2">
126+
<div className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-[color-mix(in_srgb,var(--brand)_12%,transparent)] text-[var(--brand)]">
127+
<Icon icon="lucide:key-round" width={14} height={14} />
128+
</div>
129+
<div className="min-w-0">
130+
<p className="text-[13px] font-semibold tracking-[-0.01em] text-[var(--text-primary)]">
131+
Gateway credentials
132+
</p>
133+
<p className="mt-1 text-[12px] leading-[1.65] text-[var(--text-secondary)]">
134+
Use <span className="font-medium text-[var(--text-primary)]">Access token</span>{' '}
135+
first when you have one. The fallback{' '}
136+
<span className="font-medium text-[var(--text-primary)]">Password</span> field is
137+
also masked.
138+
</p>
139+
</div>
140+
</div>
141+
</div>
142+
143+
<div className="w-full max-w-[360px] space-y-3">
120144
{/* URL input */}
121145
<div className="relative">
122146
<Icon
@@ -133,10 +157,35 @@ function AgentConnectPrompt() {
133157
if (e.key === 'Enter') handleConnect()
134158
}}
135159
placeholder={isMobileDevice ? 'wss://your-gateway.ts.net' : 'ws://localhost:18789'}
136-
className="w-full pl-10 pr-3 py-3.5 rounded-xl bg-[var(--bg)] border border-[var(--border)] text-[14px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none focus:border-[var(--brand)] focus:ring-1 focus:ring-[color-mix(in_srgb,var(--brand)_30%,transparent)] transition-all"
160+
className="w-full pl-10 pr-3 py-3.5 rounded-xl bg-[var(--bg)] border border-[var(--border)] text-[14px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none transition-all gateway-connect-url-input"
161+
autoCapitalize="off"
162+
autoCorrect="off"
163+
spellCheck={false}
164+
/>
165+
</div>
166+
167+
{/* Access token input */}
168+
<div className="relative">
169+
<Icon
170+
icon="lucide:key-round"
171+
width={15}
172+
height={15}
173+
className="absolute left-3.5 top-1/2 -translate-y-1/2 text-[var(--text-disabled)]"
174+
/>
175+
<input
176+
type="password"
177+
value={accessToken}
178+
onChange={(e) => setAccessToken(e.target.value)}
179+
onKeyDown={(e) => {
180+
if (e.key === 'Enter') handleConnect()
181+
}}
182+
placeholder="Access token (recommended)"
183+
className="gateway-credential-input w-full pl-10 pr-3 py-3.5 rounded-xl bg-[var(--bg)] border border-[var(--border)] text-[14px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none transition-all"
137184
autoCapitalize="off"
138185
autoCorrect="off"
139186
spellCheck={false}
187+
autoComplete="current-password"
188+
data-1p-ignore
140189
/>
141190
</div>
142191

@@ -155,16 +204,28 @@ function AgentConnectPrompt() {
155204
onKeyDown={(e) => {
156205
if (e.key === 'Enter') handleConnect()
157206
}}
158-
placeholder="Password (optional)"
159-
className="w-full pl-10 pr-3 py-3.5 rounded-xl bg-[var(--bg)] border border-[var(--border)] text-[14px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none focus:border-[var(--brand)] focus:ring-1 focus:ring-[color-mix(in_srgb,var(--brand)_30%,transparent)] transition-all"
207+
placeholder="Password (fallback)"
208+
className="gateway-credential-input w-full pl-10 pr-3 py-3.5 rounded-xl bg-[var(--bg)] border border-[var(--border)] text-[14px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none transition-all"
209+
autoComplete="current-password"
210+
data-1p-ignore
160211
/>
161212
</div>
162213

214+
<div className="rounded-xl border border-[var(--border)] bg-[color-mix(in_srgb,var(--bg-elevated)_82%,transparent)] px-3.5 py-2.5">
215+
<p className="text-[12px] font-medium tracking-[-0.01em] text-[var(--text-secondary)]">
216+
Where do I put the gateway secret?
217+
</p>
218+
<p className="mt-1 text-[11.5px] leading-[1.65] text-[var(--text-disabled)]">
219+
Right here: paste a gateway access token into the token field, or use the password
220+
field if your setup still uses password auth. Both stay masked in the UI.
221+
</p>
222+
</div>
223+
163224
{/* Connect button */}
164225
<button
165226
onClick={handleConnect}
166227
disabled={!url.trim()}
167-
className="w-full py-3.5 rounded-xl text-[14px] font-semibold transition-all cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed"
228+
className="w-full rounded-xl py-3.5 text-[14.5px] font-semibold tracking-[-0.01em] transition-all cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed"
168229
style={{
169230
backgroundColor: url.trim() ? 'var(--brand)' : 'var(--bg-subtle)',
170231
color: url.trim() ? 'var(--brand-contrast, #fff)' : 'var(--text-disabled)',
@@ -904,6 +965,7 @@ export function AgentPanel({ onClose }: { onClose?: () => void } = {}) {
904965
return labels
905966
}, [contextAttachments, imageAttachments])
906967

968+
// Follow-up seam: fallback local agent routing (Cursor Agent / Claude Code CLI) should plug in above sendStructuredGatewayMessage, while keeping local repo/docs as the default context source before any remote fetch.
907969
const buildSilentContext = useCallback(() => {
908970
const context = buildContext()
909971
const attachCtx = buildAttachmentContext()
@@ -2485,7 +2547,7 @@ export function AgentPanel({ onClose }: { onClose?: () => void } = {}) {
24852547
onClose={onClose}
24862548
/>
24872549
{messages.length > 0 && (
2488-
<div className="flex items-center justify-between border-b border-[var(--border)] bg-[var(--bg-elevated)] px-2.5 py-0.5 shrink-0">
2550+
<div className="flex shrink-0 items-center justify-between border-b border-[var(--border)] bg-[color-mix(in_srgb,var(--bg-elevated)_92%,transparent)] px-3 py-1.5 backdrop-blur-sm">
24892551
<div className="flex min-w-0 items-center gap-1.5">
24902552
{/* Font size controls */}
24912553
<div className="inline-flex items-center gap-0.5">
@@ -2496,7 +2558,7 @@ export function AgentPanel({ onClose }: { onClose?: () => void } = {}) {
24962558
>
24972559
<Icon icon="lucide:minus" width={12} height={12} />
24982560
</button>
2499-
<span className="w-7 select-none text-center text-[10px] font-mono tabular-nums text-[var(--text-disabled)]">
2561+
<span className="w-7 select-none text-center text-[11px] font-mono tabular-nums text-[var(--text-disabled)]">
25002562
{chatFontSize}
25012563
</span>
25022564
<button
@@ -2516,7 +2578,7 @@ export function AgentPanel({ onClose }: { onClose?: () => void } = {}) {
25162578
<button
25172579
key={f.id}
25182580
onClick={() => setChatFontFamily(f.id)}
2519-
className={`whitespace-nowrap rounded px-2 py-0.5 text-[10px] font-medium transition-colors cursor-pointer ${
2581+
className={`whitespace-nowrap rounded-md px-2.5 py-1 text-[11px] font-medium transition-colors cursor-pointer ${
25202582
chatFontFamily === f.id
25212583
? 'text-[var(--brand)] bg-[color-mix(in_srgb,var(--brand)_10%,transparent)]'
25222584
: 'text-[var(--text-disabled)] hover:text-[var(--text-tertiary)]'

0 commit comments

Comments
 (0)