Skip to content

feat: Implement medium-priority issues #47-#52#129

Open
olafkfreund wants to merge 21 commits intocosmic-utils:masterfrom
olafkfreund:master
Open

feat: Implement medium-priority issues #47-#52#129
olafkfreund wants to merge 21 commits intocosmic-utils:masterfrom
olafkfreund:master

Conversation

@olafkfreund
Copy link

Summary

  • Feature request: copy url #49 — Split editor into collapsible Basic/Advanced sections. Basic shows category, persistent profile, window size, decorations. Advanced (toggled) shows private mode, mobile sim, user agent, permissions, custom CSS/JS, URL schemes.
  • Creates desktop entry with empty Exec= line #51 — Add keyboard shortcuts: Ctrl+F (search), Ctrl+E (export), Ctrl+I (import), Ctrl+L (launch), Ctrl+D (duplicate).
  • Icon not loading #52 — Quick-actions toolbar above the editor for installed apps with Run, Duplicate, Open in Browser, and Delete buttons.
  • Following system color scheme #50 — Running app status indicators via /proc scanning every 5 seconds, displayed as a filled circle (●) prefix in the nav bar.
  • I can not edit a web app #47 — Grid/card view toggle in the header bar with 64px icon cards. View mode persisted in config (bumped to v2). Clicking a card switches to the editor.
  • Webapps use the same entry/icon as Firefox in the dock #48 — App preview thumbnails fetched via thum.io, cached for 24 hours. Auto-fetched on nav select, with a manual "Load Preview" button for installed apps.

Test plan

  • Launch with just run and verify Basic/Advanced editor sections toggle correctly
  • Test all keyboard shortcuts (Ctrl+F/E/I/L/D) work as expected
  • Verify quick-actions toolbar appears for installed apps and buttons function
  • Launch a webview app and confirm the running indicator (●) appears in nav after ~5s
  • Toggle between grid and list view; verify grid cards display and clicking opens the editor
  • Verify thumbnail preview loads for installed apps with valid URLs
  • Delete existing config and verify defaults work; keep old v1 config and verify backward compat

🤖 Generated with Claude Code

olafkfreund and others added 19 commits February 9, 2026 11:26
- Remove all .unwrap()/.expect() from production code paths, replacing
  with proper error handling via Result types and graceful fallbacks
- Fix desktop entry injection by sanitizing Name/Exec/WMClass fields
- Fix path traversal via app_id by stripping /, \, and .. sequences
- Add with_navigation_handler to webview blocking non-http/https URLs
- Add with_new_window_req_handler to block unsafe new window requests
- Validate URL scheme (http/https only) before loading in webview
- Harden icon-installer.sh: fix TOCTOU race, quote variables, add trap
- Add inline validation for title/URL fields in editor
- Add numeric-only filter with clamping for window size input
- Add toast notifications for save/delete via widget::toaster
- Add Ctrl+N keyboard shortcut for creating new web app
- Restructure app menu with Create new + divider + Settings + About
- Add RON file size limit (64KB), WalkDir depth limit (8), icon cap (200)
- Bound downloader output buffer to 32KB
- Make SVG extension check case-insensitive
- Add empty icon search state message
- Add CLAUDE.md with project documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add favicon auto-detection from URL via Google S2 favicons API
- Add search/filter input in header bar for installed web apps
- Sort nav bar apps by category then name
- Add Ctrl+S keyboard shortcut for saving current web app
- Add "New Window" desktop action to generated .desktop files
- Add first-run onboarding empty state when no apps installed
- Add tooltip on icon picker button for accessibility
- Add Nix flake with crane for reproducible builds and dev shell
- Replace once_cell dependency with std::sync::LazyLock

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add import/export of web apps via RON files with file picker dialogs
- Add app duplication button in editor for installed apps
- Add download handler redirecting to XDG download directory
- Disable devtools in production webview builds
- Add i18n keys for import/export/duplicate functionality

Closes #10, #12, #15, #18

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nitization

- Restrict url_valid() to http/https schemes only (reject javascript:, file:, data:)
- Expand sanitize_desktop_field() to strip all control chars, backslashes, semicolons
- Harden favicon download: validate domain chars, add --timeout=10, verify image
  magic bytes (PNG/JPEG/GIF/ICO), sanitize domain in filename
- Fix download handler to deny when dest_path has no valid filename
- Add full import validation: sanitize app_id, validate URL/name/icon/category,
  check profile path prefix, cap file size at 1MB and entries at 500
- Track import success/failure counts with per-app error logging
- Handle URL decode errors explicitly instead of silent empty-string fallback
- Remove dead ImportAppsResult message variant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… runtime deps

