Skip to content

Commit 2be2688

Browse files
feat: add Addons tab to launcher GUI
- Replaced right sidebar with ttk.Notebook (Profiles + Addons tabs) - Scans addons/ directory for addon.json manifests - Displays addon name, version in list - Shows details (description, id, author) on selection - Links to Pro web UI Extensions tab for management
1 parent 6f71b26 commit 2be2688

1 file changed

Lines changed: 110 additions & 41 deletions

File tree

droidgrid_v2/launcher.py

Lines changed: 110 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ def _build_ui(self):
399399

400400
self._build_cameras_section(left)
401401
self._build_session_section(left)
402-
self._build_profiles_panel(right)
402+
self._build_right_panel(right)
403403

404404
# ── status bar ───────────────────────────────────────────────────────
405405
tk.Frame(root, bg=BORDER, height=1).pack(fill=tk.X)
@@ -594,17 +594,20 @@ def _browse_dir(self, var: tk.StringVar):
594594

595595
# ── profiles panel ───────────────────────────────────────────────────────
596596

597-
def _build_profiles_panel(self, parent):
597+
def _build_right_panel(self, parent):
598598
tk.Frame(parent, bg=BORDER, width=1).pack(side=tk.LEFT, fill=tk.Y)
599599

600-
inner = tk.Frame(parent, bg=BG2, padx=14)
601-
inner.pack(fill=tk.BOTH, expand=True)
600+
notebook = ttk.Notebook(parent)
601+
notebook.pack(fill=tk.BOTH, expand=True)
602602

603-
tk.Label(inner, text="📁 Profiles",
604-
bg=BG2, fg=FG, font=FONT_HEAD).pack(anchor=tk.W, pady=(16, 10))
603+
# ── Tab 1: Profiles ──────────────────────────────────────────────────
604+
tab_profiles = tk.Frame(notebook, bg=BG2, padx=14)
605+
notebook.add(tab_profiles, text=" Profiles ")
605606

606-
# profile listbox
607-
lb_frame = tk.Frame(inner, bg=BG2)
607+
tk.Label(tab_profiles, text="📁 Profiles",
608+
bg=BG2, fg=FG, font=FONT_HEAD).pack(anchor=tk.W, pady=(12, 8))
609+
610+
lb_frame = tk.Frame(tab_profiles, bg=BG2)
608611
lb_frame.pack(fill=tk.BOTH, expand=True)
609612

610613
sb = ttk.Scrollbar(lb_frame, orient=tk.VERTICAL)
@@ -614,8 +617,7 @@ def _build_profiles_panel(self, parent):
614617
relief=tk.FLAT, font=FONT_BODY,
615618
activestyle=tk.NONE,
616619
yscrollcommand=sb.set,
617-
highlightthickness=1,
618-
highlightcolor=BORDER,
620+
highlightthickness=1, highlightcolor=BORDER,
619621
highlightbackground=BORDER,
620622
)
621623
sb.config(command=self._profile_lb.yview)
@@ -624,58 +626,125 @@ def _build_profiles_panel(self, parent):
624626
self._profile_lb.bind("<<ListboxSelect>>", self._on_profile_select)
625627
self._profile_lb.bind("<Double-Button-1>", lambda e: self._load_profile())
626628

627-
# profile buttons
628629
btn_cfg = {"bg": BG3, "fg": FG, "activebackground": BORDER,
629630
"activeforeground": FG, "relief": tk.FLAT,
630631
"cursor": "hand2", "font": FONT_SMALL}
631-
btn_row1 = tk.Frame(inner, bg=BG2)
632-
btn_row1.pack(fill=tk.X, pady=(8, 0))
632+
btn_row1 = tk.Frame(tab_profiles, bg=BG2)
633+
btn_row1.pack(fill=tk.X, pady=(6, 0))
633634
tk.Button(btn_row1, text="Load", **btn_cfg,
634635
command=self._load_profile).pack(side=tk.LEFT, fill=tk.X,
635-
expand=True, padx=(0, 2), ipady=4)
636+
expand=True, padx=(0, 2), ipady=3)
636637
tk.Button(btn_row1, text="Save As…", **btn_cfg,
637638
command=self._save_profile).pack(side=tk.LEFT, fill=tk.X,
638-
expand=True, padx=(2, 0), ipady=4)
639-
640-
btn_row2 = tk.Frame(inner, bg=BG2)
641-
btn_row2.pack(fill=tk.X, pady=(4, 0))
639+
expand=True, padx=(2, 0), ipady=3)
640+
btn_row2 = tk.Frame(tab_profiles, bg=BG2)
641+
btn_row2.pack(fill=tk.X, pady=(3, 0))
642642
tk.Button(btn_row2, text="Rename…", **btn_cfg,
643643
command=self._rename_profile).pack(side=tk.LEFT, fill=tk.X,
644-
expand=True, padx=(0, 2), ipady=4)
644+
expand=True, padx=(0, 2), ipady=3)
645645
tk.Button(btn_row2, text="Delete", bg=BG3, fg=DANGER,
646646
activebackground=BORDER, activeforeground=DANGER,
647647
relief=tk.FLAT, cursor="hand2", font=FONT_SMALL,
648648
command=self._delete_profile).pack(side=tk.LEFT, fill=tk.X,
649-
expand=True, padx=(2, 0), ipady=4)
650-
651-
# quick-save current button
652-
tk.Button(inner, text="💾 Quick Save Current Profile",
653-
bg=BG3, fg=ACCENT,
649+
expand=True, padx=(2, 0), ipady=3)
650+
tk.Button(tab_profiles, text="💾 Quick Save", bg=BG3, fg=ACCENT,
654651
activebackground=BORDER, activeforeground=ACCENT,
655652
relief=tk.FLAT, cursor="hand2", font=FONT_SMALL,
656-
command=self._quick_save
657-
).pack(fill=tk.X, pady=(8, 0), ipady=5)
653+
command=self._quick_save).pack(fill=tk.X, pady=(6, 0), ipady=4)
658654

