Skip to content
Mobile
Skill

/desktop-expert

Desktop JVM shim in desktopApp/: AmethystDesktop window, DesktopMenuBar, platform ports, OS keyring; :desktopApp:run, packaging. Use when touching desktopApp, or to run/screenshot the shared UI headless (tools/desktop-run, Xvfb) to verify a UI change. NOT for writing shared

From plugin
amethyst
1.6k12 skills

Claude Code

$ npx -y skills add vitorpamplona/amethyst --skill desktop-expert --agent claude-code

SKILL.md

desktop-expert.SKILL.md
name: desktop-expert
description: Desktop JVM shim in desktopApp/: AmethystDesktop window, DesktopMenuBar, platform ports, OS keyring; :desktopApp:run, packaging. Use when touching desktopApp, or to run/screenshot the shared UI headless (tools/desktop-run, Xvfb) to verify a UI change. NOT for writing shared screens.

Desktop (JVM shim)

`desktopApp` is a **shim**: it opens a window and renders the same `AmethystApp` Android renders. Screens, the navigation shell (bottom bar / rail / drawer, chosen by `ScreenLayoutSpec`) and the deck are shared UI in `commonsUI` — see AGENTS.md "Navigation Shell" and "Feature Workflow", and `compose-expert`. Never add a desktop-only screen or a desktop-specific navigation layout. Background: `commons/plans/2026-09-27-one-ui-android-desktop.md`, `commons/plans/2026-10-09-legacy-desktop-retirement.md` (the legacy desktop app is gone).

Where things live (`desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/`)

| File | Role | |---|---| | `app/AmethystDesktop.kt` | `main()`: macOS AWT properties, data dir, `DesktopAppModules`, the `Window`, the CompositionLocals (`LocalAppPlatform`, `LocalNotePlatform`, …) around `AmethystApp`. `--publish-scheduled` runs headless (OS timer publishing scheduled posts). | | `app/DesktopAppModules.kt` | The desktop graph (Android's `AppModules` minus phone-only parts). | | `app/DesktopAppRoot.kt` | `AppRoot` impl; `jvmDestinations`. | | `app/DesktopAppPlatform.kt` | `AppPlatform` (commonsUI `ui/platform/`); unoverridden members keep the default, which hides the feature. | | `app/DesktopNotePlatform.kt`, `DesktopRichTextPlatform.kt` | `NotePlatform`, `RichTextPlatform` (commonsUI). | | `app/DesktopAccountViewModelHost.kt`, `DesktopLocalCacheHost.kt`, `DesktopAppServices.kt` | `AccountViewModelHost`, `LocalCacheHost`, `AppServices` (commons). | | `app/DesktopMenuBar.kt` | `DesktopNavigator`, the `Command` enum, `DesktopMenuBar`, `handleShortcut`. | | `app/Legacy*Import.kt` | One-time import of the retired legacy app's accounts, deck, feeds, privacy lock. | | `platform/PlatformInfo.kt` | `PlatformInfo.current` / `host`, `isMacOS`, `isWindows`, `isLinux`, `isGnome`, `isKde`. | | `platform/SavedWindowBounds.kt` | Window position/size persisted to `window-bounds.properties`. | | `platform/WaylandDisplay.kt`, `platform/AppWindow.kt` | Native Wayland: `WLToolkit` picked in `main()` on a Wayland session (`AMETHYST_X11=1` or the "Draw through XWayland" setting, `display.properties`, opts out); `AppWindow` is Compose's `Window` elsewhere and a Swing-drawn frame there. Open new top-level windows with `AppWindow`, never `Window`/`ComposeDialog`. | | `platform/DesktopUiScale.kt` | X11 at a fractional scale (`Xft.dpi` 120 = 125%, `AMETHYST_SCALE` by hand): AWT draws whole scales only, so `AppWindow` raises Compose's density by the rest. `LocalDensity` is then not AWT's scale: convert AWT window/event coordinates with `awtPixelScale(component)`, and size AWT windows in AWT units. | | `account/`, `tor/`, `network/`, `service/`, `ui/media/` | Account storage, Tor, HTTP, scheduled posts, video/audio players. | | `update/` | Self-update: `InstallChannel` (how this copy was installed: DMG, Homebrew, MSI, zip, AppImage, tarball, .deb/.rpm, Flatpak), `DesktopUpdateInstaller` + `UpdateHelperScripts` (stage, then swap after quit), `UpdateDownloader` (SHA-256 checked). The checker is commons `appupdate`; dev runs need `-Damethyst.update.devCheck=true`. |

A new shared feature that needs a desktop behaviour = a member on one of the ports above (default in the interface, override here), not a desktop branch in the shared screen.

Shortcuts and menu bar

  • One `Command` enum in `DesktopMenuBar.kt` drives both. `Command.shortcut` is Cmd+key on macOS,

Ctrl+key elsewhere (`isMac = PlatformInfo.host == Platform.MACOS`).

  • macOS: the system `MenuBar` carries the shortcuts. Linux/Windows get **no** menu bar (Swing draws

it inside the window); shortcuts go through `handleShortcut` from the window's `onPreviewKeyEvent`.

  • Deck commands are ignored unless `DeckCommandBus.active`; held chords are de-duplicated

(`AUTO_REPEAT_WINDOW_MS`).

  • Menu labels are `Res.string.*` (commonsUI strings, per AGENTS.md "Strings").

OS detection

`PlatformInfo.current` honours an override (`-Damethyst.platform=GNOME` or `AMETHYST_PLATFORM`) to preview another OS's theming; `PlatformInfo.host` is the real OS — use it for anything that touches the actual system (menu bar, Cmd key, shell-outs).

Keys

Keys go to the OS keyring through `SecureKeyStorage` (`commons/src/jvmMain/.../commons/keystorage/SecureKeyStorage.kt`, `java-keyring`). Without one they go to a NIP-49 password file (`PasswordKeyFile`), and a login can opt out of keeping its key. The status, prompts and Settings › Key storage are shared (`KeyStorageController`, provided by `DesktopAppRoot.keyStorage`); see `commons/plans/2026-10-12-desktop-key-storage.md`.

The keyring breaks in release builds only, so test the packaged app: `Amethyst --keyring-self-test` (CI runs it through `keyring-self-test.sh` in the repo's top-level scripts folder). ProGuard must keep jkeychain's native library and dbus-java's ServiceLoader transport, and drop the signatures of jars it rewrites (hkdf): `desktopApp/compose-rules.pro`, `verifyJkeychainNativeSurvivesProguard`, `verifyDbusTransportSurvivesProguard`.

Run and package

./gradlew :desktopApp:run                                   # on the JDK Gradle runs on (21+; packages: 25)
./gradlew :desktopApp:run -Damethyst.dataDir=/tmp/amy-test  # separate profile; -Damethyst.debug=true for DEBUG logs
./gradlew :desktopApp:packageDmg    # macOS; packageMsi (Windows), packageDeb / packageRpm (Linux)
xvfb-run ./gradlew :desktopApp:trainReleaseAotCache        # AOT cache into the release image (Linux portable builds)
  • Startup class archives (runtime `classes.jsa`, per-user `AutoCreateSharedArchive`, trained AOT

cache): BUILDING.md "Startup archi

Read more
@vitorpamplona
Ships withamethyst

Nostr client for Android

Stars on GitHub

Momentum

stars in total
1.6k
Star history of vitorpamplona/amethyst on GitHub, a chart by star-history.com

Other skills on amethyst.

amy-expert

amy-expert

amy CLI in cli/: adding an `amy <verb>` command, Main.kt dispatch, Output.emit/--json contract, exit codes 0/1/2/124, ~/.amy…

android-expert

android-expert

Android shim in `amethyst/`: MainActivity, AndroidManifest/intent filters, :napplet process, play/fdroid flavors, R8 +…

auth-signers

auth-signers

NostrSigner in Amethyst: NostrSignerInternal (local key), NostrSignerRemote (NIP-46 bunker), NostrSignerExternal (NIP-55 Android…

compose-expert

compose-expert

Shared Compose UI in `commonsUI/`: screens, components, the nav shell (`ScreenLayoutSpec`, `AppShellLayout`, Navigation 3 back…

More skills from other plugins.

deep-code-review

deep-code-review

In-depth design-focused code review - understands codebase context before evaluating PR changes, posts structured feedback to…

expo3d53k
@expo@expoInstall Skill
modlens

modlens

Plug-in vision for text-only models. Hard rule: when a file path or URL with an image extension (.png, .jpg, .jpeg, .webp, .gif,…

modlens2w4.6k
@liustack@liustackInstall Skill
appllama-app-design-skill

appllama-app-design-sk…

Build native-feeling, benchmark-quality mobile app screens (Expo / React Native). Use when designing or implementing any mobile…

appllama2mo3.9k
@appllama@appllamaInstall Skill

Questions about desktop-expert.

Which coding agents can use desktop-expert?
desktop-expert installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.