- Cache installed apps in memory to avoid disk I/O on every search keystroke
- Extract save_imported() to launcher.rs for proper separation of concerns
- Improve app duplication to preserve all browser settings from original
- Replace hardcoded file dialog strings with fl!() i18n macro calls
- Fix urlencoding unwrap_or_default() in theme file picker with proper error handling
- Add 17 missing i18n keys (English fallbacks) to all 13 non-English locales
- Add wget runtime dependency to flake.nix via gappsWrapperArgs PATH prefix
- Clean up stale URL in Italian locale file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace eprintln! with tracing::error! for consistent logging (lib.rs, browser.rs, mod.rs)
- Replace PathBuf::from_str with PathBuf::from (infallible) in lib.rs and mod.rs
- Remove unused std::str::FromStr imports from lib.rs and mod.rs
- Remove redundant String::from() wrapper on .to_string() in editor.rs
- Remove unnecessary Vec<_> type annotation in lib.rs
- Extract filter_numeric() helper to deduplicate window size input filtering in editor.rs
- Remove unnecessary intermediate variable in launcher.rs installed_webapps()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…uctions

Add comprehensive features list, NixOS/Nix flake build details,
system-wide and Home Manager installation examples, keyboard shortcuts,
import/export docs, architecture overview, and generic Linux build guide.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ntions

- Remove redundant .clone() on bool in editor.rs
- Use format!() for app_id generation to avoid intermediate allocation
- Replace double file open with std::fs::read() in launcher create()
- Add path component traversal check (..) on imported profile paths
- Validate imported icon paths for directory traversal sequences
- Add 2MB size limit on favicon download responses
- Add MAX_APP_ID_LEN (128) truncation in sanitize_app_id
- Fix icon_pack_installed path accumulation bug (.push without .pop)
- Replace all `with pkgs;` with explicit pkgs.* references in flake.nix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Security hardening, panic elimination, import/export, UX improvements,
code review fixes, and documentation updates. See PR cosmic-utils#127 for details.
- Fix wrapGAppsHook -> wrapGAppsHook3 rename in nixpkgs-unstable
- Pin libcosmic to rev 4c4eddb for reproducible Nix builds
- Regenerate Cargo.lock with pinned dependency
- Fix Rust 2024 edition String-to-&str coercion in ashpd file dialogs
- Fix fl!() macro: use flat keys (warning-app-name) not dotted attributes
- Fix widget::tooltip() to accept Element via widget::text() wrapper
- Fix toast Task<Message> -> Task<Action<Message>> with .map(cosmic::Action::App)
- Fix task::message() wrapping with cosmic::action::app()
- Fix borrow checker issue with nav iterator in Rust 2024 edition
- Fix temporary lifetime in format! macro (owned String vs reference)
- Fix add_icon_packs_install_script() error type to be Send + Sync
- Remove unused imports (url::Url, ffi::OsStr)
- Add flake.lock for reproducible Nix builds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unused svg and serde_json dependencies from Cargo.toml
- Harden sanitize_app_id with whitelist filter and loop-based .. removal
- Validate icon_name/extension in move_icon to prevent path traversal
- Fix themes_list index bounds check to prevent panic
- Localize icon picker file dialog strings with fl!() macros
- Replace .expect() in main.rs with graceful error handling
- Handle read_to_end failure in image_handle instead of silently ignoring

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add runtime assertion that checks Rust compiler version is >= 1.85.0,
which is required for edition = "2024" used in Cargo.toml.

The assertion:
- Throws a clear error message if nixpkgs provides Rust < 1.85.0
- Includes the detected version in the error message
- Suggests updating the nixpkgs input as the fix
- Uses minimal, non-invasive approach (no rust-overlay or toolchain changes)

This prevents confusing build failures when using older nixpkgs versions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- #22: Move blocking I/O off async runtime with spawn_blocking in
  find_icon() and image_handle() to prevent UI freezes
- #23: Remove redundant installed_webapps() disk scan in AppEditor::from()
- #24/#31: Preserve nav selection after save/reload via rebuild_nav_from_cache
  accepting optional app ID parameter
- #25: Add sync I/O doc comments to move_icon()
- #26/#34: Add proper error handling in SaveLauncher with toast on failure
- #27: Eliminate entity ID staleness by inlining nav reload after save
- #28: Remove dead Message::Close no-op variant
- #29: Standardize FaviconResult to use Task::perform wrapping convention
- #33: Optimize Browser::from_appid() to read single .ron file directly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add custom_css and custom_js fields to Browser struct
- Inject CSS via <style> element appended to <head> on page load
- Inject JS via wry initialization_script on page load
- Add CSS/JS text input fields in app editor settings section
- Preserve CSS/JS values on app duplicate
- Add i18n keys with Fluent-escaped placeholders
- JS field includes security warning about full page access

