Add UX enhancements: modals for env vars and instructions, global env vars, taskbar icon fix
- Fix Windows taskbar icon by loading icon.ico instead of icon.png (ICO contains multiple sizes native to Windows taskbar/title bar/alt-tab) - Add "Container must be stopped to change settings" warning banner in config panel - Move per-project Environment Variables and Claude Instructions into modal dialogs for more editing space, with buttons in the config panel to open them - Move global Claude Instructions into a modal in Settings panel - Add default global Claude instruction recommending git initialization - Add global environment variables support (full stack: Rust model, TS types, container creation with merge logic where project overrides global for same key, fingerprinting for recreation checks, and Settings UI with modal) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
80
app/src/components/projects/ClaudeInstructionsModal.tsx
Normal file
80
app/src/components/projects/ClaudeInstructionsModal.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
|
||||
interface Props {
|
||||
instructions: string;
|
||||
disabled: boolean;
|
||||
onSave: (instructions: string) => Promise<void>;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function ClaudeInstructionsModal({ instructions: initial, disabled, onSave, onClose }: Props) {
|
||||
const [instructions, setInstructions] = useState(initial);
|
||||
const overlayRef = useRef<HTMLDivElement>(null);
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
textareaRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") onClose();
|
||||
};
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||
}, [onClose]);
|
||||
|
||||
const handleOverlayClick = useCallback(
|
||||
(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (e.target === overlayRef.current) onClose();
|
||||
},
|
||||
[onClose],
|
||||
);
|
||||
|
||||
const handleBlur = async () => {
|
||||
try { await onSave(instructions); } catch (err) {
|
||||
console.error("Failed to update Claude instructions:", err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={overlayRef}
|
||||
onClick={handleOverlayClick}
|
||||
className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
|
||||
>
|
||||
<div className="bg-[var(--bg-secondary)] border border-[var(--border-color)] rounded-lg p-6 w-[40rem] shadow-xl max-h-[80vh] flex flex-col">
|
||||
<h2 className="text-lg font-semibold mb-1">Claude Instructions</h2>
|
||||
<p className="text-xs text-[var(--text-secondary)] mb-4">
|
||||
Per-project instructions for Claude Code (written to ~/.claude/CLAUDE.md in container)
|
||||
</p>
|
||||
|
||||
{disabled && (
|
||||
<div className="px-2 py-1.5 mb-3 bg-[var(--warning)]/15 border border-[var(--warning)]/30 rounded text-xs text-[var(--warning)]">
|
||||
Container must be stopped to change Claude instructions.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
value={instructions}
|
||||
onChange={(e) => setInstructions(e.target.value)}
|
||||
onBlur={handleBlur}
|
||||
placeholder="Enter instructions for Claude Code in this project's container..."
|
||||
disabled={disabled}
|
||||
rows={14}
|
||||
className="w-full flex-1 px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-sm text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent)] disabled:opacity-50 resize-y font-mono"
|
||||
/>
|
||||
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
124
app/src/components/projects/EnvVarsModal.tsx
Normal file
124
app/src/components/projects/EnvVarsModal.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import type { EnvVar } from "../../lib/types";
|
||||
|
||||
interface Props {
|
||||
envVars: EnvVar[];
|
||||
disabled: boolean;
|
||||
onSave: (vars: EnvVar[]) => Promise<void>;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function EnvVarsModal({ envVars: initial, disabled, onSave, onClose }: Props) {
|
||||
const [vars, setVars] = useState<EnvVar[]>(initial);
|
||||
const overlayRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") onClose();
|
||||
};
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||
}, [onClose]);
|
||||
|
||||
const handleOverlayClick = useCallback(
|
||||
(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (e.target === overlayRef.current) onClose();
|
||||
},
|
||||
[onClose],
|
||||
);
|
||||
|
||||
const updateVar = (index: number, field: keyof EnvVar, value: string) => {
|
||||
const updated = [...vars];
|
||||
updated[index] = { ...updated[index], [field]: value };
|
||||
setVars(updated);
|
||||
};
|
||||
|
||||
const removeVar = async (index: number) => {
|
||||
const updated = vars.filter((_, i) => i !== index);
|
||||
setVars(updated);
|
||||
try { await onSave(updated); } catch (err) {
|
||||
console.error("Failed to remove environment variable:", err);
|
||||
}
|
||||
};
|
||||
|
||||
const addVar = async () => {
|
||||
const updated = [...vars, { key: "", value: "" }];
|
||||
setVars(updated);
|
||||
try { await onSave(updated); } catch (err) {
|
||||
console.error("Failed to add environment variable:", err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = async () => {
|
||||
try { await onSave(vars); } catch (err) {
|
||||
console.error("Failed to update environment variables:", err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={overlayRef}
|
||||
onClick={handleOverlayClick}
|
||||
className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
|
||||
>
|
||||
<div className="bg-[var(--bg-secondary)] border border-[var(--border-color)] rounded-lg p-6 w-[36rem] shadow-xl max-h-[80vh] overflow-y-auto">
|
||||
<h2 className="text-lg font-semibold mb-4">Environment Variables</h2>
|
||||
|
||||
{disabled && (
|
||||
<div className="px-2 py-1.5 mb-3 bg-[var(--warning)]/15 border border-[var(--warning)]/30 rounded text-xs text-[var(--warning)]">
|
||||
Container must be stopped to change environment variables.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2 mb-4">
|
||||
{vars.length === 0 && (
|
||||
<p className="text-xs text-[var(--text-secondary)]">No environment variables configured.</p>
|
||||
)}
|
||||
{vars.map((ev, i) => (
|
||||
<div key={i} className="flex gap-2 items-center">
|
||||
<input
|
||||
value={ev.key}
|
||||
onChange={(e) => updateVar(i, "key", e.target.value)}
|
||||
onBlur={handleBlur}
|
||||
placeholder="KEY"
|
||||
disabled={disabled}
|
||||
className="w-2/5 px-2 py-1.5 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-sm text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent)] disabled:opacity-50 font-mono"
|
||||
/>
|
||||
<input
|
||||
value={ev.value}
|
||||
onChange={(e) => updateVar(i, "value", e.target.value)}
|
||||
onBlur={handleBlur}
|
||||
placeholder="value"
|
||||
disabled={disabled}
|
||||
className="flex-1 px-2 py-1.5 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-sm text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent)] disabled:opacity-50 font-mono"
|
||||
/>
|
||||
<button
|
||||
onClick={() => removeVar(i)}
|
||||
disabled={disabled}
|
||||
className="px-2 py-1.5 text-sm text-[var(--error)] hover:bg-[var(--bg-primary)] rounded disabled:opacity-50 transition-colors"
|
||||
>
|
||||
x
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center">
|
||||
<button
|
||||
onClick={addVar}
|
||||
disabled={disabled}
|
||||
className="text-sm text-[var(--accent)] hover:text-[var(--accent-hover)] disabled:opacity-50 transition-colors"
|
||||
>
|
||||
+ Add variable
|
||||
</button>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import type { Project, ProjectPath, AuthMode, BedrockConfig, BedrockAuthMethod }
|
||||
import { useProjects } from "../../hooks/useProjects";
|
||||
import { useTerminal } from "../../hooks/useTerminal";
|
||||
import { useAppState } from "../../store/appState";
|
||||
import EnvVarsModal from "./EnvVarsModal";
|
||||
import ClaudeInstructionsModal from "./ClaudeInstructionsModal";
|
||||
|
||||
interface Props {
|
||||
project: Project;
|
||||
@@ -17,6 +19,8 @@ export default function ProjectCard({ project }: Props) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [showConfig, setShowConfig] = useState(false);
|
||||
const [showEnvVarsModal, setShowEnvVarsModal] = useState(false);
|
||||
const [showClaudeInstructionsModal, setShowClaudeInstructionsModal] = useState(false);
|
||||
const isSelected = selectedProjectId === project.id;
|
||||
const isStopped = project.status === "stopped" || project.status === "error";
|
||||
|
||||
@@ -165,22 +169,6 @@ export default function ProjectCard({ project }: Props) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleClaudeInstructionsBlur = async () => {
|
||||
try {
|
||||
await update({ ...project, claude_instructions: claudeInstructions || null });
|
||||
} catch (err) {
|
||||
console.error("Failed to update Claude instructions:", err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEnvVarBlur = async () => {
|
||||
try {
|
||||
await update({ ...project, custom_env_vars: envVars });
|
||||
} catch (err) {
|
||||
console.error("Failed to update environment variables:", err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBedrockRegionBlur = async () => {
|
||||
try {
|
||||
const current = project.bedrock_config ?? defaultBedrockConfig;
|
||||
@@ -358,6 +346,11 @@ export default function ProjectCard({ project }: Props) {
|
||||
{/* Config panel */}
|
||||
{showConfig && (
|
||||
<div className="space-y-2 pt-1 border-t border-[var(--border-color)] min-w-0 overflow-hidden" onClick={(e) => e.stopPropagation()}>
|
||||
{!isStopped && (
|
||||
<div className="px-2 py-1.5 bg-[var(--warning)]/15 border border-[var(--warning)]/30 rounded text-xs text-[var(--warning)]">
|
||||
Container must be stopped to change settings.
|
||||
</div>
|
||||
)}
|
||||
{/* Folder paths */}
|
||||
<div>
|
||||
<label className="block text-xs text-[var(--text-secondary)] mb-0.5">Folders</label>
|
||||
@@ -530,76 +523,29 @@ export default function ProjectCard({ project }: Props) {
|
||||
</div>
|
||||
|
||||
{/* Environment Variables */}
|
||||
<div>
|
||||
<label className="block text-xs text-[var(--text-secondary)] mb-0.5">Environment Variables</label>
|
||||
{envVars.map((ev, i) => (
|
||||
<div key={i} className="flex gap-1 mb-1">
|
||||
<input
|
||||
value={ev.key}
|
||||
onChange={(e) => {
|
||||
const vars = [...envVars];
|
||||
vars[i] = { ...vars[i], key: e.target.value };
|
||||
setEnvVars(vars);
|
||||
}}
|
||||
onBlur={handleEnvVarBlur}
|
||||
placeholder="KEY"
|
||||
disabled={!isStopped}
|
||||
className="w-1/3 px-2 py-1 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-xs text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent)] disabled:opacity-50 font-mono"
|
||||
/>
|
||||
<input
|
||||
value={ev.value}
|
||||
onChange={(e) => {
|
||||
const vars = [...envVars];
|
||||
vars[i] = { ...vars[i], value: e.target.value };
|
||||
setEnvVars(vars);
|
||||
}}
|
||||
onBlur={handleEnvVarBlur}
|
||||
placeholder="value"
|
||||
disabled={!isStopped}
|
||||
className="flex-1 px-2 py-1 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-xs text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent)] disabled:opacity-50 font-mono"
|
||||
/>
|
||||
<button
|
||||
onClick={async () => {
|
||||
const vars = envVars.filter((_, j) => j !== i);
|
||||
setEnvVars(vars);
|
||||
try { await update({ ...project, custom_env_vars: vars }); } catch (err) {
|
||||
console.error("Failed to remove environment variable:", err);
|
||||
}
|
||||
}}
|
||||
disabled={!isStopped}
|
||||
className="px-1.5 py-1 text-xs text-[var(--error)] hover:bg-[var(--bg-primary)] rounded disabled:opacity-50 transition-colors"
|
||||
>
|
||||
x
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="text-xs text-[var(--text-secondary)]">
|
||||
Environment Variables{envVars.length > 0 && ` (${envVars.length})`}
|
||||
</label>
|
||||
<button
|
||||
onClick={async () => {
|
||||
const vars = [...envVars, { key: "", value: "" }];
|
||||
setEnvVars(vars);
|
||||
try { await update({ ...project, custom_env_vars: vars }); } catch (err) {
|
||||
console.error("Failed to add environment variable:", err);
|
||||
}
|
||||
}}
|
||||
disabled={!isStopped}
|
||||
className="text-xs text-[var(--accent)] hover:text-[var(--accent-hover)] disabled:opacity-50 transition-colors"
|
||||
onClick={() => setShowEnvVarsModal(true)}
|
||||
className="text-xs px-2 py-0.5 text-[var(--accent)] hover:text-[var(--accent-hover)] hover:bg-[var(--bg-primary)] rounded transition-colors"
|
||||
>
|
||||
+ Add variable
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Claude Instructions */}
|
||||
<div>
|
||||
<label className="block text-xs text-[var(--text-secondary)] mb-0.5">Claude Instructions</label>
|
||||
<textarea
|
||||
value={claudeInstructions}
|
||||
onChange={(e) => setClaudeInstructions(e.target.value)}
|
||||
onBlur={handleClaudeInstructionsBlur}
|
||||
placeholder="Per-project instructions for Claude Code (written to ~/.claude/CLAUDE.md in container)"
|
||||
disabled={!isStopped}
|
||||
rows={3}
|
||||
className="w-full px-2 py-1 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-xs text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent)] disabled:opacity-50 resize-y font-mono"
|
||||
/>
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="text-xs text-[var(--text-secondary)]">
|
||||
Claude Instructions{claudeInstructions ? " (set)" : ""}
|
||||
</label>
|
||||
<button
|
||||
onClick={() => setShowClaudeInstructionsModal(true)}
|
||||
className="text-xs px-2 py-0.5 text-[var(--accent)] hover:text-[var(--accent-hover)] hover:bg-[var(--bg-primary)] rounded transition-colors"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Bedrock config */}
|
||||
@@ -734,6 +680,30 @@ export default function ProjectCard({ project }: Props) {
|
||||
{error && (
|
||||
<div className="text-xs text-[var(--error)] mt-1 ml-4">{error}</div>
|
||||
)}
|
||||
|
||||
{showEnvVarsModal && (
|
||||
<EnvVarsModal
|
||||
envVars={envVars}
|
||||
disabled={!isStopped}
|
||||
onSave={async (vars) => {
|
||||
setEnvVars(vars);
|
||||
await update({ ...project, custom_env_vars: vars });
|
||||
}}
|
||||
onClose={() => setShowEnvVarsModal(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showClaudeInstructionsModal && (
|
||||
<ClaudeInstructionsModal
|
||||
instructions={claudeInstructions}
|
||||
disabled={!isStopped}
|
||||
onSave={async (instructions) => {
|
||||
setClaudeInstructions(instructions);
|
||||
await update({ ...project, claude_instructions: instructions || null });
|
||||
}}
|
||||
onClose={() => setShowClaudeInstructionsModal(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,22 +4,24 @@ import DockerSettings from "./DockerSettings";
|
||||
import AwsSettings from "./AwsSettings";
|
||||
import { useSettings } from "../../hooks/useSettings";
|
||||
import { useUpdates } from "../../hooks/useUpdates";
|
||||
import ClaudeInstructionsModal from "../projects/ClaudeInstructionsModal";
|
||||
import EnvVarsModal from "../projects/EnvVarsModal";
|
||||
import type { EnvVar } from "../../lib/types";
|
||||
|
||||
export default function SettingsPanel() {
|
||||
const { appSettings, saveSettings } = useSettings();
|
||||
const { appVersion, checkForUpdates } = useUpdates();
|
||||
const [globalInstructions, setGlobalInstructions] = useState(appSettings?.global_claude_instructions ?? "");
|
||||
const [globalEnvVars, setGlobalEnvVars] = useState<EnvVar[]>(appSettings?.global_custom_env_vars ?? []);
|
||||
const [checkingUpdates, setCheckingUpdates] = useState(false);
|
||||
const [showInstructionsModal, setShowInstructionsModal] = useState(false);
|
||||
const [showEnvVarsModal, setShowEnvVarsModal] = useState(false);
|
||||
|
||||
// Sync local state when appSettings change
|
||||
useEffect(() => {
|
||||
setGlobalInstructions(appSettings?.global_claude_instructions ?? "");
|
||||
}, [appSettings?.global_claude_instructions]);
|
||||
|
||||
const handleInstructionsBlur = async () => {
|
||||
if (!appSettings) return;
|
||||
await saveSettings({ ...appSettings, global_claude_instructions: globalInstructions || null });
|
||||
};
|
||||
setGlobalEnvVars(appSettings?.global_custom_env_vars ?? []);
|
||||
}, [appSettings?.global_claude_instructions, appSettings?.global_custom_env_vars]);
|
||||
|
||||
const handleCheckNow = async () => {
|
||||
setCheckingUpdates(true);
|
||||
@@ -43,19 +45,43 @@ export default function SettingsPanel() {
|
||||
<ApiKeyInput />
|
||||
<DockerSettings />
|
||||
<AwsSettings />
|
||||
|
||||
{/* Global Claude Instructions */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Claude Instructions</label>
|
||||
<label className="block text-sm font-medium mb-1">Claude Instructions</label>
|
||||
<p className="text-xs text-[var(--text-secondary)] mb-1.5">
|
||||
Global instructions applied to all projects (written to ~/.claude/CLAUDE.md in containers)
|
||||
</p>
|
||||
<textarea
|
||||
value={globalInstructions}
|
||||
onChange={(e) => setGlobalInstructions(e.target.value)}
|
||||
onBlur={handleInstructionsBlur}
|
||||
placeholder="Instructions for Claude Code in all project containers..."
|
||||
rows={4}
|
||||
className="w-full px-2 py-1.5 text-xs bg-[var(--bg-primary)] border border-[var(--border-color)] rounded focus:outline-none focus:border-[var(--accent)] resize-y font-mono"
|
||||
/>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-[var(--text-secondary)]">
|
||||
{globalInstructions ? "Configured" : "Not set"}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setShowInstructionsModal(true)}
|
||||
className="text-xs px-2 py-0.5 text-[var(--accent)] hover:text-[var(--accent-hover)] hover:bg-[var(--bg-primary)] rounded transition-colors"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Global Environment Variables */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Global Environment Variables</label>
|
||||
<p className="text-xs text-[var(--text-secondary)] mb-1.5">
|
||||
Applied to all project containers. Per-project variables override global ones with the same key.
|
||||
</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-[var(--text-secondary)]">
|
||||
{globalEnvVars.length > 0 ? `${globalEnvVars.length} variable${globalEnvVars.length === 1 ? "" : "s"}` : "None"}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setShowEnvVarsModal(true)}
|
||||
className="text-xs px-2 py-0.5 text-[var(--accent)] hover:text-[var(--accent-hover)] hover:bg-[var(--bg-primary)] rounded transition-colors"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Updates section */}
|
||||
@@ -89,6 +115,34 @@ export default function SettingsPanel() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showInstructionsModal && (
|
||||
<ClaudeInstructionsModal
|
||||
instructions={globalInstructions}
|
||||
disabled={false}
|
||||
onSave={async (instructions) => {
|
||||
setGlobalInstructions(instructions);
|
||||
if (appSettings) {
|
||||
await saveSettings({ ...appSettings, global_claude_instructions: instructions || null });
|
||||
}
|
||||
}}
|
||||
onClose={() => setShowInstructionsModal(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showEnvVarsModal && (
|
||||
<EnvVarsModal
|
||||
envVars={globalEnvVars}
|
||||
disabled={false}
|
||||
onSave={async (vars) => {
|
||||
setGlobalEnvVars(vars);
|
||||
if (appSettings) {
|
||||
await saveSettings({ ...appSettings, global_custom_env_vars: vars });
|
||||
}
|
||||
}}
|
||||
onClose={() => setShowEnvVarsModal(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ export interface AppSettings {
|
||||
custom_image_name: string | null;
|
||||
global_aws: GlobalAwsSettings;
|
||||
global_claude_instructions: string | null;
|
||||
global_custom_env_vars: EnvVar[];
|
||||
auto_check_updates: boolean;
|
||||
dismissed_update_version: string | null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user