Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问clear审计通过

compose-expert作曲专家

Agent Skill

compose-expert 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

9,359

周安装

402

GitHub Stars

1,491

下载量

3,280
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:compose-expert(作曲专家)
来源仓库:https://github.com/vitorpamplona/amethyst
仓库路径:skills/compose-expert
安装命令:
npx skills add https://github.com/vitorpamplona/amethyst --skill compose-expert
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/vitorpamplona/amethyst --skill compose-expert

简介

compose-expert 提供 Compose Multiplatform 的视觉 UI 模式,支持 Android 和桌面端共享组件。

  • 适用于创建或重构共享 UI 组件、构建自定义图标或优化重绘性能的场景。
  • 涵盖 remember、derivedStateOf 等状态管理机制和 @Stable/@Immutable 注解使用。
  • 建议结合 android-expert 和 desktop-expert 处理平台特定导航结构。
  • 使用时需特别注意 StateFlow 和 sealed class 等 Kotlin 状态模式。

SKILL.md

Compose Multiplatform Expert

Visual UI patterns for sharing composables across Android and Desktop.

When to Use This Skill

  • Creating or refactoring shared UI components
  • Deciding whether to share UI in commonMain or keep platform-specific
  • Building custom ImageVector icons (robohash pattern)
  • State management: remember, derivedStateOf, produceState
  • Recomposition optimization: visual usage of @Stable/@Immutable
  • Material3 theming and styling
  • Performance: lazy lists, image loading

Delegate to other skills:

  • Navigation structure → android-expert, desktop-expert
  • Kotlin state patterns (StateFlow, sealed classes) → kotlin-expert
  • Build configuration → gradle-expert

Philosophy: Share by Default

Default to commons/commonMain unless platform experts indicate otherwise.

Always Share

  • UI components: Buttons, cards, lists, dialogs, inputs
  • State visualization: Loading, empty, error states
  • Custom icons: ImageVector assets (robohash, custom paths)
  • Theme utilities: Color calculations, style helpers
  • Material3 components: Any UI using Material primitives

Keep Platform-Specific

  • Navigation structure: Bottom nav (Android) vs Sidebar (Desktop)
  • Screen layouts: Platform-specific scaffolding
  • System integrations: File pickers, notifications, share sheets
  • Platform UX: Gestures, keyboard shortcuts, window management

Decision Framework

  1. Uses only Material3 primitives? → Share in commonMain
  2. Requires platform system APIs? → Platform-specific
  3. Pure visual component without navigation? → Share in commonMain
  4. Needs platform UX patterns? → Ask android-expert or desktop-expert

If uncertain, default to sharing - easier to split later than merge.

Shared Composable Anatomy

Structure

@Composable
fun SharedComponent(
    // State parameters (read-only)
    data: DataClass,
    isLoading: Boolean,
    // Event parameters (write-only)
    onAction: () -> Unit,
    // Visual parameters
    modifier: Modifier = Modifier,
    // Optional customization
    colors: ComponentColors = ComponentDefaults.colors()
) {
    // Implementation
}

Pattern: State down, events up

  • Parameters above modifier = required state/events
  • modifier parameter = layout control
  • Parameters below modifier = optional customization

Example: AddButton

@Composable
fun AddButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    text: String = "Add",
    enabled: Boolean = true
) {
    OutlinedButton(
        modifier = modifier,
        enabled = enabled,
        onClick = onClick,
        shape = ActionButtonShape,
        contentPadding = ActionButtonPadding
    ) {
        Text(text = text, textAlign = TextAlign.Center)
    }
}

// Shared constants for consistency
val ActionButtonShape = RoundedCornerShape(20.dp)
val ActionButtonPadding = PaddingValues(vertical = 0.dp, horizontal = 16.dp)

Why this works on all platforms:

  • Material3 primitives (OutlinedButton, Text)
  • No platform APIs
  • Configurable through parameters
  • Consistent styling via shared constants

State Management Patterns

remember - Cache Across Recompositions

@Composable
fun ExpandableCard() {
    var isExpanded by remember { mutableStateOf(false) }

    Column {
        IconButton(onClick = { isExpanded = !isExpanded }) {
            Icon(
                if (isExpanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
                contentDescription = if (isExpanded) "Collapse" else "Expand"
            )
        }

        if (isExpanded) {
            Text("Expanded content...")
        }
    }
}

Visual pattern: Toggle button → state changes → UI expands/collapses Use for: Simple UI state (toggles, counters, text input)

derivedStateOf - Optimize Frequent Changes

@Composable
fun ScrollToTopButton(listState: LazyListState) {
    // Only recomposes when showButton changes, not every scroll pixel
    val showButton by remember {
        derivedStateOf {
            listState.firstVisibleItemIndex > 0
        }
    }

    if (showButton) {
        FloatingActionButton(onClick = { /* scroll to top */ }) {
            Icon(Icons.Default.ArrowUpward, null)
        }
    }
}

Visual pattern: Scroll position (0, 1, 2...) → boolean (show/hide) → Button visibility Use for: Input changes frequently, derived result changes rarely Performance: Prevents recomposition on every scroll event

produceState - Async to Compose State

@Composable
fun LoadUserProfile(userId: String): State<User?> {
    return produceState<User?>(initialValue = null, userId) {
        value = repository.fetchUser(userId)
    }
}

@Composable
fun ProfileScreen(userId: String) {
    val user by LoadUserProfile(userId)

    when (user) {
        null -> LoadingState("Loading profile...")
        else -> ProfileCard(user!!)
    }
}

Visual pattern: Async operation → state updates → UI reflects changes Use for: Convert Flow, LiveData, callbacks into Compose state Lifecycle: Coroutine cancelled when composable leaves composition

For Kotlin-specific state patterns (StateFlow, sealed classes), see kotlin-expert.

State Hoisting

Move state up to make composables reusable:

// ❌ Stateful - hard to test, can't control externally
@Composable
fun BadSearchBar() {
    var query by remember { mutableStateOf("") }
    TextField(value = query, onValueChange = { query = it })
}

// ✅ Stateless - reusable, testable
@Composable
fun GoodSearchBar(
    query: String,
    onQueryChange: (String) -> Unit,
    modifier: Modifier = Modifier
) {
    TextField(
        value = query,
        onValueChange = onQueryChange,
        modifier = modifier
    )
}

@Composable
fun SearchScreen() {
    var query by remember { mutableStateOf("") }

    Column {
        GoodSearchBar(query = query, onQueryChange = { query = it })
        SearchResults(query = query)
    }
}

Principle: State up, events down

  • State: query: String (read-only parameter)
  • Events: onQueryChange: (String) -> Unit (callback parameter)

Recomposition Optimization

Visual Usage of @Immutable

Use @Immutable on data classes passed to composables:

@Immutable
data class UserProfile(val name: String, val avatar: String)

@Composable
fun ProfileCard(profile: UserProfile) {
    // Only recomposes when profile instance changes
    Row {
        RobohashImage(robot = profile.avatar)
        Text(profile.name, style = MaterialTheme.typography.titleMedium)
    }
}

Visual effect: Prevents recomposition when parent recomposes with same data Pattern: Mark parameter data classes as @Immutable Note: For Kotlin language details on @Immutable, see kotlin-expert

Stable Parameters

// ✅ Stable - won't trigger recomposition unless colors instance changes
@Composable
fun ThemedCard(
    content: String,
    colors: CardColors = CardDefaults.colors(),
    modifier: Modifier = Modifier
) {
    Card(colors = colors, modifier = modifier) {
        Text(content)
    }
}

For @Stable annotation details, see kotlin-expert.

Material3 Theming

All shared composables use Material3 for consistency:

@Composable
fun ThemedComponent() {
    val bg = MaterialTheme.colorScheme.background
    val fg = MaterialTheme.colorScheme.onBackground
    val primary = MaterialTheme.colorScheme.primary

    Column(
        modifier = Modifier.background(bg)
    ) {
        Text(
            "Title",
            style = MaterialTheme.typography.headlineMedium,
            color = fg
        )
        Button(
            onClick = { /* ... */ },
            colors = ButtonDefaults.buttonColors(containerColor = primary)
        ) {
            Text("Action")
        }
    }
}

Principles:

  • Colors: MaterialTheme.colorScheme.*
  • Typography: MaterialTheme.typography.*
  • Shapes: MaterialTheme.shapes.*

Theme Detection

@Composable
private fun isLightTheme(): Boolean {
    val background = MaterialTheme.colorScheme.background
    return (background.red + background.green + background.blue) / 3 > 0.5f
}

@Composable
fun ThemedIcon() {
    val isDark = !isLightTheme()
    val tint = if (isDark) Color.White else Color.Black
    Icon(Icons.Default.Face, null, tint = tint)
}

Custom Icons: ImageVector Pattern

Amethyst uses ImageVector for multiplatform icons.

roboBuilder DSL

fun roboBuilder(block: Builder.() -> Unit): ImageVector {
    return ImageVector.Builder(
        name = "Robohash",
        defaultWidth = 300.dp,
        defaultHeight = 300.dp,
        viewportWidth = 300f,
        viewportHeight = 300f
    ).apply(block).build()
}

Building Icons

fun customIcon(fgColor: SolidColor, builder: Builder) {
    builder.addPath(pathData1, fill = fgColor, stroke = Black, strokeLineWidth = 1.5f)
    builder.addPath(pathData2, fill = Black, fillAlpha = 0.4f)
    builder.addPath(pathData3, fill = Black, fillAlpha = 0.2f)
}

private val pathData1 = PathData {
    moveTo(144.5f, 87.5f)
    reflectiveCurveToRelative(-51.0f, 3.0f, -53.0f, 55.0f)
    lineToRelative(16.0f, 16.0f)
    close()
}

@Composable
fun CustomIcon() {
    Image(
        painter = rememberVectorPainter(
            roboBuilder {
                customIcon(SolidColor(Color.Blue), this)
            }
        ),
        contentDescription = "Custom icon"
    )
}

Why ImageVector?

  • Pure Kotlin, no XML
  • Works on Android, Desktop, iOS
  • GPU-accelerated
  • Type-safe

Caching Pattern

object CustomIcons {
    private val cache = mutableMapOf<String, ImageVector>()

    fun get(key: String): ImageVector {
        return cache.getOrPut(key) {
            buildIcon(key)
        }
    }
}

@Composable
fun CachedIcon(key: String) {
    Image(imageVector = CustomIcons.get(key), contentDescription = null)
}

For detailed icon patterns, see references/icon-assets.md.

Common Visual Patterns

State Visualization

@Composable
fun DataScreen(uiState: UiState) {
    when (uiState) {
        is UiState.Loading -> LoadingState("Loading...")
        is UiState.Empty -> EmptyState(
            title = "No data",
            onRefresh = { /* refresh */ }
        )
        is UiState.Error -> ErrorState(
            message = uiState.message,
            onRetry = { /* retry */ }
        )
        is UiState.Success -> ContentList(uiState.items)
    }
}

Components (all in commons/commonMain):

  • LoadingState - Progress indicator + message
  • EmptyState - Empty message + optional refresh button
  • ErrorState - Error message + optional retry button

Relay Status (Amethyst Pattern)

@Composable
fun RelayStatusIndicator(connectedCount: Int) {
    val statusColor = when {
        connectedCount == 0 -> RelayStatusColors.Disconnected
        connectedCount < 3 -> RelayStatusColors.Connecting
        else -> RelayStatusColors.Connected
    }

    Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
        Icon(
            imageVector = if (connectedCount > 0) Icons.Default.Check else Icons.Default.Close,
            tint = statusColor,
            modifier = Modifier.size(16.dp)
        )
        Text(
            "$connectedCount relay${if (connectedCount != 1) "s" else ""}",
            color = MaterialTheme.colorScheme.onSurfaceVariant
        )
    }
}

Visual mapping:

  • 0 relays → Red + X icon
  • 1-2 relays → Yellow + Check icon
  • 3+ relays → Green + Check icon

Placeholder Pattern

@Composable
fun PlaceholderScreen(
    title: String,
    description: String,
    modifier: Modifier = Modifier
) {
    Column(modifier = modifier) {
        Text(title, style = MaterialTheme.typography.headlineMedium)
        Spacer(Modifier.height(16.dp))
        Text(description, color = MaterialTheme.colorScheme.onSurfaceVariant)
    }
}

// Specific implementations
@Composable
fun SearchPlaceholder() = PlaceholderScreen(
    title = "Search",
    description = "Search for users, notes, and hashtags."
)

Pattern: Generic composable + specific wrappers with preset text

Performance

Avoid Unnecessary Recomposition

// ❌ Bad - recomposes on every scroll
@Composable
fun BadButton(scrollState: ScrollState) {
    if (scrollState.value > 100) {
        Button(onClick = {}) { Text("Top") }
    }
}

// ✅ Good - only recomposes when visibility changes
@Composable
fun GoodButton(scrollState: ScrollState) {
    val show by remember { derivedStateOf { scrollState.value > 100 } }
    if (show) {
        Button(onClick = {}) { Text("Top") }
    }
}

Lazy Lists

@Composable
fun FeedList(items: List<Item>) {
    LazyColumn {
        items(items, key = { it.id }) { item ->
            FeedItem(item)
        }
    }
}

Key principle: Use key parameter for stable item identity

Bundled Resources

  • references/shared-composables-catalog.md - Complete catalog of shared UI components
  • references/state-patterns.md - State management patterns with visual examples
  • references/icon-assets.md - Custom ImageVector icon patterns
  • references/rich-text-parsing.md - RichTextParser, UrlParser, GalleryParser, Patterns, MediaContentModels; NIP-92 imeta enrichment
  • scripts/find-composables.sh - Find all @Composable functions in codebase

Quick Reference

TaskPatternLocation
Reusable UIState hoistingcommons/commonMain
Simple stateremember {mutableStateOf()}Composable scope
Derived statederivedStateOf {}remember block
Async → stateproduceState {}Composable function
Custom iconsroboBuilder + PathDatacommons/icons
Loading/ErrorLoadingState, ErrorStatecommons/ui/components
Theme colorsMaterialTheme.colorSchemeAny @Composable
NavigationDelegate to platform expertamethyst/, desktopApp/

Common Workflows

Creating a Shared Component

  1. Start in commons/src/commonMain/kotlin/.../ui/components/
  2. Use Material3 primitives only
  3. Hoist state (parameters for data, callbacks for events)
  4. Add modifier parameter
  5. Use MaterialTheme for colors/typography
  6. Test on both Android and Desktop

Converting Existing Component

  1. Read current implementation in amethyst/ or desktopApp/
  2. Identify pure visual logic (no platform APIs)
  3. Create in commons/commonMain with hoisted state
  4. Replace platform implementations with shared component
  5. Keep platform-specific wrappers if needed

Custom Icon

  1. Export SVG from design tool
  2. Convert to PathData using Android Studio
  3. Create icon function with roboBuilder
  4. Add caching if generated dynamically
  5. Wrap in @Composable for easy use

Navigation (Delegate)

For navigation patterns:

  • Android bottom nav → android-expert
  • Desktop sidebar → desktop-expert
  • Multi-window → desktop-expert

Related Skills

  • kotlin-expert - Kotlin language aspects (@Immutable details, StateFlow, sealed classes)
  • android-expert - Android navigation, platform APIs
  • desktop-expert - Desktop navigation, window management, OS specifics
  • kotlin-coroutines - Async patterns, Flow integration

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Claude Code

28.59%
按下载量换算938

OpenCode

22.85%
按下载量换算749

Antigravity

19.52%
按下载量换算640

Gemini CLI

14.13%
按下载量换算463

Codex

7.67%
按下载量换算252

Cursor

3.51%
按下载量换算115

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills