From daff3c3f5aefa6de31add0b59c278b5331748ec1 Mon Sep 17 00:00:00 2001 From: Jordan Fleck Date: Fri, 1 May 2026 20:36:41 -0400 Subject: [PATCH 1/6] adding code to call quickmenu --- usr/lib/webapp-manager/webapp-manager.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/usr/lib/webapp-manager/webapp-manager.py b/usr/lib/webapp-manager/webapp-manager.py index d6aeb3e..c22768e 100755 --- a/usr/lib/webapp-manager/webapp-manager.py +++ b/usr/lib/webapp-manager/webapp-manager.py @@ -22,6 +22,7 @@ # 3. Local application/library specific imports. from common import _async, idle, WebAppManager, download_favicon, ICONS_DIR, BROWSER_TYPE_FIREFOX, BROWSER_TYPE_FIREFOX_FLATPAK, BROWSER_TYPE_ZEN_FLATPAK, BROWSER_TYPE_FIREFOX_SNAP +from quickstart import QuickstartDialog setproctitle.setproctitle("webapp-manager") @@ -215,6 +216,12 @@ def __init__(self, application): self.load_webapps() + # Show quickstart when the user has no apps yet (first run or clean slate) + if len([w for w in self.manager.get_webapps() if w.is_valid]) <= 1: + dialog = QuickstartDialog(self.window, self.manager) + dialog.run() + self.load_webapps() # refresh list if apps were just created + # Used by the OK button, indicates whether we're editing a web-app or adding a new one. self.edit_mode = False From 89faf53e8973ea57730bf36fa93197ecbe4daa6c Mon Sep 17 00:00:00 2001 From: Jordan Fleck Date: Fri, 1 May 2026 20:40:34 -0400 Subject: [PATCH 2/6] new file with both backend and display code for quickmenu. can be separated into existing files if preferred. --- usr/lib/webapp-manager/quickstart.py | 225 +++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 usr/lib/webapp-manager/quickstart.py diff --git a/usr/lib/webapp-manager/quickstart.py b/usr/lib/webapp-manager/quickstart.py new file mode 100644 index 0000000..3caba65 --- /dev/null +++ b/usr/lib/webapp-manager/quickstart.py @@ -0,0 +1,225 @@ +#!/usr/bin/python3 + +# Quickstart dialog — shown on first launch (no webapps installed). +# Offers to create a default suite of Email, Notes, Reminders, and Contacts +# for the Google, Apple, or Microsoft ecosystem in one click. + +import os +import gi +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +_ = lambda s: s # i18n passthrough; real gettext is wired in the main module + +# --------------------------------------------------------------------------- +# Ecosystem definitions +# Each entry lists the four web apps that will be created. +# --------------------------------------------------------------------------- +ECOSYSTEMS = [ + { + "key": "google", + "label": "Google", + "icon": "web-google", # falls back gracefully if absent + "color": "#4285F4", + "apps": [ + {"name": "Gmail", "url": "https://mail.google.com", "icon": "web-google-gmail", "desc": "Google email"}, + {"name": "Google Keep", "url": "https://keep.google.com", "icon": "web-google", "desc": "Google notes"}, + {"name": "Google Tasks", "url": "https://tasks.google.com", "icon": "web-google", "desc": "Google reminders"}, + {"name": "Google Contacts", "url": "https://contacts.google.com", "icon": "web-google", "desc": "Google contacts"}, + ], + }, + { + "key": "apple", + "label": "Apple", + "icon": "web-apple", + "color": "#555555", + "apps": [ + {"name": "iCloud Mail", "url": "https://www.icloud.com/mail", "icon": "web-apple", "desc": "iCloud email"}, + {"name": "iCloud Notes", "url": "https://www.icloud.com/notes", "icon": "web-apple", "desc": "iCloud notes"}, + {"name": "iCloud Reminders", "url": "https://www.icloud.com/reminders", "icon": "web-apple", "desc": "iCloud reminders"}, + {"name": "iCloud Contacts", "url": "https://www.icloud.com/contacts", "icon": "web-apple", "desc": "iCloud contacts"}, + ], + }, + { + "key": "microsoft", + "label": "Microsoft", + "icon": "web-microsoft", + "color": "#00A4EF", + "apps": [ + {"name": "Outlook Mail", "url": "https://outlook.live.com/mail", "icon": "web-microsoft", "desc": "Outlook email"}, + {"name": "OneNote", "url": "https://www.onenote.com/notebooks", "icon": "web-microsoft", "desc": "Microsoft notes"}, + {"name": "Microsoft To Do", "url": "https://to-do.office.com", "icon": "web-microsoft", "desc": "Microsoft reminders"}, + {"name": "Outlook People", "url": "https://outlook.live.com/people", "icon": "web-microsoft", "desc": "Outlook contacts"}, + ], + }, +] + +APP_LABELS = ["Email", "Notes", "Reminders", "Contacts"] + + +class QuickstartDialog: + """ + Modal dialog shown when the user has no web apps installed. + Presents three ecosystem choices; clicking one creates all four apps + automatically. Cancel dismisses without creating anything. + """ + + def __init__(self, parent_window, manager): + self.manager = manager + self.chosen_ecosystem = None + + # Resolve the first available browser once up-front + self.browser = None + for b in manager.get_supported_browsers(): + if os.path.exists(b.test_path): + self.browser = b + break + + # ------------------------------------------------------------------ # + # Dialog shell + # ------------------------------------------------------------------ # + self.dialog = Gtk.Dialog() + self.dialog.set_transient_for(parent_window) + self.dialog.set_modal(True) + self.dialog.set_resizable(False) + self.dialog.set_title(_("Welcome to Web Apps")) + self.dialog.set_default_size(560, -1) + self.dialog.set_border_width(0) + + content = self.dialog.get_content_area() + content.set_spacing(0) + + outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=20) + outer.set_border_width(28) + content.pack_start(outer, True, True, 0) + + # ------------------------------------------------------------------ # + # Header + # ------------------------------------------------------------------ # + header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + header.set_halign(Gtk.Align.CENTER) + + app_icon = Gtk.Image.new_from_icon_name("webapp-manager", Gtk.IconSize.DIALOG) + header.pack_start(app_icon, False, False, 0) + + title = Gtk.Label() + title.set_markup("%s" % _("Welcome to Web Apps")) + header.pack_start(title, False, False, 0) + + subtitle = Gtk.Label() + subtitle.set_markup( + "%s" % + _("Pick an ecosystem to get Email, Notes, Reminders and Contacts\n" + "installed as apps in one click.") + ) + subtitle.set_justify(Gtk.Justification.CENTER) + subtitle.set_line_wrap(True) + header.pack_start(subtitle, False, False, 0) + + outer.pack_start(header, False, False, 0) + + # ------------------------------------------------------------------ # + # Ecosystem cards + # ------------------------------------------------------------------ # + cards_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12) + cards_box.set_halign(Gtk.Align.CENTER) + + icon_theme = Gtk.IconTheme.get_default() + + for eco in ECOSYSTEMS: + btn = self._make_card(eco, icon_theme) + cards_box.pack_start(btn, False, False, 0) + + outer.pack_start(cards_box, False, False, 0) + + # ------------------------------------------------------------------ # + # "Maybe later" cancel link + # ------------------------------------------------------------------ # + cancel_btn = Gtk.Button(label=_("Maybe later")) + cancel_btn.set_halign(Gtk.Align.CENTER) + cancel_btn.set_relief(Gtk.ReliefStyle.NONE) + cancel_btn.connect("clicked", lambda w: self.dialog.destroy()) + outer.pack_start(cancel_btn, False, False, 0) + + self.dialog.show_all() + + # ---------------------------------------------------------------------- # + # Helpers + # ---------------------------------------------------------------------- # + + def _make_card(self, eco, icon_theme): + """Return a styled button representing one ecosystem.""" + btn = Gtk.Button() + btn.set_relief(Gtk.ReliefStyle.NONE) + btn.set_sensitive(self.browser is not None) + btn.set_tooltip_text( + _("Create %s apps") % eco["label"] if self.browser + else _("No supported browser detected.") + ) + btn.connect("clicked", self._on_ecosystem_clicked, eco) + + card = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8) + card.set_border_width(14) + + # Ecosystem icon — graceful fallback + icon_name = eco["icon"] if icon_theme.has_icon(eco["icon"]) else "web-browser" + if not icon_theme.has_icon(icon_name): + icon_name = "webapp-manager" + eco_icon = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.DIALOG) + card.pack_start(eco_icon, False, False, 0) + + # Ecosystem name + name_lbl = Gtk.Label() + name_lbl.set_markup("%s" % eco["label"]) + card.pack_start(name_lbl, False, False, 0) + + # Four app labels + for app_label in APP_LABELS: + lbl = Gtk.Label() + lbl.set_markup("%s" % app_label) + card.pack_start(lbl, False, False, 0) + + btn.add(card) + return btn + + def _on_ecosystem_clicked(self, _widget, eco): + """Create all four apps for the chosen ecosystem then close.""" + if not self.browser: + return + + icon_theme = Gtk.IconTheme.get_default() + + for app in eco["apps"]: + # Resolve icon: prefer the specified one, fall back to webapp-manager + icon = app["icon"] + if not icon_theme.has_icon(icon): + icon = "webapp-manager" + + self.manager.create_webapp( + name=app["name"], + desc=app["desc"], + url=app["url"], + icon=icon, + category="Network", + browser=self.browser, + custom_parameters="", + isolate_profile=True, + navbar=False, + privatewindow=False, + ) + + self.chosen_ecosystem = eco["key"] + self.dialog.destroy() + + # ---------------------------------------------------------------------- # + # Public API + # ---------------------------------------------------------------------- # + + def run(self): + """ + Show the dialog and block until it closes. + Returns the chosen ecosystem key ("google" / "apple" / "microsoft"), + or None if the user cancelled. + """ + self.dialog.run() + return self.chosen_ecosystem From b808ec08feb38269e3ebb28d0694f7f6d01e7441 Mon Sep 17 00:00:00 2001 From: Jordan Fleck Date: Fri, 1 May 2026 21:12:12 -0400 Subject: [PATCH 3/6] minor edits --- usr/lib/webapp-manager/quickstart.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/lib/webapp-manager/quickstart.py b/usr/lib/webapp-manager/quickstart.py index 3caba65..72d4b71 100644 --- a/usr/lib/webapp-manager/quickstart.py +++ b/usr/lib/webapp-manager/quickstart.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 -# Quickstart dialog — shown on first launch (no webapps installed). +# Quickstart dialog — shown on first launch (zero or one webapp(s) installed). # Offers to create a default suite of Email, Notes, Reminders, and Contacts # for the Google, Apple, or Microsoft ecosystem in one click. @@ -59,7 +59,7 @@ class QuickstartDialog: """ - Modal dialog shown when the user has no web apps installed. + Modal dialog shown when the user has no or one web app(s) installed. Presents three ecosystem choices; clicking one creates all four apps automatically. Cancel dismisses without creating anything. """ From f2ebb4bd1bce291fcf8c0e05503a61471ddc52e2 Mon Sep 17 00:00:00 2001 From: Jordan Fleck Date: Sat, 2 May 2026 15:08:58 -0400 Subject: [PATCH 4/6] minor change for testing to get quickstart to come up --- usr/lib/webapp-manager/webapp-manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/webapp-manager/webapp-manager.py b/usr/lib/webapp-manager/webapp-manager.py index c22768e..0e33f73 100755 --- a/usr/lib/webapp-manager/webapp-manager.py +++ b/usr/lib/webapp-manager/webapp-manager.py @@ -217,7 +217,7 @@ def __init__(self, application): self.load_webapps() # Show quickstart when the user has no apps yet (first run or clean slate) - if len([w for w in self.manager.get_webapps() if w.is_valid]) <= 1: + if len([w for w in self.manager.get_webapps() if w.is_valid]) >= 1: # changed from < for testing dialog = QuickstartDialog(self.window, self.manager) dialog.run() self.load_webapps() # refresh list if apps were just created From 6062cde38000ed57aabce844ad2284774d680e1f Mon Sep 17 00:00:00 2001 From: Jordan Fleck Date: Sat, 2 May 2026 15:21:44 -0400 Subject: [PATCH 5/6] small change --- usr/lib/webapp-manager/quickstart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/webapp-manager/quickstart.py b/usr/lib/webapp-manager/quickstart.py index 72d4b71..e835fe4 100644 --- a/usr/lib/webapp-manager/quickstart.py +++ b/usr/lib/webapp-manager/quickstart.py @@ -31,7 +31,7 @@ { "key": "apple", "label": "Apple", - "icon": "web-apple", + "icon": "main-apple", #changed from web-apple "color": "#555555", "apps": [ {"name": "iCloud Mail", "url": "https://www.icloud.com/mail", "icon": "web-apple", "desc": "iCloud email"}, From 85c9a0693f2d3c31e252f1d0f5e8bcafda524fd5 Mon Sep 17 00:00:00 2001 From: Jordan Fleck Date: Mon, 4 May 2026 13:09:47 -0400 Subject: [PATCH 6/6] comment change and testing conditional removed --- usr/lib/webapp-manager/quickstart.py | 2 +- usr/lib/webapp-manager/webapp-manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/lib/webapp-manager/quickstart.py b/usr/lib/webapp-manager/quickstart.py index e835fe4..2d131c9 100644 --- a/usr/lib/webapp-manager/quickstart.py +++ b/usr/lib/webapp-manager/quickstart.py @@ -31,7 +31,7 @@ { "key": "apple", "label": "Apple", - "icon": "main-apple", #changed from web-apple + "icon": "web-apple", #changed from web-apple "color": "#555555", "apps": [ {"name": "iCloud Mail", "url": "https://www.icloud.com/mail", "icon": "web-apple", "desc": "iCloud email"}, diff --git a/usr/lib/webapp-manager/webapp-manager.py b/usr/lib/webapp-manager/webapp-manager.py index 0e33f73..c22768e 100755 --- a/usr/lib/webapp-manager/webapp-manager.py +++ b/usr/lib/webapp-manager/webapp-manager.py @@ -217,7 +217,7 @@ def __init__(self, application): self.load_webapps() # Show quickstart when the user has no apps yet (first run or clean slate) - if len([w for w in self.manager.get_webapps() if w.is_valid]) >= 1: # changed from < for testing + if len([w for w in self.manager.get_webapps() if w.is_valid]) <= 1: dialog = QuickstartDialog(self.window, self.manager) dialog.run() self.load_webapps() # refresh list if apps were just created