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:
@@ -284,12 +284,12 @@ export const ManifestEditor = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="stack" style={{ gap: "1.5rem" }}>
|
||||
<div className="stack stack-lg">
|
||||
{/* Base Image */}
|
||||
<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
|
||||
<h4 style={{ margin: 0 }}>Base Image</h4>
|
||||
<div className="row" style={{ gap: "1rem" }}>
|
||||
<div className="form-group" style={{ flex: 1 }}>
|
||||
<div className="card-md stack stack-md">
|
||||
<h4 className="m-0">Base Image</h4>
|
||||
<div className="form-row row-md">
|
||||
<div className="form-group">
|
||||
<label>Base Definition</label>
|
||||
<select
|
||||
value={baseDefinitionId}
|
||||
@@ -307,7 +307,7 @@ export const ManifestEditor = ({
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group" style={{ flex: 1 }}>
|
||||
<div className="form-group">
|
||||
<label>Custom Base Image</label>
|
||||
<input
|
||||
type="text"
|
||||
@@ -325,8 +325,8 @@ export const ManifestEditor = ({
|
||||
</div>
|
||||
|
||||
{/* Packages */}
|
||||
<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
|
||||
<h4 style={{ margin: 0 }}>Packages</h4>
|
||||
<div className="card-md stack stack-md">
|
||||
<h4 className="m-0">Packages</h4>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Node.js Version</label>
|
||||
@@ -342,9 +342,9 @@ export const ManifestEditor = ({
|
||||
|
||||
<div>
|
||||
<label>APT Packages</label>
|
||||
<div className="stack" style={{ gap: "0.5rem", marginTop: "0.5rem" }}>
|
||||
<div className="stack stack-sm mt-2">
|
||||
{aptPackages.map((pkg, idx) => (
|
||||
<div key={idx} className="row" style={{ gap: "0.5rem" }}>
|
||||
<div key={idx} className="form-row row-sm">
|
||||
<input
|
||||
type="text"
|
||||
value={pkg.name}
|
||||
@@ -365,8 +365,7 @@ export const ManifestEditor = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={addAptPackage}
|
||||
className="button-secondary"
|
||||
style={{ width: "fit-content" }}
|
||||
className="button-secondary w-fit"
|
||||
>
|
||||
<Icon name="add" size="sm" /> Add APT Package
|
||||
</button>
|
||||
@@ -375,9 +374,9 @@ export const ManifestEditor = ({
|
||||
|
||||
<div>
|
||||
<label>NPM Global Packages</label>
|
||||
<div className="stack" style={{ gap: "0.5rem", marginTop: "0.5rem" }}>
|
||||
<div className="stack stack-sm mt-2">
|
||||
{npmPackages.map((pkg, idx) => (
|
||||
<div key={idx} className="row" style={{ gap: "0.5rem" }}>
|
||||
<div key={idx} className="form-row row-sm">
|
||||
<input
|
||||
type="text"
|
||||
value={pkg.name}
|
||||
@@ -398,8 +397,7 @@ export const ManifestEditor = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={addNpmPackage}
|
||||
className="button-secondary"
|
||||
style={{ width: "fit-content" }}
|
||||
className="button-secondary w-fit"
|
||||
>
|
||||
<Icon name="add" size="sm" /> Add NPM Package
|
||||
</button>
|
||||
@@ -408,9 +406,9 @@ export const ManifestEditor = ({
|
||||
|
||||
<div>
|
||||
<label>Pip Packages</label>
|
||||
<div className="stack" style={{ gap: "0.5rem", marginTop: "0.5rem" }}>
|
||||
<div className="stack stack-sm mt-2">
|
||||
{pipPackages.map((pkg, idx) => (
|
||||
<div key={idx} className="row" style={{ gap: "0.5rem" }}>
|
||||
<div key={idx} className="form-row row-sm">
|
||||
<input
|
||||
type="text"
|
||||
value={pkg.name}
|
||||
@@ -431,8 +429,7 @@ export const ManifestEditor = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={addPipPackage}
|
||||
className="button-secondary"
|
||||
style={{ width: "fit-content" }}
|
||||
className="button-secondary w-fit"
|
||||
>
|
||||
<Icon name="add" size="sm" /> Add Pip Package
|
||||
</button>
|
||||
@@ -441,10 +438,10 @@ export const ManifestEditor = ({
|
||||
</div>
|
||||
|
||||
{/* Runtime User */}
|
||||
<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
|
||||
<h4 style={{ margin: 0 }}>Runtime User</h4>
|
||||
<div className="row" style={{ gap: "1rem" }}>
|
||||
<div className="form-group" style={{ flex: 1 }}>
|
||||
<div className="card-md stack stack-md">
|
||||
<h4 className="m-0">Runtime User</h4>
|
||||
<div className="form-row row-md">
|
||||
<div className="form-group">
|
||||
<label>User Name</label>
|
||||
<input
|
||||
type="text"
|
||||
@@ -453,7 +450,7 @@ export const ManifestEditor = ({
|
||||
className="form-input"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group" style={{ flex: 1 }}>
|
||||
<div className="form-group">
|
||||
<label>UID</label>
|
||||
<input
|
||||
type="number"
|
||||
@@ -462,7 +459,7 @@ export const ManifestEditor = ({
|
||||
className="form-input"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group" style={{ flex: 1 }}>
|
||||
<div className="form-group">
|
||||
<label>GID</label>
|
||||
<input
|
||||
type="number"
|
||||
@@ -475,11 +472,11 @@ export const ManifestEditor = ({
|
||||
</div>
|
||||
|
||||
{/* Environment */}
|
||||
<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
|
||||
<h4 style={{ margin: 0 }}>Environment Variables</h4>
|
||||
<div className="stack" style={{ gap: "0.5rem" }}>
|
||||
<div className="card-md stack stack-md">
|
||||
<h4 className="m-0">Environment Variables</h4>
|
||||
<div className="stack stack-sm">
|
||||
{envVars.map((ev, idx) => (
|
||||
<div key={idx} className="row" style={{ gap: "0.5rem" }}>
|
||||
<div key={idx} className="form-row row-sm">
|
||||
<input
|
||||
type="text"
|
||||
value={ev.key}
|
||||
@@ -507,8 +504,7 @@ export const ManifestEditor = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={addEnvVar}
|
||||
className="button-secondary"
|
||||
style={{ width: "fit-content" }}
|
||||
className="button-secondary w-fit"
|
||||
>
|
||||
<Icon name="add" size="sm" /> Add Env Var
|
||||
</button>
|
||||
@@ -516,26 +512,20 @@ export const ManifestEditor = ({
|
||||
</div>
|
||||
|
||||
{/* Build Scripts */}
|
||||
<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
|
||||
<h4 style={{ margin: 0 }}>Build Scripts (run during docker build)</h4>
|
||||
<div className="stack" style={{ gap: "0.5rem" }}>
|
||||
<div className="card-md stack stack-md">
|
||||
<h4 className="m-0">Build Scripts (run during docker build)</h4>
|
||||
<div className="stack stack-sm">
|
||||
{buildScripts.map((script, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="row"
|
||||
style={{ gap: "0.5rem", alignItems: "flex-start" }}
|
||||
className="form-row row-sm items-start"
|
||||
>
|
||||
<textarea
|
||||
value={script}
|
||||
onChange={(e) => updateBuildScript(idx, e.target.value)}
|
||||
placeholder="git config --global user.email 'dev@example.com'"
|
||||
className="form-input"
|
||||
className="form-input font-mono text-sm flex-1"
|
||||
rows={2}
|
||||
style={{
|
||||
fontFamily: "monospace",
|
||||
fontSize: "0.8125rem",
|
||||
flex: 1,
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
@@ -550,8 +540,7 @@ export const ManifestEditor = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={addBuildScript}
|
||||
className="button-secondary"
|
||||
style={{ width: "fit-content" }}
|
||||
className="button-secondary w-fit"
|
||||
>
|
||||
<Icon name="add" size="sm" /> Add Build Script
|
||||
</button>
|
||||
@@ -559,28 +548,22 @@ export const ManifestEditor = ({
|
||||
</div>
|
||||
|
||||
{/* Startup Scripts */}
|
||||
<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
|
||||
<h4 style={{ margin: 0 }}>
|
||||
<div className="card-md stack stack-md">
|
||||
<h4 className="m-0">
|
||||
Startup Scripts (run when container starts)
|
||||
</h4>
|
||||
<div className="stack" style={{ gap: "0.5rem" }}>
|
||||
<div className="stack stack-sm">
|
||||
{startupScripts.map((script, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="row"
|
||||
style={{ gap: "0.5rem", alignItems: "flex-start" }}
|
||||
className="form-row row-sm items-start"
|
||||
>
|
||||
<textarea
|
||||
value={script}
|
||||
onChange={(e) => updateStartupScript(idx, e.target.value)}
|
||||
placeholder="chown -R user:user /workspace"
|
||||
className="form-input"
|
||||
className="form-input font-mono text-sm flex-1"
|
||||
rows={2}
|
||||
style={{
|
||||
fontFamily: "monospace",
|
||||
fontSize: "0.8125rem",
|
||||
flex: 1,
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
@@ -595,8 +578,7 @@ export const ManifestEditor = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={addStartupScript}
|
||||
className="button-secondary"
|
||||
style={{ width: "fit-content" }}
|
||||
className="button-secondary w-fit"
|
||||
>
|
||||
<Icon name="add" size="sm" /> Add Startup Script
|
||||
</button>
|
||||
@@ -604,21 +586,15 @@ export const ManifestEditor = ({
|
||||
</div>
|
||||
|
||||
{/* Mounts */}
|
||||
<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
|
||||
<h4 style={{ margin: 0 }}>Mount Schema</h4>
|
||||
<div className="stack" style={{ gap: "1rem" }}>
|
||||
<div className="card-md stack stack-md">
|
||||
<h4 className="m-0">Mount Schema</h4>
|
||||
<div className="stack stack-md">
|
||||
{mounts.map((mount, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="stack"
|
||||
style={{
|
||||
gap: "0.5rem",
|
||||
padding: "0.75rem",
|
||||
border: "1px solid var(--border)",
|
||||
borderRadius: "0.375rem",
|
||||
}}
|
||||
className="stack stack-sm p-3 border rounded-md"
|
||||
>
|
||||
<div className="row" style={{ gap: "0.5rem" }}>
|
||||
<div className="form-row row-sm">
|
||||
<input
|
||||
type="text"
|
||||
value={mount.name}
|
||||
@@ -655,15 +631,8 @@ export const ManifestEditor = ({
|
||||
<Icon name="delete" size="sm" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="row" style={{ gap: "0.5rem" }}>
|
||||
<label
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "0.25rem",
|
||||
fontSize: "0.875rem",
|
||||
}}
|
||||
>
|
||||
<div className="form-row row-sm flex-wrap">
|
||||
<label className="checkbox-label text-sm min-h-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={mount.writable}
|
||||
@@ -673,14 +642,7 @@ export const ManifestEditor = ({
|
||||
/>
|
||||
Writable
|
||||
</label>
|
||||
<label
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "0.25rem",
|
||||
fontSize: "0.875rem",
|
||||
}}
|
||||
>
|
||||
<label className="checkbox-label text-sm min-h-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={mount.readonly}
|
||||
@@ -734,8 +696,7 @@ export const ManifestEditor = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={addMount}
|
||||
className="button-secondary"
|
||||
style={{ width: "fit-content" }}
|
||||
className="button-secondary w-fit"
|
||||
>
|
||||
<Icon name="add" size="sm" /> Add Mount
|
||||
</button>
|
||||
@@ -743,10 +704,10 @@ export const ManifestEditor = ({
|
||||
</div>
|
||||
|
||||
{/* Runtime */}
|
||||
<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
|
||||
<h4 style={{ margin: 0 }}>Runtime</h4>
|
||||
<div className="row" style={{ gap: "1rem" }}>
|
||||
<div className="form-group" style={{ flex: 1 }}>
|
||||
<div className="card-md stack stack-md">
|
||||
<h4 className="m-0">Runtime</h4>
|
||||
<div className="form-row row-md">
|
||||
<div className="form-group">
|
||||
<label>Command</label>
|
||||
<input
|
||||
type="text"
|
||||
@@ -756,7 +717,7 @@ export const ManifestEditor = ({
|
||||
className="form-input"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group" style={{ flex: 1 }}>
|
||||
<div className="form-group">
|
||||
<label>Working Directory</label>
|
||||
<input
|
||||
type="text"
|
||||
@@ -767,10 +728,8 @@ export const ManifestEditor = ({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row" style={{ gap: "1rem" }}>
|
||||
<label
|
||||
style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}
|
||||
>
|
||||
<div className="form-row row-md">
|
||||
<label className="checkbox-label min-h-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={stdinOpen}
|
||||
@@ -778,9 +737,7 @@ export const ManifestEditor = ({
|
||||
/>
|
||||
stdin_open
|
||||
</label>
|
||||
<label
|
||||
style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}
|
||||
>
|
||||
<label className="checkbox-label min-h-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={tty}
|
||||
@@ -792,15 +749,9 @@ export const ManifestEditor = ({
|
||||
</div>
|
||||
|
||||
{/* Preview */}
|
||||
<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<h4 style={{ margin: 0 }}>Live Preview</h4>
|
||||
<div className="card-md stack stack-md">
|
||||
<div className="row justify-between items-center">
|
||||
<h4 className="m-0">Live Preview</h4>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handlePreview}
|
||||
@@ -813,40 +764,16 @@ export const ManifestEditor = ({
|
||||
</div>
|
||||
{previewError && <p className="text-error">{previewError}</p>}
|
||||
{preview && (
|
||||
<div className="stack" style={{ gap: "1rem" }}>
|
||||
<div className="stack stack-md">
|
||||
<div>
|
||||
<label style={{ fontWeight: 600, fontSize: "0.875rem" }}>
|
||||
Dockerfile
|
||||
</label>
|
||||
<pre
|
||||
style={{
|
||||
background: "var(--code-bg, #1e1e1e)",
|
||||
color: "var(--code-fg, #d4d4d4)",
|
||||
padding: "1rem",
|
||||
borderRadius: "0.375rem",
|
||||
overflow: "auto",
|
||||
fontSize: "0.8125rem",
|
||||
maxHeight: "300px",
|
||||
}}
|
||||
>
|
||||
<label className="font-semibold text-sm">Dockerfile</label>
|
||||
<pre className="code-block" style={{ maxHeight: "300px" }}>
|
||||
{preview.dockerfile}
|
||||
</pre>
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ fontWeight: 600, fontSize: "0.875rem" }}>
|
||||
Compose
|
||||
</label>
|
||||
<pre
|
||||
style={{
|
||||
background: "var(--code-bg, #1e1e1e)",
|
||||
color: "var(--code-fg, #d4d4d4)",
|
||||
padding: "1rem",
|
||||
borderRadius: "0.375rem",
|
||||
overflow: "auto",
|
||||
fontSize: "0.8125rem",
|
||||
maxHeight: "200px",
|
||||
}}
|
||||
>
|
||||
<label className="font-semibold text-sm">Compose</label>
|
||||
<pre className="code-block" style={{ maxHeight: "200px" }}>
|
||||
{preview.compose}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user