659-
# separator
660-
tk.Frame(inner, bg=BORDER, height=1).pack(fill=tk.X, pady=14)
655+
self._refresh_profile_list()
656+
657+
# ── Tab 2: Addons ────────────────────────────────────────────────────
658+
tab_addons = tk.Frame(notebook, bg=BG2, padx=14)
659+
notebook.add(tab_addons, text=" Addons ")
660+
self._build_addons_panel(tab_addons)
661+
662+
def _build_addons_panel(self, parent):
663+
tk.Label(parent, text="🧩 Addons",
664+
bg=BG2, fg=FG, font=FONT_HEAD).pack(anchor=tk.W, pady=(12, 8))
665+
666+
# scan addons directory
667+
addons_root = Path(__file__).resolve().parent.parent / "addons"
668+
found = []
669+
if addons_root.exists():
670+
for d in sorted(addons_root.iterdir()):
671+
if d.is_dir():
672+
manifest_path = d / "addon.json"
673+
if manifest_path.exists():
674+
try:
675+
manifest = json.loads(manifest_path.read_text())
676+
found.append(manifest)
677+
except Exception:
678+
found.append({"id": d.name, "name": d.name,
679+
"version": "?", "description": "Invalid manifest"})
680+
681+
if not found:
682+
tk.Label(parent, text="No addons found", bg=BG2, fg=FG3,
683+
font=FONT_SMALL).pack(anchor=tk.W, pady=10)
684+
tk.Label(parent, text="Add addon directories to:", bg=BG2,
685+
fg=FG3, font=FONT_SMALL).pack(anchor=tk.W)
686+
tk.Label(parent, text=str(addons_root), bg=BG2,
687+
fg=ACCENT, font=FONT_SMALL).pack(anchor=tk.W)
688+
return
689+
690+
# addon list
691+
lb_frame = tk.Frame(parent, bg=BG2)
692+
lb_frame.pack(fill=tk.BOTH, expand=True)
693+
694+
self._addon_lb = tk.Listbox(
695+
lb_frame, bg=BG3, fg=FG,
696+
selectbackground=ACCENT, selectforeground="#fff",
697+
relief=tk.FLAT, font=FONT_BODY,
698+
activestyle=tk.NONE,
699+
highlightthickness=1, highlightcolor=BORDER,
700+
highlightbackground=BORDER,
701+
)
702+
self._addon_lb.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
703+
704+
self._addon_data = found
705+
for a in found:
706+
desc = a.get("description", "")[:40]
707+
label = f" {a['name']} v{a.get('version','?')}"
708+
self._addon_lb.insert(tk.END, label)
709+
710+
# addon detail area
711+
self._addon_detail_var = tk.StringVar(value="Select an addon for details")
712+
tk.Label(parent, textvariable=self._addon_detail_var,
713+
bg=BG2, fg=FG2, font=FONT_SMALL,
714+
wraplength=200, justify=tk.LEFT, anchor=tk.W).pack(
715+
anchor=tk.W, fill=tk.X, pady=(6, 0))
716+
717+
self._addon_lb.bind("<<ListboxSelect>>", self._on_addon_select)
718+
719+
# footer
720+
tk.Frame(parent, bg=BORDER, height=1).pack(fill=tk.X, pady=8)
661721

662-
# help text
663722
help_lines = [
664-
"Quick tips:",
665-
"• Double-click profile to load",
666-
"• Press Test to check cameras",
667-
"• Press ▶ Launch to start",
668-
"",
669-
"Profiles saved to:",
670-
"~/.droidgrid/profiles.json",
723+
"Addons extend DroidGrid with",
724+
"custom capabilities.",
725+
"Manage addons in the Pro",
726+
"web UI (Extensions tab).",
671727
]
672728
for line in help_lines:
673-
tk.Label(inner, text=line, bg=BG2,
674-
fg=FG3 if not line.startswith("~") else ACCENT,
675-
font=FONT_SMALL, anchor=tk.W,
676-
justify=tk.LEFT).pack(anchor=tk.W)
729+
tk.Label(parent, text=line, bg=BG2,
730+
fg=FG3, font=FONT_SMALL,
731+
anchor=tk.W).pack(anchor=tk.W)
677732

678-
self._refresh_profile_list()
733+
def _on_addon_select(self, event=None):
734+
sel = self._addon_lb.curselection()
735+
if not sel:
736+
return
737+
idx = sel[0]
738+
if idx < len(self._addon_data):
739+
a = self._addon_data[idx]
740+
desc = a.get("description", "No description available.")
741+
lines = [f"Name: {a['name']}",
742+
f"ID: {a.get('id', '?')}",
743+
f"Ver: {a.get('version', '?')}",
744+
f"Auth: {a.get('author', '?')}",
745+
f"",
746+
f"{desc}"]
747+
self._addon_detail_var.set("\n".join(lines))
679748

680749
def _refresh_profile_list(self):
681750
self._profile_lb.delete(0, tk.END)

0 commit comments

Comments
 (0)