diff --git a/frontend/src/components/SettingsPanel.jsx b/frontend/src/components/SettingsPanel.jsx
index 8b60e7f..1e29299 100644
--- a/frontend/src/components/SettingsPanel.jsx
+++ b/frontend/src/components/SettingsPanel.jsx
@@ -23,7 +23,6 @@ export default function SettingsPanel({ settings, onSave, onClose }) {
minimal_mode: settings?.minimal_mode ?? false,
});
- // SOLVES (#581): Persistent Collapse State Hooks
const [isExpanded, setIsExpanded] = useState(() => {
const savedState = localStorage.getItem("localmind_settings_expanded");
return savedState !== null ? JSON.parse(savedState) : true;
@@ -51,6 +50,19 @@ export default function SettingsPanel({ settings, onSave, onClose }) {
localStorage.setItem("localmind_settings_drafts", JSON.stringify(drafts));
}, [drafts]);
+ // FIXED (#578): Global keyboard listener listening for Escape key panel dismiss signals
+ useEffect(() => {
+ function handleKeyDown(e) {
+ if (e.key === "Escape") {
+ onClose();
+ }
+ }
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [onClose]);
+
+ const isEmptyState = !settings || Object.keys(settings).length === 0;
+
function set(key, val) {
setForm(p => ({ ...p, [key]: val }));
if (errors[key]) {
@@ -105,28 +117,30 @@ export default function SettingsPanel({ settings, onSave, onClose }) {
};
return (
- /* FIXED (#580): Changed top wrapper from a generic
to a semantic section landmark region */
- {/* FIXED (#580): Transformed paragraph into a proper heading paired to the section landmark label */}
Settings
-
- {/* SOLVES (#581): Persistent Collapse Button Controller */}
-
+
{errors.global && (
@@ -135,19 +149,17 @@ export default function SettingsPanel({ settings, onSave, onClose }) {
)}
- {/* SOLVES (#581): Controlled expanded layout rendering window wrapper */}
{isExpanded && (
<>
- {/* FIXED (#580): Provided unique htmlId properties to bind labels explicitly to inputs */}
-
-
+
- set("default_language", e.target.value)} className={`sel ${errors.default_language ? "border-red-500" : ""}`}>
+ set("default_language", e.target.value)} className="sel focus:ring-2 focus:ring-purple-500">
{LANGUAGES.map(l => )}
@@ -155,29 +167,29 @@ export default function SettingsPanel({ settings, onSave, onClose }) {
set("temperature", parseFloat(e.target.value))}
- className="w-full accent-purple-500" />
+ className="w-full accent-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500 rounded" />
set("rag_top_k", parseInt(e.target.value))}
- className="w-full accent-purple-500" />
+ className="w-full accent-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500 rounded" />
set("rag_chunk_overlap", parseInt(e.target.value))}
- className="w-full accent-purple-500" />
+ className="w-full accent-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500 rounded" />
set("max_history_turns", parseInt(e.target.value))}
- className="w-full accent-purple-500" />
+ className="w-full accent-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500 rounded" />
- set("theme", e.target.value)} className={`sel ${errors.theme ? "border-red-500" : ""}`}>
+ set("theme", e.target.value)} className="sel focus:ring-2 focus:ring-purple-500">
@@ -193,7 +205,7 @@ export default function SettingsPanel({ settings, onSave, onClose }) {
type="checkbox"
checked={form.minimal_mode}
onChange={e => set("minimal_mode", e.target.checked)}
- className="accent-purple-500"
+ className="accent-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500 rounded"
/>
Low-bandwidth mode
@@ -209,13 +221,13 @@ export default function SettingsPanel({ settings, onSave, onClose }) {
value={newDraft}
onChange={e => setNewDraft(e.target.value)}
placeholder="Type a canned prompt snippet or scratchpad note..."
- className="flex-1 bg-gray-800 text-gray-200 border border-gray-700 rounded-lg px-3 py-1 text-xs outline-none focus:border-purple-500 placeholder-gray-600"
+ className="flex-1 bg-gray-800 text-gray-200 border border-gray-700 rounded-lg px-3 py-1 text-xs outline-none focus:border-purple-500 placeholder-gray-600 focus:ring-2 focus:ring-purple-500"
onKeyDown={e => e.key === "Enter" && handleAddDraft()}
/>
@@ -231,7 +243,7 @@ export default function SettingsPanel({ settings, onSave, onClose }) {
@@ -247,14 +259,14 @@ export default function SettingsPanel({ settings, onSave, onClose }) {
type="button"
onClick={handleSave}
disabled={isSaving}
- className="text-xs bg-purple-700 hover:bg-purple-600 disabled:bg-gray-800 text-white px-4 py-1.5 rounded-lg transition font-medium shadow-md"
+ className="text-xs bg-purple-700 hover:bg-purple-600 disabled:bg-gray-800 text-white px-4 py-1.5 rounded-lg transition font-medium shadow-md focus:outline-none focus:ring-2 focus:ring-purple-500"
>
{isSaving ? "Saving..." : "Save Settings"}
@@ -263,7 +275,7 @@ export default function SettingsPanel({ settings, onSave, onClose }) {