5 Commits

Author SHA1 Message Date
the_og e6b60e94e7 Merge pull request 'release: v1.3.0 — named server sets, project config discovery, schema lint, UX polish' (#60) from release/v1.3.0 into main
CI / Lint (ruff) (push) Successful in 7s
CI / Tests (py3.12 / windows-latest) (push) Successful in 20s
CI / Tests (py3.12 / ubuntu-latest) (push) Successful in 9s
CI / Tests (py3.13 / ubuntu-latest) (push) Successful in 9s
CI / Tests (py3.10 / ubuntu-latest) (push) Successful in 9s
Build & Release / Build (Windows) (push) Successful in 53s
Build & Release / Build (Linux) (push) Successful in 1m0s
Build & Release / Build (macOS) (push) Successful in 1m58s
Build & Release / Publish Release (push) Successful in 10s
2026-07-12 14:03:50 -04:00
Cowork Supervisor 4afe21666d release: bump version to 1.3.0
CI / Tests (py3.12 / windows-latest) (pull_request) Successful in 21s
CI / Lint (ruff) (pull_request) Successful in 7s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 10s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 9s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 9s
Named server sets (#52), project .mcp.json discovery (#53), schema
lint (#54), Ctrl+S + enable/disable-all (#55).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 14:02:38 -04:00
the_og 06e74d4d2c Merge pull request 'feat: Ctrl+S save shortcut + enable/disable-all actions (#55)' (#59) from feat/55-ux-polish into main
CI / Lint (ruff) (push) Successful in 6s
CI / Tests (py3.10 / ubuntu-latest) (push) Successful in 10s
CI / Tests (py3.12 / windows-latest) (push) Successful in 20s
CI / Tests (py3.12 / ubuntu-latest) (push) Successful in 9s
CI / Tests (py3.13 / ubuntu-latest) (push) Successful in 9s
2026-07-12 14:02:23 -04:00
Cowork Supervisor f92b851127 Merge remote-tracking branch 'origin/main' into feat/55-ux-polish
CI / Lint (ruff) (pull_request) Successful in 7s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 9s
CI / Tests (py3.12 / windows-latest) (pull_request) Successful in 20s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 9s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 9s
2026-07-12 14:00:56 -04:00
Cowork Supervisor 31ef4a0e85 feat: Ctrl+S save shortcut + enable/disable-all actions (#55)
CI / Tests (py3.12 / windows-latest) (pull_request) Successful in 20s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 8s
CI / Lint (ruff) (pull_request) Successful in 7s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 9s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 8s
Closes #55

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 13:55:19 -04:00
4 changed files with 42 additions and 5 deletions
+38 -1
View File
@@ -23,6 +23,7 @@ from PySide6.QtGui import (
QDesktopServices, QDesktopServices,
QGuiApplication, QGuiApplication,
QIcon, QIcon,
QKeySequence,
QPainter, QPainter,
QPixmap, QPixmap,
) )
@@ -1687,11 +1688,21 @@ class MainWindow(QMainWindow):
head.setObjectName("h1") head.setObjectName("h1")
v.addWidget(head) v.addWidget(head)
search_row = QHBoxLayout()
self.search_box = QLineEdit() self.search_box = QLineEdit()
self.search_box.setPlaceholderText("Search servers by name, command, or url…") self.search_box.setPlaceholderText("Search servers by name, command, or url…")
self.search_box.setClearButtonEnabled(True) self.search_box.setClearButtonEnabled(True)
self.search_box.textChanged.connect(self._on_search_changed) self.search_box.textChanged.connect(self._on_search_changed)
v.addWidget(self.search_box) search_row.addWidget(self.search_box, 1)
self.enable_all_btn = QPushButton("All on")
self.enable_all_btn.setToolTip("Enable every server")
self.enable_all_btn.clicked.connect(lambda: self._set_all_enabled(True))
search_row.addWidget(self.enable_all_btn)
self.disable_all_btn = QPushButton("All off")
self.disable_all_btn.setToolTip("Disable every server")
self.disable_all_btn.clicked.connect(lambda: self._set_all_enabled(False))
search_row.addWidget(self.disable_all_btn)
v.addLayout(search_row)
# Named server sets (issue #52): apply a saved Active/Disabled split # Named server sets (issue #52): apply a saved Active/Disabled split
# in one click. Sets live in the config file under _bccServerSets. # in one click. Sets live in the config file under _bccServerSets.
@@ -1799,6 +1810,12 @@ class MainWindow(QMainWindow):
undo_action.setShortcut("Ctrl+Z") undo_action.setShortcut("Ctrl+Z")
undo_action.triggered.connect(self._undo) undo_action.triggered.connect(self._undo)
self.addAction(undo_action) self.addAction(undo_action)
# Ctrl+S / Cmd+S shortcut — routed through a guard so it respects
# the same dirty/validation gating as the Save button.
save_action = QAction(self)
save_action.setShortcut(QKeySequence.StandardKey.Save)
save_action.triggered.connect(self._save_shortcut)
self.addAction(save_action)
bar.addStretch() bar.addStretch()
self.validation_lbl = QLabel("") self.validation_lbl = QLabel("")
bar.addWidget(self.validation_lbl) bar.addWidget(self.validation_lbl)
@@ -1825,6 +1842,19 @@ class MainWindow(QMainWindow):
self._mark_dirty() self._mark_dirty()
self.status.setText("Undone.") self.status.setText("Undone.")
def _set_all_enabled(self, enabled: bool):
"""Flip every server's enabled flag in one step (one undo snapshot)."""
if not self.servers or all(s.enabled == enabled for s in self.servers):
return # nothing to change
cur = self._current_index()
self._push_undo()
for s in self.servers:
s.enabled = enabled
sel = cur if 0 <= cur < len(self.servers) else None
self._refresh_tables(select_index=sel)
self._mark_dirty()
self.status.setText("All servers enabled." if enabled else "All servers disabled.")
# --- profiles -------------------------------------------------------- # # --- profiles -------------------------------------------------------- #
def reload_profiles(self): def reload_profiles(self):
discovered = core.discover_profiles() discovered = core.discover_profiles()
@@ -2412,6 +2442,13 @@ class MainWindow(QMainWindow):
self.save_btn.setEnabled(self.dirty) self.save_btn.setEnabled(self.dirty)
return True return True
def _save_shortcut(self):
"""Ctrl+S / Cmd+S handler — only fires when the Save button itself
would accept a click, so the shortcut can't bypass validation/dirty
gating."""
if self.save_btn.isEnabled():
self.save()
def save(self): def save(self):
if not self.current_profile: if not self.current_profile:
return return
+2 -2
View File
@@ -78,8 +78,8 @@ if sys.platform == "darwin":
info_plist={ info_plist={
"CFBundleName": "Better Claude Config", "CFBundleName": "Better Claude Config",
"CFBundleDisplayName": "Better Claude Config", "CFBundleDisplayName": "Better Claude Config",
"CFBundleShortVersionString": "1.2.1", "CFBundleShortVersionString": "1.3.0",
"CFBundleVersion": "1.2.1", "CFBundleVersion": "1.3.0",
"NSHighResolutionCapable": True, "NSHighResolutionCapable": True,
"NSRequiresAquaSystemAppearance": False, # supports dark mode "NSRequiresAquaSystemAppearance": False, # supports dark mode
"LSMinimumSystemVersion": "11.0", "LSMinimumSystemVersion": "11.0",
+1 -1
View File
@@ -65,7 +65,7 @@ KNOWN_FIELDS = {"command", "args", "env", "url", "type", "headers"}
# binary. All network I/O here is fail-quiet (returns None on any problem) # binary. All network I/O here is fail-quiet (returns None on any problem)
# so it's safe to run unattended, off the UI thread, at startup. # so it's safe to run unattended, off the UI thread, at startup.
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
__version__ = "1.2.1" __version__ = "1.3.0"
REPO_URL = "https://git.avezzano.io/the_og/better-claude-config" REPO_URL = "https://git.avezzano.io/the_og/better-claude-config"
ISSUES_URL = f"{REPO_URL}/issues" ISSUES_URL = f"{REPO_URL}/issues"
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "better-claude-config" name = "better-claude-config"
version = "1.2.1" version = "1.3.0"
description = "Cross-platform GUI for editing the mcpServers block of Claude Desktop and Claude Code configs" description = "Cross-platform GUI for editing the mcpServers block of Claude Desktop and Claude Code configs"
readme = "README.md" readme = "README.md"
license = { file = "LICENSE" } license = { file = "LICENSE" }