Skip to content

Commit

Permalink
Initial setup of repository (#2)
Browse files Browse the repository at this point in the history
# Initial setup of repository

## ♻️ Current situation & Problem
Currently, the repo is mostly empty except for a template package.

## ⚙️ Release Notes 
- Initial setup of the `SpeziChat` repo which previously was a target in
the [SpeziML](https://github.com/StanfordSpezi/SpeziML) repo
- Improved README and documentation via DocC


## 📚 Documentation
--


## ✅ Testing
UI Tests are included


## 📝 Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md):
- [X] I agree to follow the [Code of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md).
  • Loading branch information
philippzagar authored Nov 20, 2023
1 parent 0e0ff11 commit b8f84de
Show file tree
Hide file tree
Showing 38 changed files with 1,041 additions and 213 deletions.
14 changes: 12 additions & 2 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,19 @@ jobs:
path: 'Tests/UITests'
scheme: TestApp
artifactname: TestApp.xcresult
ipados:
name: Build and Test iPadOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
runsonlabels: '["macOS", "self-hosted"]'
path: 'Tests/UITests'
scheme: TestApp
resultBundle: TestAppiPadOS.xcresult
destination: 'platform=iOS Simulator,name=iPad mini (6th generation)'
artifactname: TestAppiPadOS.xcresult
uploadcoveragereport:
name: Upload Coverage Report
needs: [packageios, ios]
needs: [packageios, ios, ipados]
uses: StanfordSpezi/.github/.github/workflows/create-and-upload-coverage-report.yml@v2
with:
coveragereports: SpeziChat.xcresult TestApp.xcresult
coveragereports: SpeziChat.xcresult TestApp.xcresult TestAppiPadOS.xcresult
2 changes: 1 addition & 1 deletion .spi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ builder:
configs:
- platform: ios
documentation_targets:
- SpeziChat
- SpeziChat
6 changes: 6 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@ authors:
- family-names: "Schmiedmayer"
given-names: "Paul"
orcid: "https://orcid.org/0000-0002-8607-9148"
- family-names: "Philipp"
given-names: "Zagar"
orcid: "https://orcid.org/0009-0001-5934-2078"
- family-names: "Adrit"
given-names: "Rao"
orcid: "https://orcid.org/0000-0002-0780-033X"
title: "SpeziChat"
url: "https://github.com/StanfordSpezi/SpeziChat"
3 changes: 3 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ SpeziChat contributors
====================

* [Paul Schmiedmayer](https://github.com/PSchmiedmayer)
* [Vishnu Ravi](https://github.com/vishnuravi)
* [Philipp Zagar](https://github.com/philippzagar)
* [Adrit Rao](https://github.com/AdritRao)
7 changes: 2 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,13 @@ let package = Package(
.library(name: "SpeziChat", targets: ["SpeziChat"])
],
dependencies: [
.package(url: "https://github.com/StanfordSpezi/Spezi", .upToNextMinor(from: "0.8.0"))
.package(url: "https://github.com/StanfordSpezi/SpeziSpeech", .upToNextMinor(from: "0.1.0"))
],
targets: [
.target(
name: "SpeziChat",
dependencies: [
.product(name: "Spezi", package: "Spezi")
],
resources: [
.process("Resources")
.product(name: "SpeziSpeechRecognizer", package: "SpeziSpeech")
]
),
.testTarget(
Expand Down
103 changes: 81 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,63 +8,122 @@ SPDX-License-Identifier: MIT
-->

# SpeziChat
# Spezi Chat

[![Build and Test](https://github.com/StanfordSpezi/SpeziChat/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/StanfordSpezi/SpeziChat/actions/workflows/build-and-test.yml)
[![codecov](https://codecov.io/gh/StanfordSpezi/SpeziChat/graph/badge.svg?token=b2Dn0r9eo6)](https://codecov.io/gh/StanfordSpezi/SpeziChat)


Enable applications to connect to Chat devices.
Provides UI components for building chat-based applications.


## Overview

...
The `SpeziChat` module provides views that can be used to implement chat-based use cases, such as a message view or a voice input field.

|![Screenshot displaying the regular chat view.](Sources/SpeziChat/SpeziChat.docc/Resources/ChatView.png#gh-light-mode-only) ![Screenshot displaying the regular chat view.](Sources/SpeziChat/SpeziChat.docc/Resources/ChatView~dark.png#gh-dark-mode-only)|![Screenshot displaying the text input chat view.](Sources/SpeziChat/SpeziChat.docc/Resources/ChatView+TextInput.png#gh-light-mode-only) ![Screenshot displaying the text input chat view.](Sources/SpeziChat/SpeziChat.docc/Resources/ChatView+TextInput~dark.png#gh-dark-mode-only)|![Screenshot displaying the voice input chat view.](Sources/SpeziChat/SpeziChat.docc/Resources/ChatView+VoiceInput.png#gh-light-mode-only) ![Screenshot displaying the voice input chat view.](Sources/SpeziChat/SpeziChat.docc/Resources/ChatView+VoiceInput~dark.png#gh-dark-mode-only)
|:--:|:--:|:--:|
|[`ChatView`](https://swiftpackageindex.com/stanfordspezi/spezichat/documentation/spezichat/chatview)|[`ChatView`](https://swiftpackageindex.com/stanfordspezi/spezichat/documentation/spezichat/chatview)|[`ChatView`](https://swiftpackageindex.com/stanfordspezi/spezichat/documentation/spezichat/chatview)|


## Setup


### 1. Add Spezi Chat as a Dependency
### Add Spezi Chat as a Dependency

You need to add the Spezi Chat Swift package to
[your app in Xcode](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app#) or
[Swift package](https://developer.apple.com/documentation/xcode/creating-a-standalone-swift-package-with-xcode#Add-a-dependency-on-another-Swift-package).

> [!IMPORTANT]
> If your application is not yet configured to use Spezi, follow the [Spezi setup article](https://swiftpackageindex.com/stanfordspezi/spezi/documentation/spezi/initial-setup) setup the core Spezi infrastructure.
> If your application is not yet configured to use Spezi, follow the [Spezi setup article](https://swiftpackageindex.com/stanfordspezi/spezi/documentation/spezi/initial-setup) to setup the core Spezi infrastructure.
## Examples

### Chat View

### 2. Register the Module
The [`ChatView`](https://swiftpackageindex.com/stanfordspezi/spezichat/documentation/spezichat/chatview) provides a basic reusable chat view which includes a message input field. The input can be either typed out via the iOS keyboard or provided as voice input and transcribed into written text.

The `Chat` module needs to be registered in a Spezi-based application using the
[`configuration`](https://swiftpackageindex.com/stanfordspezi/spezi/documentation/spezi/speziappdelegate/configuration) in a
[`SpeziAppDelegate`](https://swiftpackageindex.com/stanfordspezi/spezi/documentation/spezi/speziappdelegate):
```swift
class ExampleAppDelegate: SpeziAppDelegate {
override var configuration: Configuration {
Configuration {
Chat()
// ...
}
struct ChatTestView: View {
@State private var chat: Chat = [
ChatEntity(role: .assistant, content: "Assistant Message!")
]


var body: some View {
ChatView($chat)
.navigationTitle("SpeziChat")
}
}
```

> [!NOTE]
> You can learn more about a [`Module` in the Spezi documentation](https://swiftpackageindex.com/stanfordspezi/spezi/documentation/spezi/module).
### Messages View

The [`MessagesView`](https://swiftpackageindex.com/stanfordspezi/spezichat/documentation/spezichat/messagesview) displays a `Chat` containing multiple `ChatEntity`s with different `ChatEntity/Role`s in a typical chat-like fashion.
The `View` automatically scrolls down to the newest message that is added to the passed `Chat` SwiftUI `Binding`.

```swift
struct MessagesViewTestView: View {
@State private var chat: Chat = [
ChatEntity(role: .user, content: "User Message!"),
ChatEntity(role: .assistant, content: "Assistant Message!")
]


var body: some View {
MessagesView($chat)
}
}
```

## Example
### Message View

...
The [`MessageView`](https://swiftpackageindex.com/stanfordspezi/spezichat/documentation/spezichat/messageview) is a reusable SwiftUI `View` to display the contents of a `ChatEntity` within a typical chat message bubble. This bubble is properly aligned according to the associated `ChatEntity/Role`.

```swift
...
struct MessageViewTestView: View {
var body: some View {
VStack {
MessageView(ChatEntity(role: .user, content: "User Message!"))
MessageView(ChatEntity(role: .assistant, content: "Assistant Message!"))
MessageView(ChatEntity(role: .system, content: "System Message (hidden)!"))
}
.padding()
}
}
```

For more information, please refer to the API documentation.
### MessageInput View

The [`MessageInputView`](https://swiftpackageindex.com/stanfordspezi/spezichat/documentation/spezichat/messageinputview) is a reusable SwiftUI `View` to handle text-based or speech-based user input. The provided message is attached to the passed `Chat` via a SwiftUI `Binding`.

```swift
struct MessageInputTestView: View {
@State private var chat: Chat = []
@State private var disableInput = false
@State private var messageInputHeight: CGFloat = 0


var body: some View {
VStack {
Spacer()
MessageInputView($chat, messagePlaceholder: "TestMessage")
.disabled(disableInput)
/// Get the height of the `MessageInputView` via a SwiftUI `PreferenceKey`
.onPreferenceChange(MessageInputViewHeightKey.self) { newValue in
messageInputHeight = newValue
}
}
}
}
```

For more information, please refer to the [API documentation](https://swiftpackageindex.com/StanfordSpezi/SpeziChat/documentation).

## Applications using Spezi Chat

[HealthGPT](https://github.com/StanfordBDHG/HealthGPT) and [LLMonFHIR](https://github.com/StanfordBDHG/LLMonFHIR) provide a great starting points and examples using the `SpeziChat` module.

## Contributing

Expand All @@ -73,7 +132,7 @@ Contributions to this project are welcome. Please make sure to read the [contrib

## License

This project is licensed under the MIT License. See [Licenses](https://github.com/StanfordSpezi/SpeziContact/tree/main/LICENSES) for more information.
This project is licensed under the MIT License. See [Licenses](https://github.com/StanfordSpezi/SpeziChat/tree/main/LICENSES) for more information.

![Spezi Footer](https://raw.githubusercontent.com/StanfordSpezi/.github/main/assets/FooterLight.png#gh-light-mode-only)
![Spezi Footer](https://raw.githubusercontent.com/StanfordSpezi/.github/main/assets/FooterDark.png#gh-dark-mode-only)
42 changes: 0 additions & 42 deletions Sources/SpeziChat/Chat.swift

This file was deleted.

91 changes: 91 additions & 0 deletions Sources/SpeziChat/ChatView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//
// This source file is part of the Stanford Spezi open source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import SwiftUI


/// Provides a basic reusable chat view which includes a message input field. The input can be either typed out via the iOS keyboard or provided as voice input and transcribed into written text.
///
/// The actual content of the ``ChatView`` is defined by a ``Chat``, which contains an ordered array of ``ChatEntity``s representing the individual messages within the ``ChatView``.
/// The ``Chat`` is passed to the ``ChatView`` as a SwiftUI `Binding`, which enables modification of the ``Chat`` from outside of the view, for example via a SwiftUI `.onChange()` `View` modifier.
///
///
/// ```swift
/// struct ChatTestView: View {
/// @State private var chat: Chat = [
/// ChatEntity(role: .assistant, content: "Assistant Message!")
/// ]
///
/// var body: some View {
/// ChatView($chat)
/// .navigationTitle("SpeziChat")
/// }
/// }
/// ```
public struct ChatView: View {
@Binding var chat: Chat
@Binding var disableInput: Bool
let messagePlaceholder: String?

@State var messageInputHeight: CGFloat = 0


public var body: some View {
ZStack {
VStack {
MessagesView($chat, bottomPadding: $messageInputHeight)
.gesture(
TapGesture().onEnded {
UIApplication.shared.sendAction(
#selector(UIResponder.resignFirstResponder),
to: nil,
from: nil,
for: nil
)
}
)
}
VStack {
Spacer()
MessageInputView($chat, messagePlaceholder: messagePlaceholder)
.disabled(disableInput)
.onPreferenceChange(MessageInputViewHeightKey.self) { newValue in
messageInputHeight = newValue
}
}
}
}


/// - Parameters:
/// - chat: The chat that should be displayed.
/// - disableInput: Flag if the input view should be disabled.
/// - messagePlaceholder: Placeholder text that should be added in the input field.
public init(
_ chat: Binding<Chat>,
disableInput: Binding<Bool> = .constant(false),
messagePlaceholder: String? = nil
) {
self._chat = chat
self._disableInput = disableInput
self.messagePlaceholder = messagePlaceholder
}
}


#Preview {
ChatView(.constant(
[
ChatEntity(role: .system, content: "System Message!"),
ChatEntity(role: .system, content: "System Message (hidden)!"),
ChatEntity(role: .user, content: "User Message!"),
ChatEntity(role: .assistant, content: "Assistant Message!"),
ChatEntity(role: .function, content: "Function Message!")
]
))
}
Loading

0 comments on commit b8f84de

Please sign in to comment.