Closes #36
Closes #37

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
#38, #39, #40)

- #35: Per-app permission management (camera, mic, geolocation, notifications)
  with JavaScript-based enforcement in webview
- #38: Custom user agent string with Default/Mobile/Custom presets
- #39: Forward web notifications to COSMIC desktop via notify-rust IPC
- #40: Website data management — clear cookies/cache/storage per app

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
#41: Expanded desktop actions — New Window, New Private Window (--private
flag), Open in Browser (xdg-open). Added `private` field to WebviewArgs
with --private CLI flag support in webview binary.

#42: URL scheme handlers — Added url_schemes field to Browser struct,
comma-separated input in editor UI, parsed and validated on save.
Added i18n keys (url-schemes, url-schemes-placeholder).

#43: Media session integration — Auto-wires play/pause/seek handlers to
first video/audio element via initialization script. Reports media state
changes over IPC. MutationObserver re-wires on DOM changes.

#44: Notification badge detection — Monitors document.title for [N]/(N)
patterns and intercepts Badging API (setAppBadge/clearAppBadge). Reports
badge count changes over IPC.

#45: Site title auto-detection — fetch_site_title() fetches page HTML
via wget and extracts <title> tag. Triggered alongside favicon download
when title field is empty. Includes html_decode_basic() for entities.

#46: StartupWMClass fix — WM_CLASS is set via gtk::glib::set_program_name()
before window creation, which GTK uses as the X11 WM_CLASS res_name.
Matches StartupWMClass in generated .desktop entries.

Also refactored IPC handler to be unconditional (handles notifications,
media state, and badge count), and reformatted code per clippy/rustfmt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The COSMIC trademark policy reserves the cosmic- prefix for official
System76 packages. Third-party projects should use cosmic-ext- instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rsion check

- Replace flake-utils with nixpkgs.lib.genAttrs scoped to Linux only
- Replace fragile _ binding version check with idiomatic assert
- Add overlays.default output for NixOS system integration
- Remove redundant rustfmt/clippy from devShell (already in toolchain)
- Update CLAUDE.md to document flake.nix existence

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- #49: Split editor into Basic/Advanced collapsible sections
- #51: Add keyboard shortcuts (Ctrl+F/E/I/L/D) for search, export,
  import, launch, and duplicate
- #52: Quick-actions toolbar (Run, Duplicate, Open in Browser, Delete)
  for installed apps above the editor
- #50: Running app status indicators via /proc scanning with 5s polling,
  shown as filled circle prefix in nav bar
- #47: Grid/card view toggle with icon cards, persisted in config (v2)
- #48: App preview thumbnails via thum.io with 24h cache and auto-fetch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 13, 2026 17:34
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements a batch of medium-priority UX/features for Quick Web Apps, including editor UX improvements, app management actions, view mode persistence, thumbnails, keyboard shortcuts, and status indicators; also adds import/export and various hardening changes.

Changes:

  • Add Basic/Advanced editor sections, quick-actions toolbar, duplication, and app data clearing.
  • Add keyboard shortcuts, search, grid/list view toggle (persisted via config v2), and running-app indicators via /proc polling.
  • Add thumbnail previews (thum.io) + favicon/title fetching, plus import/export of app configs and Nix flake packaging/docs updates.

Reviewed changes

