add mobile support
This commit is contained in:
parent
616410aa36
commit
f6c8b2a299
@ -12,6 +12,26 @@
|
||||
padding: 12px 20px;
|
||||
background: var(--bg-secondary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.mobile-menu-toggle {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mobile-menu-toggle:hover {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.header-brand {
|
||||
@ -29,11 +49,13 @@
|
||||
.header-brand h1 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.header-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
@ -61,6 +83,7 @@
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status-badge.connected .status-dot {
|
||||
@ -82,6 +105,11 @@
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sidebar-overlay {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
@ -97,9 +125,47 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
/* Responsive - Tablet */
|
||||
@media (max-width: 1200px) {
|
||||
.content-panels {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive - Mobile */
|
||||
@media (max-width: 768px) {
|
||||
.header {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.mobile-menu-toggle {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.header-brand h1 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.sidebar-overlay {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.content-panels {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useState, useCallback } from 'react'
|
||||
import { Menu, X } from 'lucide-react'
|
||||
import { Sidebar } from './components/Sidebar'
|
||||
import { ToolsPanel } from './components/ToolsPanel'
|
||||
import { ResultsPane } from './components/ResultsPane'
|
||||
@ -50,6 +51,7 @@ function App() {
|
||||
const [toolResult, setToolResult] = useState<ToolResult | null>(null)
|
||||
const [isExecuting, setIsExecuting] = useState(false)
|
||||
const [lastExecution, setLastExecution] = useState<{ toolName: string; params: Record<string, unknown> } | null>(null)
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false)
|
||||
|
||||
const handleConnect = useCallback(async () => {
|
||||
if (isConnected) {
|
||||
@ -93,6 +95,13 @@ function App() {
|
||||
return (
|
||||
<div className="app">
|
||||
<header className="header">
|
||||
<button
|
||||
className="mobile-menu-toggle"
|
||||
onClick={() => setSidebarOpen(!sidebarOpen)}
|
||||
aria-label={sidebarOpen ? 'Close menu' : 'Open menu'}
|
||||
>
|
||||
{sidebarOpen ? <X size={20} /> : <Menu size={20} />}
|
||||
</button>
|
||||
<div className="header-brand" onClick={() => window.location.reload()}>
|
||||
<h1>MCP UI Inspector</h1>
|
||||
</div>
|
||||
@ -100,18 +109,24 @@ function App() {
|
||||
{isConnected ? (
|
||||
<span className="status-badge connected">
|
||||
<span className="status-dot"></span>
|
||||
Connected
|
||||
<span className="status-text">Connected</span>
|
||||
</span>
|
||||
) : (
|
||||
<span className="status-badge disconnected">
|
||||
<span className="status-dot"></span>
|
||||
Disconnected
|
||||
<span className="status-text">Disconnected</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="main-layout">
|
||||
{sidebarOpen && (
|
||||
<div
|
||||
className="sidebar-overlay"
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
/>
|
||||
)}
|
||||
<Sidebar
|
||||
serverUrl={serverUrl}
|
||||
onServerUrlChange={setServerUrl}
|
||||
@ -121,6 +136,8 @@ function App() {
|
||||
isStateless={isStateless}
|
||||
onConnect={handleConnect}
|
||||
error={error}
|
||||
isOpen={sidebarOpen}
|
||||
onClose={() => setSidebarOpen(false)}
|
||||
/>
|
||||
|
||||
<main className="content">
|
||||
|
||||
@ -59,3 +59,17 @@
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.btn {
|
||||
padding: 10px 14px;
|
||||
font-size: 14px;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
min-height: 36px;
|
||||
padding: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 400px;
|
||||
min-width: 300px;
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
@ -193,3 +193,53 @@
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile styles */
|
||||
@media (max-width: 768px) {
|
||||
.results-pane {
|
||||
min-width: 0;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.results-pane .panel-header {
|
||||
padding: 0 12px;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.results-tabs {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.results-actions {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.results-loading,
|
||||
.results-empty,
|
||||
.results-error {
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.results-error pre {
|
||||
font-size: 11px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.text-output {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.text-output pre {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,25 @@
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Mobile sidebar */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.3s ease;
|
||||
width: 85%;
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
.sidebar.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-section {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ interface SidebarProps {
|
||||
isStateless: boolean
|
||||
onConnect: () => void
|
||||
error: string | null
|
||||
isOpen?: boolean
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
export function Sidebar({
|
||||
@ -21,10 +23,18 @@ export function Sidebar({
|
||||
sessionId,
|
||||
isStateless,
|
||||
onConnect,
|
||||
error
|
||||
error,
|
||||
isOpen,
|
||||
onClose
|
||||
}: SidebarProps) {
|
||||
const handleConnect = () => {
|
||||
onConnect()
|
||||
// Close sidebar on mobile after connecting
|
||||
if (onClose) onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="sidebar">
|
||||
<aside className={`sidebar ${isOpen ? 'open' : ''}`}>
|
||||
<div className="sidebar-section">
|
||||
<div className="sidebar-section-header">
|
||||
<Server size={16} />
|
||||
@ -46,7 +56,7 @@ export function Sidebar({
|
||||
|
||||
<Button
|
||||
variant={isConnected ? 'default' : 'primary'}
|
||||
onClick={onConnect}
|
||||
onClick={handleConnect}
|
||||
loading={isConnecting}
|
||||
loadingText="Connecting..."
|
||||
icon={isConnected ? <PlugZap size={16} /> : <Plug size={16} />}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid var(--border-color);
|
||||
min-width: 400px;
|
||||
min-width: 300px;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
@ -285,3 +285,55 @@
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile styles */
|
||||
@media (max-width: 768px) {
|
||||
.tools-panel {
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.tools-layout {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tools-list {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-height: 200px;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tool-item {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.tool-detail {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.tool-detail-header {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.tool-params {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.tool-actions {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.param-field input,
|
||||
.param-field select {
|
||||
font-size: 16px; /* Prevents zoom on iOS */
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.json-editor {
|
||||
font-size: 14px;
|
||||
min-height: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,3 +85,28 @@ code,
|
||||
background: var(--accent-blue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Touch-friendly improvements */
|
||||
@media (max-width: 768px) {
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
min-height: 44px; /* Minimum touch target size */
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-size: 16px; /* Prevents zoom on iOS */
|
||||
}
|
||||
}
|
||||
|
||||
/* Safe area insets for notched devices */
|
||||
@supports (padding: env(safe-area-inset-bottom)) {
|
||||
body {
|
||||
padding-left: env(safe-area-inset-left);
|
||||
padding-right: env(safe-area-inset-right);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user