Skip to content

feat(theme): Phase 6 — .aethertheme import/export + drag-and-drop#3164

Merged
ten9876 merged 1 commit into
mainfrom
auto/theme-pr6
May 25, 2026
Merged

feat(theme): Phase 6 — .aethertheme import/export + drag-and-drop#3164
ten9876 merged 1 commit into
mainfrom
auto/theme-pr6

Conversation

@ten9876

@ten9876 ten9876 commented May 25, 2026

Copy link
Copy Markdown
Collaborator

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-pr4 so 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 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

  • Builds clean (--target all)
  • Save As "My Glassy Plasma" → Theme actions → Export to file… → save as .aethertheme
  • Switch to Default Dark → Theme actions → Import from file… → re-imports + activates
  • Drag the same file onto the dialog → re-imports as "My Glassy Plasma (2)"
  • Drop a non-theme file → silently refused at drag-enter
  • Single Qt-rendered dropdown arrow on the Theme actions button (was two)

🤖 Generated with Claude Code

@ten9876 ten9876 requested a review from a team as a code owner May 25, 2026 20:38
Base automatically changed from auto/theme-pr4 to main May 25, 2026 20:47
## 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>
@ten9876 ten9876 merged commit df964e3 into main May 25, 2026
8 checks passed
@ten9876 ten9876 deleted the auto/theme-pr6 branch May 25, 2026 21:13
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>
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.

RFC: Theming subsystem (light + dark + user-customizable + import/export)

1 participant