Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/iOS/MNNLLMChat/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
MNN

# Preset-ASR assets are provisioned separately and must not enter Git.
libsherpa-mnn.a
MNNLLMiOS/LocalModel/preset_audio/
MNNLLMiOS/LocalModel/xasr-mnn-int8/
MNNLLMiOS/LocalModel/Qwen3.5-2B-MNN/
.DS_Store
MNNLLMiOS/LocalModel/Qwen3.5-2B/
_model_holding/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ struct MessageView: View {
attachmentsView(message)
}

if !message.text.isEmpty {
if !message.text.isEmpty, message.recording == nil {
textWithTimeView(message)
.font(Font(font))

Expand All @@ -175,11 +175,17 @@ struct MessageView: View {
}

if let recording = message.recording {
VStack(alignment: .trailing, spacing: 8) {
VStack(alignment: .leading, spacing: 10) {
recordingView(recording)
messageTimeView()
.padding(.bottom, 8)
.padding(.trailing, 12)
if !message.text.isEmpty {
audioEvidenceView(message.text)
}
HStack {
Spacer()
messageTimeView()
}
.padding(.bottom, 8)
.padding(.horizontal, MessageView.horizontalTextPadding)
}
}
}
Expand Down Expand Up @@ -350,6 +356,31 @@ struct MessageView: View {
.padding(.top, 8)
}

@ViewBuilder
func audioEvidenceView(_ text: String) -> some View {
let lines = text.split(separator: "\n", maxSplits: 1, omittingEmptySubsequences: true)
VStack(alignment: .leading, spacing: 4) {
if let transcript = lines.first {
Text(String(transcript))
.font(.caption)
.foregroundColor(message.user.isCurrentUser
? theme.colors.messageMyText.opacity(0.86)
: theme.colors.messageFriendText.opacity(0.86))
.fixedSize(horizontal: false, vertical: true)
}
if lines.count > 1 {
Text(String(lines[1]))
.font(.caption2.monospacedDigit())
.foregroundColor(message.user.isCurrentUser
? theme.colors.messageMyText.opacity(0.62)
: theme.colors.messageFriendText.opacity(0.62))
.fixedSize(horizontal: false, vertical: true)
}
}
.padding(.horizontal, MessageView.horizontalTextPadding)
.accessibilityElement(children: .combine)
}

func messageTimeView(needsCapsule: Bool = false) -> some View {
Group {
if showMessageTimeView {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import SwiftUI

struct RecordWaveformWithButtons: View {

@Environment(\.chatTheme) private var theme

@StateObject var recordPlayer = RecordingPlayer()

// 160 is screen left-padding/right-padding and playButton's width.
Expand All @@ -30,21 +28,25 @@ struct RecordWaveformWithButtons: View {

var body: some View {
HStack(spacing: 12) {
Group {
if recordPlayer.playing {
theme.images.message.pauseAudio
.renderingMode(.template)
} else {
theme.images.message.playAudio
.renderingMode(.template)
}
}
.foregroundColor(colorButton)
.viewSize(40)
.circleBackground(colorButtonBg)
.onTapGesture {
Button {
recordPlayer.togglePlay(recording)
} label: {
Image(systemName: recordPlayer.playing ? "pause.fill" : "play.fill")
.font(.system(size: 17, weight: .semibold))
.foregroundColor(colorButton)
.offset(x: recordPlayer.playing ? 0 : 1)
.frame(width: 44, height: 44)
.background(Circle().fill(colorButtonBg))
.overlay {
Circle()
.stroke(colorButton.opacity(0.18), lineWidth: 1)
}
.shadow(color: Color.black.opacity(0.10), radius: 2, y: 1)
}
.buttonStyle(.plain)
.contentShape(Circle())
.accessibilityLabel(recordPlayer.playing ? "暂停音频" : "播放音频")
.accessibilityHint("播放或暂停样例音频")

VStack(alignment: .leading, spacing: 5) {
RecordWaveformPlaying(samples: recording.waveformSamples, progress: recordPlayer.progress, color: colorWaveform, addExtraDots: false) { progress in
Expand Down
13 changes: 0 additions & 13 deletions apps/iOS/MNNLLMChat/LocalPackages/swift-cmark/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ let package = Package(
.library(
name: "cmark-gfm-extensions",
targets: ["cmark-gfm-extensions"]),
.executable(
name: "cmark-gfm-bin",
targets: ["cmark-gfm-bin"]),
.executable(name: "api_test",
targets: ["api_test"])
],
Expand All @@ -54,16 +51,6 @@ let package = Package(
],
cSettings: cSettings
),
.target(name: "cmark-gfm-bin",
dependencies: [
"cmark-gfm",
"cmark-gfm-extensions",
],
path: "bin",
sources: [
"main.c",
]
),
.target(name: "api_test",
dependencies: [
"cmark-gfm",
Expand Down
24 changes: 24 additions & 0 deletions apps/iOS/MNNLLMChat/MNNLLMiOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
3ECE19C72E9CE1BD001A4CDD /* TextFileActivityItemSource.swift in MNNChat */ = {isa = PBXBuildFile; fileRef = 3ECE19C62E9CE1BD001A4CDD /* TextFileActivityItemSource.swift */; };
3ECE19C92E9CE1F0001A4CDD /* BatchFileTestService.swift in MNNChat */ = {isa = PBXBuildFile; fileRef = 3ECE19C82E9CE1F0001A4CDD /* BatchFileTestService.swift */; };
3E000000000000000000A102 /* PresetPrompts.swift in MNNChat */ = {isa = PBXBuildFile; fileRef = 3E000000000000000000A101 /* PresetPrompts.swift */; };
5A000000000000000000A107 /* PresetZipformerASRService.swift in MNNChat */ = {isa = PBXBuildFile; fileRef = 5A000000000000000000A101 /* PresetZipformerASRService.swift */; };
5A000000000000000000A104 /* PresetZipformerASRWrapper.mm in MNNChat */ = {isa = PBXBuildFile; fileRef = 5A000000000000000000A103 /* PresetZipformerASRWrapper.mm */; };
5A000000000000000000A106 /* libsherpa-mnn.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A000000000000000000A105 /* libsherpa-mnn.a */; };
3EEAB30B2E8BD5550039DF50 /* BatchFileTestViewModel.swift in MNNChat */ = {isa = PBXBuildFile; fileRef = 3EEAB30A2E8BD5550039DF50 /* BatchFileTestViewModel.swift */; };
4230C2D22E7D4EB5001169DF /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4230C2A22E7D4EB5001169DF /* Preview Assets.xcassets */; };
4230C2DB2E7D4EB5001169DF /* mock.json in Resources */ = {isa = PBXBuildFile; fileRef = 4230C2AD2E7D4EB5001169DF /* mock.json */; };
Expand Down Expand Up @@ -156,6 +159,10 @@
3ECE19C62E9CE1BD001A4CDD /* TextFileActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFileActivityItemSource.swift; sourceTree = "<group>"; };
3ECE19C82E9CE1F0001A4CDD /* BatchFileTestService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchFileTestService.swift; sourceTree = "<group>"; };
3E000000000000000000A101 /* PresetPrompts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetPrompts.swift; sourceTree = "<group>"; };
5A000000000000000000A101 /* PresetZipformerASRService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetZipformerASRService.swift; sourceTree = "<group>"; };
5A000000000000000000A102 /* PresetZipformerASRWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PresetZipformerASRWrapper.h; sourceTree = "<group>"; };
5A000000000000000000A103 /* PresetZipformerASRWrapper.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PresetZipformerASRWrapper.mm; sourceTree = "<group>"; };
5A000000000000000000A105 /* libsherpa-mnn.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsherpa-mnn.a; sourceTree = "<group>"; };
3EEAB30A2E8BD5550039DF50 /* BatchFileTestViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchFileTestViewModel.swift; sourceTree = "<group>"; };
4230C22C2E7D4EB5001169DF /* ChatInteractorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInteractorProtocol.swift; sourceTree = "<group>"; };
4230C22D2E7D4EB5001169DF /* LLMChatInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LLMChatInteractor.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -284,6 +291,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5A000000000000000000A106 /* libsherpa-mnn.a in Frameworks */,
3ECAC6062F3C62570003C496 /* ExyteChat in Frameworks */,
3E2F97302E89158600E61F35 /* ExyteChat in Frameworks */,
3E2F972F2E89158600E61F35 /* ExyteChat in Frameworks */,
Expand All @@ -303,6 +311,7 @@
isa = PBXGroup;
children = (
3E440F082EE2DEDE00BB15FF /* AudioPlaybackManager.swift */,
5A000000000000000000A101 /* PresetZipformerASRService.swift */,
);
path = Audio;
sourceTree = "<group>";
Expand Down Expand Up @@ -332,6 +341,7 @@
isa = PBXGroup;
children = (
3E301C682D5C84730045E5E1 /* MNN.framework */,
5A000000000000000000A105 /* libsherpa-mnn.a */,
);
name = Frameworks;
sourceTree = "<group>";
Expand Down Expand Up @@ -458,6 +468,8 @@
4230C2522E7D4EB5001169DF /* InferenceEngine */ = {
isa = PBXGroup;
children = (
5A000000000000000000A102 /* PresetZipformerASRWrapper.h */,
5A000000000000000000A103 /* PresetZipformerASRWrapper.mm */,
3E2B293A2F1F7C28006DE361 /* SanaDiffusionSession.h */,
3E2B293B2F1F7C28006DE361 /* SanaDiffusionSession.mm */,
4230C24E2E7D4EB5001169DF /* DiffusionSession.h */,
Expand Down Expand Up @@ -890,6 +902,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5A000000000000000000A107 /* PresetZipformerASRService.swift in MNNChat */,
5A000000000000000000A104 /* PresetZipformerASRWrapper.mm in MNNChat */,
4230C2DF2E7D4EB5001169DF /* ChatInteractorProtocol.swift in MNNChat */,
4230C2E02E7D4EB5001169DF /* LLMChatInteractor.swift in MNNChat */,
4230C2E12E7D4EB5001169DF /* ThinkResultProcessor.swift in MNNChat */,
Expand Down Expand Up @@ -1175,6 +1189,11 @@
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 17;
HEADER_SEARCH_PATHS = "$(SRCROOT)/../../frameworks/sherpa-mnn";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 14;
Expand Down Expand Up @@ -1232,6 +1251,11 @@
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 17;
HEADER_SEARCH_PATHS = "$(SRCROOT)/../../frameworks/sherpa-mnn";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 14;
Expand Down
Loading