feat(theme): Phase 6 — .aethertheme import/export + drag-and-drop#3164
Merged
Conversation
## Summary Closes RFC #3076 by adding a share-friendly file format for AetherSDR themes plus the import / export / drag-and-drop UI that operators need to actually swap themes around. The format is plain JSON with a `schemaVersion` discriminator (same shape as the user-dir saves), so sharing happens via Discord / Discussions / gist / email attachment — no hosted marketplace. ## ThemeManager additions * exportThemeToFile(themeName, filePath, errorMessage) Dumps the active theme's live state (or another registered theme's on-disk JSON) to an arbitrary file. Schema version stamped, gradient + scalar tokens serialised identically to saveCurrentThemeAs. * importThemeFromFile(filePath, errorMessage) Validates magic + schemaVersion, picks a destination name from the JSON's "name" field (file stem fallback), sanitises against path traversal (slashes → underscores), refuses to clobber built- ins, suffixes "(2)", "(3)"… on name collisions instead of overwriting existing themes, rewrites the "name" field to match the final filename, copies into ~/.config/AetherSDR/themes/, and makes the new theme active. Returns the final name on success or empty + populated errorMessage on failure. ## Theme Editor changes * "Theme actions ▾" menu gains an Export… / Import… pair below the existing Rename / Delete actions. * setAcceptDrops(true) + dragEnterEvent / dropEvent overrides — drop one or more .aethertheme (or .json theme) files anywhere on the dialog window to install them in order. Drop validation refuses non-local URLs and any file whose extension isn't aethertheme / json, so an arbitrary file dropped on the dialog gets a "no-go" cursor instead of a confusing error. * Cosmetic: removed the manual ▾ glyph from "Theme actions" button label — QPushButton::setMenu() auto-renders one and the two arrows collided visually. ## Format compatibility * schemaVersion = 1 is current. schemaVersion > 1 loads with a log warning instead of refusing — unknown tokens round-trip unchanged so a future v2 theme survives a v1 build (with the unknown tokens unused but preserved). * Missing tokens on import fall back to the built-in defaults via the existing seedBuiltinDefaults() path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
G6PWY-Chris
pushed a commit
to G6PWY-Chris/AetherSDR
that referenced
this pull request
Jun 22, 2026
…thersdr#3164) ## Summary Closes [RFC aethersdr#3076](aethersdr#3076) by adding a share-friendly file format for AetherSDR themes plus the import / export / drag-and-drop UI that operators need to actually swap themes around. **Depends on aethersdr#3160** — currently targets `auto/theme-pr4` so the diff shows only Phase 6's delta. Will retarget to main once aethersdr#3160 lands. ## ThemeManager additions - `exportThemeToFile(themeName, filePath, *err)` — dumps the active theme's live state (or another registered theme's on-disk JSON) to an arbitrary file - `importThemeFromFile(filePath, *err)` — validates schema, picks a destination name from the JSON's `"name"` field (file stem fallback), sanitises against path traversal, refuses to clobber built-ins, suffixes `(2)` / `(3)`… on name collisions, copies into `~/.config/AetherSDR/themes/`, makes it active. Returns the final name on success. ## Theme Editor changes - **"Theme actions" menu** gains an Export… / Import… pair below the existing Rename / Delete actions - **Drag-and-drop**: drop one or more `.aethertheme` (or `.json` theme) files anywhere on the dialog window → each imports + activates in order - Drop validation refuses non-local URLs and any file whose extension isn't `aethertheme` / `json` — arbitrary files get the no-go cursor at drag-enter - Cosmetic: removed the manual `▾` glyph from "Theme actions" button label (QPushButton with `setMenu()` auto-renders one; the two collided) ## File format (`.aethertheme`) Plain JSON, identical shape to the user-dir `themes/*.json` files (same `schemaVersion`, `name`, `author`, `version`, `description`, `tokens`). Sharing happens via file exchange — Discord, Discussions, gist, email attachment. No hosted marketplace. ## Format compatibility - `schemaVersion = 1` is current; `> 1` loads with a log warning instead of refusing — unknown tokens round-trip unchanged so a future v2 theme survives a v1 build - Missing tokens on import fall back to built-in defaults via the existing `seedBuiltinDefaults()` path ## Test plan - [x] Builds clean (`--target all`) - [x] Save As "My Glassy Plasma" → Theme actions → Export to file… → save as `.aethertheme` - [x] Switch to Default Dark → Theme actions → Import from file… → re-imports + activates - [x] Drag the same file onto the dialog → re-imports as "My Glassy Plasma (2)" - [x] Drop a non-theme file → silently refused at drag-enter - [x] Single Qt-rendered dropdown arrow on the Theme actions button (was two) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes RFC #3076 by adding a share-friendly file format for AetherSDR themes plus the import / export / drag-and-drop UI that operators need to actually swap themes around.
Depends on #3160 — currently targets
auto/theme-pr4so the diff shows only Phase 6's delta. Will retarget to main once #3160 lands.ThemeManager additions
exportThemeToFile(themeName, filePath, *err)— dumps the active theme's live state (or another registered theme's on-disk JSON) to an arbitrary fileimportThemeFromFile(filePath, *err)— validates schema, picks a destination name from the JSON's"name"field (file stem fallback), sanitises against path traversal, refuses to clobber built-ins, suffixes(2)/(3)… on name collisions, copies into~/.config/AetherSDR/themes/, makes it active. Returns the final name on success.Theme Editor changes
.aethertheme(or.jsontheme) files anywhere on the dialog window → each imports + activates in orderaethertheme/json— arbitrary files get the no-go cursor at drag-enter▾glyph from "Theme actions" button label (QPushButton withsetMenu()auto-renders one; the two collided)File format (
.aethertheme)Plain JSON, identical shape to the user-dir
themes/*.jsonfiles (sameschemaVersion,name,author,version,description,tokens). Sharing happens via file exchange — Discord, Discussions, gist, email attachment. No hosted marketplace.Format compatibility
schemaVersion = 1is current;> 1loads with a log warning instead of refusing — unknown tokens round-trip unchanged so a future v2 theme survives a v1 buildseedBuiltinDefaults()pathTest plan
--target all).aethertheme🤖 Generated with Claude Code