feat(web/ui): refactor component inline styles into utility classes

Pass 2 of the web UI spacing/typography/visual-rhythm rework.

- Add layout, spacing, typography, visual, card, and component utilities
- Add form-section, form-row, form-help, text-error, alert-success
- Unify .form-group and .form-field; add .status-badge family
- Alias legacy button classes to .btn primitives
- Refactor ToolTypeListSidebar and ConfigProfileListSidebar to use .sidebar
  and var(--sidebar-width) instead of hardcoded 280px
- Refactor ToolTypeEditorPanel, ConfigProfileEditorPanel,
  git-mount-editor, and manifest-editor to use utility classes

Quality gates: npm run typecheck, npm run lint, npm run build pass.
Inline style blocks in target components reduced from 198 to 11.
This commit is contained in:
Developer
2026-06-16 14:47:37 +00:00
parent 38d116dcf7
commit 5d5b39bec1
8 changed files with 957 additions and 571 deletions
@@ -53,35 +53,25 @@ export const ToolTypeEditorPanel = ({
const hasSelection = isCreating || selectedToolType;
return (
<div style={{ flex: 1, overflow: "auto", padding: "1.5rem", minWidth: 0 }}>
<div className="flex-1 overflow-auto p-5 min-w-0">
{!hasSelection ? (
<div
style={{
textAlign: "center",
paddingTop: "4rem",
color: "var(--muted)",
}}
>
<div style={{ opacity: 0.3, marginBottom: "1rem" }}>
<div className="empty-state">
<div className="empty-state-icon">
<Icon name="code" size="lg" />
</div>
<h3 style={{ margin: "0 0 0.5rem 0", fontWeight: 500 }}>
Select a tool type
</h3>
<p style={{ margin: 0 }}>
Choose a tool from the list to edit, or create a new one.
</p>
<h3>Select a tool type</h3>
<p>Choose a tool from the list to edit, or create a new one.</p>
</div>
) : (
<div>
<div style={{ marginBottom: "1.5rem" }}>
<h1 style={{ margin: "0 0 0.5rem 0", fontSize: "1.5rem" }}>
<div className="max-w-form">
<div className="mb-5">
<h1 className="m-0 mb-2 text-xl">
{isCreating
? "Create Tool Type"
: selectedToolType?.display_name}
</h1>
{!isCreating && (
<p className="muted" style={{ margin: 0 }}>
<p className="muted m-0">
{selectedToolType?.name} · {selectedToolType?.definition_type}{" "}
·{" "}
{selectedToolType?.interface_type === "web"
@@ -91,11 +81,7 @@ export const ToolTypeEditorPanel = ({
)}
</div>
<form
onSubmit={onSubmit}
className="stack"
style={{ gap: "1rem", maxWidth: "800px" }}
>
<form onSubmit={onSubmit} className="stack stack-md">
<div className="form-group">
<label htmlFor="definition-type">Definition Type</label>
<select
@@ -118,8 +104,8 @@ export const ToolTypeEditorPanel = ({
</select>
</div>
<div className="row" style={{ gap: "1rem" }}>
<div className="form-group" style={{ flex: 1 }}>
<div className="form-row row-md">
<div className="form-group">
<label htmlFor="tool-type-name">Name *</label>
<input
id="tool-type-name"
@@ -134,7 +120,7 @@ export const ToolTypeEditorPanel = ({
required
/>
</div>
<div className="form-group" style={{ flex: 1 }}>
<div className="form-group">
<label htmlFor="tool-type-display-name">
Display Name *
</label>
@@ -166,8 +152,8 @@ export const ToolTypeEditorPanel = ({
/>
</div>
<div className="row" style={{ gap: "1rem" }}>
<div className="form-group" style={{ flex: 1 }}>
<div className="form-row row-md">
<div className="form-group">
<label htmlFor="tool-type-category">Category</label>
<input
id="tool-type-category"
@@ -180,7 +166,7 @@ export const ToolTypeEditorPanel = ({
className="form-input"
/>
</div>
<div className="form-group" style={{ flex: 1 }}>
<div className="form-group">
<label htmlFor="tool-type-interface">Interface Type</label>
<select
id="tool-type-interface"
@@ -280,11 +266,7 @@ export const ToolTypeEditorPanel = ({
? "version: '3.8'\nservices:\n app:\n image: ..."
: "FROM node:18\nWORKDIR /app\n..."
}
className="form-input"
style={{
fontFamily: "monospace",
fontSize: "0.875rem",
}}
className="form-input font-mono text-sm"
required
/>
</div>
@@ -306,8 +288,8 @@ export const ToolTypeEditorPanel = ({
/>
</div>
<div className="row" style={{ gap: "1rem" }}>
<div className="form-group" style={{ flex: 1 }}>
<div className="form-row row-md">
<div className="form-group">
<label htmlFor="readiness-timeout">Timeout (seconds)</label>
<input
id="readiness-timeout"
@@ -319,7 +301,7 @@ export const ToolTypeEditorPanel = ({
className="form-input"
/>
</div>
<div className="form-group" style={{ flex: 1 }}>
<div className="form-group">
<label htmlFor="readiness-interval">
Interval (seconds)
</label>
@@ -350,8 +332,8 @@ export const ToolTypeEditorPanel = ({
{toolTypeError && <p className="text-error">{toolTypeError}</p>}
<div className="dialog-actions" style={{ marginTop: "1rem" }}>
<button type="submit">
<div className="dialog-actions mt-4">
<button type="submit" className="btn btn-primary">
<Icon name={isCreating ? "add" : "save"} size="sm" />
{isCreating ? "Create Tool Type" : "Save Changes"}
</button>
@@ -359,7 +341,7 @@ export const ToolTypeEditorPanel = ({
<button
type="button"
onClick={onReset}
className="button-secondary"
className="btn btn-secondary"
>
<Icon name="cancel" size="sm" /> Discard
</button>
@@ -17,82 +17,28 @@ export const ToolTypeListSidebar = ({
onDelete,
}: ToolTypeListSidebarProps) => {
return (
<div
style={{
width: "280px",
minWidth: "280px",
borderRight: "1px solid var(--border)",
display: "flex",
flexDirection: "column",
background: "var(--panel)",
}}
>
<div
style={{ padding: "1rem", borderBottom: "1px solid var(--border)" }}
>
<h2 style={{ margin: 0, fontSize: "1.125rem" }}>Tool Workshop</h2>
<p
className="muted"
style={{ margin: "0.25rem 0 0 0", fontSize: "0.875rem" }}
>
<div className="sidebar">
<div className="sidebar-header">
<h2>Tool Workshop</h2>
<p className="muted">
{toolTypes.length} tool type{toolTypes.length !== 1 ? "s" : ""}
</p>
</div>
<div style={{ flex: 1, overflowY: "auto", padding: "0.5rem" }}>
<div className="sidebar-list">
{toolTypes.map((toolType) => (
<button
key={toolType.id}
onClick={() => onSelect(toolType)}
style={{
width: "100%",
textAlign: "left",
padding: "0.75rem 1rem",
marginBottom: "0.25rem",
borderRadius: "0.375rem",
border: "none",
background:
selectedToolTypeId === toolType.id
? "var(--brand)"
: "transparent",
color:
selectedToolTypeId === toolType.id ? "white" : "var(--ink)",
cursor: "pointer",
display: "flex",
alignItems: "center",
gap: "0.75rem",
transition: "background 0.15s",
}}
onMouseEnter={(e) => {
if (selectedToolTypeId !== toolType.id) {
e.currentTarget.style.background = "#ece7df";
}
}}
onMouseLeave={(e) => {
if (selectedToolTypeId !== toolType.id) {
e.currentTarget.style.background = "transparent";
}
}}
className={
selectedToolTypeId === toolType.id
? "sidebar-item sidebar-item-active"
: "sidebar-item"
}
>
<div style={{ flex: 1, minWidth: 0 }}>
<div
style={{
fontWeight: 600,
fontSize: "0.9375rem",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
{toolType.display_name}
</div>
<div
style={{
fontSize: "0.8125rem",
opacity: 0.8,
marginTop: "0.125rem",
}}
>
<div className="sidebar-item-content">
<div className="sidebar-item-title">{toolType.display_name}</div>
<div className="sidebar-item-subtitle">
{toolType.category || "Uncategorized"} ·{" "}
{toolType.interface_type === "web"
? `Port ${toolType.default_port}`
@@ -104,19 +50,6 @@ export const ToolTypeListSidebar = ({
e.stopPropagation();
onDelete(toolType.id);
}}
style={{
background: "none",
border: "none",
color:
selectedToolTypeId === toolType.id
? "rgba(255,255,255,0.8)"
: "var(--muted)",
cursor: "pointer",
padding: "0.25rem",
borderRadius: "0.25rem",
flexShrink: 0,
opacity: 0,
}}
className="delete-btn"
title="Delete tool type"
>
@@ -126,33 +59,8 @@ export const ToolTypeListSidebar = ({
))}
</div>
<div style={{ padding: "1rem", borderTop: "1px solid var(--border)" }}>
<button
onClick={onCreate}
style={{
width: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: "0.5rem",
padding: "0.75rem",
borderRadius: "0.5rem",
border: "2px dashed var(--border)",
background: "transparent",
color: "var(--muted)",
cursor: "pointer",
fontWeight: 600,
transition: "all 0.15s",
}}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = "var(--brand)";
e.currentTarget.style.color = "var(--brand)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = "var(--border)";
e.currentTarget.style.color = "var(--muted)";
}}
>
<div className="sidebar-footer">
<button onClick={onCreate} className="sidebar-create-button">
<Icon name="add" size="sm" /> New Tool Type
</button>
</div>