一键导入
add-theme-icons
Add new `AppTheme.Icon` entries by pulling Icons8 icons (Arcade style by default) and converting them to Compose ImageVectors via the Valkyrie CLI.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Add new `AppTheme.Icon` entries by pulling Icons8 icons (Arcade style by default) and converting them to Compose ImageVectors via the Valkyrie CLI.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Enforce the Campfire pattern for feature modules to react to Audiobookshelf socket events. Feature impl modules contribute a `SocketEventListener` via `@ContributesMultibinding(UserScope::class, boundType = SocketEventListener::class)` — no `Scoped`, no `CoroutineScopeHolder` injection, no flow collection, no edits to `:infra:socket:impl`. Trigger when authoring or reviewing a class that reacts to socket events, or when asked to "wire X to socket events" / "add a socket listener for Y".
Enforce the Compose guideline that every `@Composable` function must declare `modifier: Modifier = Modifier` as its first optional parameter and apply it to the root layout. Trigger when authoring or reviewing any `@Composable` function — including `private` / `internal` helpers extracted from a UI file.
Extract hardcoded user-facing string literals from a Jetpack Compose / Compose Multiplatform file into the owning module's `composeResources/values/<module>_strings.xml`, then replace each call site with `stringResource(Res.string.<key>, ...)` and add the matching `Res` / key imports. Trigger when the user asks to "localize", "i18n", "extract strings", or "move strings to resources" for a given Compose file.
Enforce the Campfire IconButton accessibility convention — every IconButton (and its derivatives) must be wrapped with `IconButtonTooltip` and given a localized action label. Trigger when reviewing or authoring Compose UI that adds, copies, or moves an icon-only button.
Generate a new set of modules, i.e. a Subproject, in a given directory following a prescribed format
| name | add-theme-icons |
| description | Add new `AppTheme.Icon` entries by pulling Icons8 icons (Arcade style by default) and converting them to Compose ImageVectors via the Valkyrie CLI. |
| argument-hint | ["concept terms or icon ids..."] |
Invoke when the user wants to expand the set of icons available in AppTheme.Icon (used by OOTB AppTheme.Fixed themes and user-built AppTheme.Fixed.Custom themes). Icons should match the app's camping / nature / travel theme.
brew install ComposeGears/repo/valkyrie, check with command -v valkyrie). Required — do not hand-translate SVG paths.mcp__icons8mcp__search_icons, mcp__icons8mcp__get_icon_svg.| Concern | Value |
|---|---|
| Icon style (platform) | arcade (colored; unless user specifies otherwise) |
| Iconpack object | CampfireIcons (defined at common/compose/src/commonMain/kotlin/app/campfire/common/compose/icons/CampfireIcons.kt) |
| Nested pack | Theme |
| ImageVector package | app.campfire.common.compose.icons.theme |
| ImageVector files dir | common/compose/src/commonMain/kotlin/app/campfire/common/compose/icons/theme/ |
| Lazy thread-safety | LazyThreadSafetyMode.PUBLICATION (Valkyrie emits NONE — you must replace it) |
| Indent | 2 spaces (ktlint) |
| Enum location | ui/theming/api/src/commonMain/kotlin/app/campfire/ui/theming/api/AppTheme.kt → AppTheme.Icon |
Search candidates. First read AppTheme.kt and list the existing Icon enum entries so you can exclude them. Then use mcp__icons8mcp__search_icons with platform=arcade and camping/nature/travel query terms (examples: campfire, compass, binoculars, lantern, map, axe, sleeping bag, evergreen, suitcase, log cabin, canoe, fishing, hammock, trail, camper, waterfall, flashlight). Note: some searches return zero (kayak, hammock, thermos) — try synonyms (dinghy, picnic). Fire multiple searches in parallel.
Fetch SVGs in parallel via mcp__icons8mcp__get_icon_svg for each chosen icon id.
Stage SVGs. Write each to /tmp/campfire-icons-svg/<PascalCaseName>.svg. The filename becomes the generated Kotlin property name (CampfireIcons.Theme.<PascalCaseName>) — avoid names that collide with existing Kotlin types (e.g. prefer PaperMap over Map).
Run Valkyrie. Output to a scratch dir first (Valkyrie creates a theme/ subfolder):
valkyrie svgxml2imagevector \
--input-path=/tmp/campfire-icons-svg \
--output-path=/tmp/campfire-icons-out \
--package-name=app.campfire.common.compose.icons \
--iconpack-name=CampfireIcons \
--nested-pack-name=Theme \
--output-format=lazy-property \
--indent-size=2 \
--trailing-comma=true
Key flags: --package-name is the root package (Valkyrie appends .theme from --nested-pack-name); do not pass the full ...icons.theme package or you'll get a doubled path. --trailing-comma=true is required — without it ktlint flags every multi-arg path(...) call.
Post-process — replace Valkyrie's default lazy mode to match project convention:
bash -c 'for f in /tmp/campfire-icons-out/theme/*.kt; do sed -i "" "s/LazyThreadSafetyMode.NONE/LazyThreadSafetyMode.PUBLICATION/g" "$f"; done'
Move into the source tree.
cp /tmp/campfire-icons-out/theme/*.kt \
/Users/r0adkll/StudioProjects/Campfire/common/compose/src/commonMain/kotlin/app/campfire/common/compose/icons/theme/
Wire into AppTheme.Icon. In ui/theming/api/src/commonMain/kotlin/app/campfire/ui/theming/api/AppTheme.kt:
import app.campfire.common.compose.icons.theme.<Name> (keep alphabetical ordering).<Name>(icon = { CampfireIcons.Theme.<Name> }), to the Icon enum.Verify. Run --format first (it auto-fixes the alphabetical import reordering that adding new imports can break), then compile:
./scripts/ktlint --format
./gradlew :common:compose:compileKotlinIosSimulatorArm64 :ui:theming:api:compileKotlinIosSimulatorArm64 --console=plain
Pre-existing warnings (ExperimentalMaterial3Api opt-in, dayOfMonth deprecation, SynchronizedObject under compileCommonMainKotlinMetadata) are unrelated — ignore.
Clean up /tmp/campfire-icons-svg and /tmp/campfire-icons-out.
AppThemeImage via Image(...) (not Icon(... tint)), so the original Arcade colors come through — do not convert to single-color vectors.AppTheme.Fixed.Custom.icon), so every added entry is immediately user-selectable.color, plumpy), pass that as the platform filter in step 1 — the rest of the workflow is identical.