account-state
Account state and in-memory event store patterns in Amethyst. Use when working with `Account.kt` (per-user state objects — `kind3FollowList`, `nip65RelayList`,…
Compose Multiplatform Desktop patterns for the `desktopApp/` module. Use when working with (1) Desktop-only APIs (Window, WindowState, Tray, MenuBar, Dialog), (2) keyboard shortcuts and menu systems with OS-aware conventions (Cmd vs Ctrl, isMacOS branching), (3) desktop
$ npx -y skills add vitorpamplona/amethyst --skill desktop-expert --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/desktop-expertContext preview
The summary Claude sees to decide when to auto-load this skill.
Compose Multiplatform Desktop patterns for the `desktopApp/` module. Use when working with (1) Desktop-only APIs (Window, WindowState, Tray, MenuBar, Dialog), (2) keyboard shortcuts and menu systems with OS-aware conventions (Cmd vs Ctrl, isMacOS branching), (3) desktop
name: desktop-expert description: Compose Multiplatform Desktop patterns for the `desktopApp/` module. Use when working with (1) Desktop-only APIs (Window, WindowState, Tray, MenuBar, Dialog), (2) keyboard shortcuts and menu systems with OS-aware conventions (Cmd vs Ctrl, isMacOS branching), (3) desktop navigation (NavigationRail/sidebar vs Android bottom nav, multi-window), (4) file system integration (file pickers, drag-and-drop, Desktop.getDesktop()), (5) OS-specific behavior on macOS/Windows/Linux, (6) desktop UX principles (keyboard-first, tooltips). Delegates shared composables to compose-expert, build/packaging to gradle-expert, and source-set structure to kotlin-multiplatform.
Expert in Compose Multiplatform Desktop development for AmethystMultiplatform. Covers Desktop-specific APIs, OS conventions, navigation patterns, and UX principles.
**Auto-invoke when:**
**Delegate to:**
**In scope:**
**Out of scope:**
---
Desktop apps start with the `application {}` block:
// desktopApp/src/jvmMain/kotlin/Main.kt
fun main() = application {
val windowState = rememberWindowState(
width = 1200.dp,
height = 800.dp,
position = WindowPosition.Aligned(Alignment.Center)
)
Window(
onCloseRequest = ::exitApplication,
state = windowState,
title = "Amethyst"
) {
MenuBar { /* ... */ }
App()
}
}**Key points:**
**See:** `desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt` — grep for `fun main()`, `application {`, the top-level `Window`, and `MenuBar {` (the file is large and line numbers drift; navigate by symbol).
---
val windowState = rememberWindowState(
width = 1200.dp,
height = 800.dp,
position = WindowPosition.Aligned(Alignment.Center)
)
Window(
state = windowState,
title = "My App",
resizable = true,
onCloseRequest = ::exitApplication
) {
// Content
}fun main() = application {
var showSettings by remember { mutableStateOf(false) }
Window(onCloseRequest = ::exitApplication, title = "Main") {
Button(onClick = { showSettings = true }) {
Text("Open Settings")
}
}
if (showSettings) {
Window(
onCloseRequest = { showSettings = false },
title = "Settings"
) {
// Settings UI
}
}
}**Pattern:** Use state to control window visibility conditionally.
---
Window(onCloseRequest = ::exitApplication, title = "App") {
MenuBar {
Menu("File") {
Item("New Note", onClick = { /* ... */ })
Separator()
Item("Quit", onClick = ::exitApplication)
}
Menu("Edit") {
Item("Copy", onClick = { /* ... */ })
Item("Paste", onClick = { /* ... */ })
}
}
App()
}**Current state:** `Main.kt` already branches on `isMacOS` (declared at L120) for every menu shortcut — `if (isMacOS) { KeyShortcut(..., meta = true) } else { KeyShortcut(..., ctrl = true) }` (see L239, L249, L286, L313, L325, L335, L347, L358, L374, L384, L400, L416, L449). When adding a new shortcut, follow the same branching pattern rather than hardcoding `ctrl = true`.
**OS-specific shortcuts:**
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyShortcut
// Detect OS
val isMacOS = System.getProperty("os.name").lowercase().contains("mac")
MenuBar {
Menu("File") {
Item(
"New Note",
shortcut = if (isMacOS) {
KeyShortcut(Key.N, meta = true) // Cmd+N on macOS
} else {
KeyShortcut(Key.N, ctrl = true) // Ctrl+N on Win/Linux
},
onClick = { /* ... */ }
)
Item(
"Settings",
shortcut = if (isMacOS) {
KeyShortcut(Key.Comma, meta = true) // Cmd+, on macOS
} else {
KeyShortcut(Key.Comma, ctrl = true) // Ctrl+, on Win/Linux
},
onClick = { /* ... */ }
)
Separator()
Item(
"Quit",
shortcut = if (isMacOS) {
KeyShortcut(Key.Q, meta = true) // Cmd+Q on macOS
} else {
KeyShortcut(Key.Q, ctrl = true) // Ctrl+Q on Win/Linux
},
onClick = ::exitApplication
)
}
}**Standard shortcuts:**
| Action | macOS | Windows/Linux | |--------|-------|---------------| | New | Cmd+N |
Account state and in-memory event store patterns in Amethyst. Use when working with `Account.kt` (per-user state objects — `kind3FollowList`, `nip65RelayList`,…
Patterns for extending `amy`, the Amethyst CLI in `cli/`. Use when adding an `amy <verb>` command, touching files under `cli/src/main/kotlin/…/cli/`, wiring a…
Android platform patterns for the `amethyst/` module. Use when working with (1) Android navigation (Navigation Compose, type-safe routes, bottom nav), (2)…
Signer abstraction patterns in Amethyst. Use when working with event signing, choosing between a local keypair (`NostrSignerInternal`), a remote NIP-46 bunker…
Advanced Compose Multiplatform UI patterns for shared composables. Use when working with visual UI components, state management patterns (remember,…
Use when writing or reviewing Jetpack Compose layout APIs, modifier parameters, modifier chain construction, hardcoded root layout decisions, or layout…