Copilot reviewed 23 out of 31 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/localize.rs Switch localization loader to std::sync::LazyLock.
src/lib.rs Config v2 bump; add favicon/title/thumbnail download + caching helpers; add /proc running scan; improve error handling and blocking I/O usage.
src/launcher.rs Harden DB reads; sanitize desktop entry fields; add desktop actions; add import/export implementation.
src/browser.rs Add app-id sanitization + new per-app settings (UA, permissions, CSS/JS, schemes); change config load path.
src/bin/webview.rs Add URL-scheme safety checks, permission/UA handling, notification forwarding, and custom CSS/JS injection.
src/bin/dev-heppen-webapps/pages/mod.rs Add shortcuts, search, grid/list view, running indicators polling, quick-actions toolbar, import/export UI, toasts.
src/bin/dev-heppen-webapps/pages/iconpicker.rs Improve file chooser error handling + empty search results UX.
src/bin/dev-heppen-webapps/pages/editor.rs Basic/Advanced sections; favicon + title fetch; duplication; thumbnail loading; validation hints.
src/bin/dev-heppen-webapps/main.rs Avoid panic on tracing subscriber init failure.
src/bin/dev-heppen-webapps/config.rs Add persisted ViewMode and bump config version to 2.
resources/scripts/icon-installer.sh Safer temp file handling + cleanup via trap; quote paths.
i18n/*/webapps.ftl Add new strings for import/export, toasts, advanced settings, thumbnails, etc.
flake.nix / flake.lock Add Nix flake build/devshell/checks; wrap PATH with wget.
README.md Expand feature/docs; add Nix instructions and architecture section.
Cargo.toml / Cargo.lock Add deps (notify-rust), remove once_cell/svg, pin libcosmic rev.
CLAUDE.md Repository guide / architecture notes for Claude Code.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +38 to +39
if sanitized.len() > MAX_APP_ID_LEN {
sanitized[..MAX_APP_ID_LEN].to_string()
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sanitize_app_id truncates with sanitized[..MAX_APP_ID_LEN], which slices by byte index and can panic if the sanitized ID contains non-ASCII alphanumeric characters (UTF-8 multibyte). Consider either restricting to ASCII (is_ascii_alphanumeric) or truncating by chars().take(MAX_APP_ID_LEN) to guarantee valid boundaries.

Suggested change
if sanitized.len() > MAX_APP_ID_LEN {
sanitized[..MAX_APP_ID_LEN].to_string()
if sanitized.chars().count() > MAX_APP_ID_LEN {
sanitized.chars().take(MAX_APP_ID_LEN).collect()

Copilot uses AI. Check for mistakes.
Comment on lines +98 to 106
let content = std::fs::read_to_string(&db_path).ok()?;

// Same 64KB safety limit used in launcher::installed_webapps()
const MAX_RON_SIZE: usize = 64 * 1024;
if content.len() > MAX_RON_SIZE {
tracing::warn!("RON file too large: {}", db_path.display());
return None;
}

Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Browser::from_appid reads the entire RON file into memory before enforcing the 64KB limit. A large file in the database directory could still cause excessive memory usage/slowdown. Prefer checking metadata.len() (or reading only up to the limit) before read_to_string.

Suggested change
let content = std::fs::read_to_string(&db_path).ok()?;
// Same 64KB safety limit used in launcher::installed_webapps()
const MAX_RON_SIZE: usize = 64 * 1024;
if content.len() > MAX_RON_SIZE {
tracing::warn!("RON file too large: {}", db_path.display());
return None;
}
// Same 64KB safety limit used in launcher::installed_webapps()
const MAX_RON_SIZE: usize = 64 * 1024;
// Check file size via metadata before reading to avoid loading overly large files.
let metadata = std::fs::metadata(&db_path).ok()?;
if metadata.len() > MAX_RON_SIZE as u64 {
tracing::warn!("RON file too large: {}", db_path.display());
return None;
}
let content = std::fs::read_to_string(&db_path).ok()?;

Copilot uses AI. Check for mistakes.
license = pkgs.lib.licenses.gpl3Only;
maintainers = [];
platforms = pkgs.lib.platforms.linux;
mainProgram = "dev-heppen-webapps";
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package installs the main binary as dev.heppen.webapps (see justfile and apps.default.program), but meta.mainProgram is set to dev-heppen-webapps. This mismatch can affect tooling that relies on mainProgram. Consider setting mainProgram to dev.heppen.webapps.

Suggested change
mainProgram = "dev-heppen-webapps";
mainProgram = "dev.heppen.webapps";

Copilot uses AI. Check for mistakes.
Comment on lines +88 to +101
#### Quick run (no installation)

```bash
nix run github:olafkfreund/cosmic-ext-web-apps
```

#### Build from source

```bash
git clone https://github.com/olafkfreund/cosmic-ext-web-apps.git
cd cosmic-ext-web-apps
nix build
./result/bin/dev.heppen.webapps
```
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Nix instructions reference github:olafkfreund/cosmic-ext-web-apps, which doesn’t match this repository (cosmic-utils/web-apps). This will mislead users trying to use the flake from this repo. Consider updating the URLs and directory names to point at the current repository / flake output names.

Copilot uses AI. Check for mistakes.
- #53: Content blocking (ads/trackers) via DOM removal
- #54: HTTP/HTTPS proxy support via environment variables
- #55: Configurable page zoom level via CSS zoom
- #56: Session restore — saves/restores last visited URL
- #57: Usage statistics — launch count and last launched timestamp
- #58: Bulk operations — select, delete, export multiple apps
- #59: Minimize to background on window close
- #60: Block third-party cookies via JS override
- #61: Block WebRTC IP leak by disabling RTCPeerConnection
- #62: Auto dark mode via CSS filter inversion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant