Author SHA1 Message Date
cdnutterandClaude Opus 5.5 c17d6f174b ios: 1.0.15, mixer rows stretch to fill spare height
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:22:27 -07:00
cdnutterandClaude Sonnet 5.5 e7e4fcc9d6 ios: 1.0.14, source.json, README for tab bar, AirPlay and debug logs
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 14:14:55 -07:00
4 changed files with 25 additions and 7 deletions

No files matched your search

+3
View File
@@ -171,6 +171,9 @@ CarPlay and Siri need the `carplay-audio` and `siri` entitlements, which a free
- **Now Playing:** shuffle and repeat sit either side of the play controls; tap repeat again for repeat one. The speed button (next to the mixer) goes from 0.5× to 2× without changing pitch.
- **Mixer:** on Now Playing, under the waveform. Tap **Save** to keep a mix for that song; it comes back every time the song plays.
- **AirPlay:** the AirPlay button on Now Playing picks the speaker or TV. Stems are mixed on the phone, so mixer changes apply live while it plays over AirPlay.
- **Tab bar:** Settings → Tab Bar. Pick and reorder up to five of Library, Songs, Albums, Artists, Genres, Playlists, Search, Downloads and Settings. Settings always stays.
- **Debug logs:** Settings → Debug. Turn on Verbose Logging, then View, Export or Clear the log. Export opens the share sheet with the app version, device, iOS version and audio output at the top. Tokens are stripped from the log.
- **Karaoke:** the mic button mutes vocals on every song and shows synced lyrics from [LRCLIB](https://lrclib.net). Tap a line to jump to it. Vocals can be On, Soft or Off, and Soft has a slider for how quiet.
- **Playlists:** the Playlists tab switches between Plex and Stem.
- Plex playlists sync with Plex and Plexamp.
+11 -5
View File
@@ -583,6 +583,9 @@ private struct MixerPanel: View {
let pm = g.playback
let names = pm.stemNames
let stems = pm.source == .Stems
GeometryReader { geo in
// Spread spare height across the rows (stems + master) instead of leaving it all at the bottom.
let rowH = min(62, max(46, (geo.size.height - 80) / CGFloat(names.count + 1)))
ScrollView {
VStack(alignment: .leading, spacing: 0) {
if pm.source == .Original {
@@ -622,21 +625,22 @@ private struct MixerPanel: View {
let levels = stems ? meters.step(pm.engine, names.count) : []
VStack(spacing: 0) {
ForEach(Array(names.enumerated()), id: \.element) { i, name in
StemFader(name: name, setting: pm.mix[name] ?? StemSetting(), level: i < levels.count ? levels[i] : 0, enabled: stems)
StemFader(name: name, setting: pm.mix[name] ?? StemSetting(), level: i < levels.count ? levels[i] : 0, enabled: stems, height: rowH)
}
}
}
p.separator.frame(height: 0.5).padding(.vertical, 6)
FaderRow(dot: nil, label: "Master", percent: Int(pm.master * 100), dim: false) {
FaderRow(dot: nil, label: "Master", percent: Int(pm.master * 100), dim: false, height: rowH) {
FaderSlider(value: pm.master, fill: p.label, onChange: pm.setMaster)
} trailing: { EmptyView() }
Spacer(minLength: 12)
Spacer(minLength: 16)
}
.padding(.horizontal, 24)
}
.scrollIndicators(.hidden)
}
}
}
/// [Chip] in the sheet's own colours: selected is the label colour, so it reads on any tint.
private struct PresetChip: View {
@@ -665,12 +669,13 @@ private struct StemFader: View {
let setting: StemSetting
let level: Float
let enabled: Bool
var height: CGFloat = 46
@Environment(\.palette) private var p
private var pm: PlaybackManager { AppGraph.shared.playback }
var body: some View {
let color = Color(hex: Stems.color(name))
FaderRow(dot: setting.muted ? p.label3 : color, label: Stems.label(name), percent: Int(stemTaper(setting.level) * 100), dim: setting.muted) {
FaderRow(dot: setting.muted ? p.label3 : color, label: Stems.label(name), percent: Int(stemTaper(setting.level) * 100), dim: setting.muted, height: height) {
VStack(spacing: 0) {
FaderSlider(value: setting.level, fill: setting.muted ? p.label3 : color, detent: StemSetting.unity, enabled: enabled) {
pm.setStemLevel(name, $0)
@@ -702,6 +707,7 @@ private struct FaderRow<Fader: View, Trailing: View>: View {
let label: String
let percent: Int
let dim: Bool
var height: CGFloat = 46
@ViewBuilder let fader: Fader
@ViewBuilder let trailing: Trailing
@Environment(\.palette) private var p
@@ -718,7 +724,7 @@ private struct FaderRow<Fader: View, Trailing: View>: View {
.frame(width: 36, alignment: .trailing)
trailing
}
.frame(height: 46)
.frame(height: height)
}
}
+2 -2
View File
@@ -6,8 +6,8 @@ options:
settings:
base:
SWIFT_VERSION: "5.0"
MARKETING_VERSION: "1.0.13"
CURRENT_PROJECT_VERSION: "14"
MARKETING_VERSION: "1.0.15"
CURRENT_PROJECT_VERSION: "16"
targets:
Stemify:
type: application
+9
View File
@@ -15,6 +15,15 @@
"tintColor": "#FA2D48",
"category": "entertainment",
"versions": [
{
"version": "1.0.14",
"buildVersion": "15",
"date": "2026-10-20T14:14:26-07:00",
"localizedDescription": "Reorderable tab bar (Songs, Albums, Artists, Genres, Playlists, Search, Downloads), AirPlay route picker in Now Playing, debug logging with log export in Settings, more room above the seeker. Unsigned IPA.",
"downloadURL": "https://git.iamchrisama.com/cdnutter/stemify/releases/download/ios-v1.0.14/stemify-1.0.14.ipa",
"size": 1625201,
"minOSVersion": "17.0"
},
{
"version": "1.0.13",
"buildVersion": "14",