Skip to content

Instantly share code, notes, and snippets.

@michaelforrest
Last active July 7, 2025 10:24
Show Gist options
  • Save michaelforrest/e6b8c6bf65f1ecd97d2c7ca625676eb5 to your computer and use it in GitHub Desktop.
Save michaelforrest/e6b8c6bf65f1ecd97d2c7ca625676eb5 to your computer and use it in GitHub Desktop.
What we need for RealtimeSwift in CueCam
struct MainCameraView: RealtimeView{
var body: some RealtimeView {
RealtimeSourceView("Primary")
VideoPencilView(canvasID: canvasID)
}
}
struct LightboxView: RealtimeView{
var active: Bool
var body: some RealtimeView{
if active{
CameraView(camera, mode: settings.pictureInPictureMode)
VideoPencilView(canvasID: canvasID)
}
}
}
struct VideoPencilCameraReference: RealtimeView{
var body: some RealtimeView{
RealtimeSourceView("Primary")
}
}
struct EditorWindowView: RealtimeView{
var doc: Doc
var card: Card
var body: some RealtimeView{
PrimaryView()
.environment(card)
.injectingRealtimeSources(doc: doc, card: card)
}
}
enum CameraMode{
case fullScreen, aside3D
case pictureInPicture(Corner)
case chromaKeyOverlay(Edge)
}
enum ChromaKey{
case none
case regular(ChromaKeySettings)
case virtual(VirtualChromaKeySettings)
}
// sent to Video Pencil and used in Main Camera
@RealtimeSubrenderer("Primary") PrimaryView: RealtimeView{
var camera: Camera
var cameraMode: CameraMode
var card: Card?
var slide: Slide?
var body: some View{
if mode == .aside3D{
CameraView(camera.name, mode: .fullScreen)
}
Stack(axis: slide?.layout.axis, order: slide?.layout.order){
SlideView(slide: slide) // can be empty if slide is full screen
if let slide.presentedItem{
// picture in picture
slide.presentedItemView // could just be the slide if it's full screen
.overlay(CameraView(mode: camera.settings.overlayMode)
.overlay(VideoPencilView(canvasID: card.id)) // we'll need to cache each canvas' latest drawing on this side because Video Pencil only sends one.
.asideEffect(mode == .aside3D)
}else{
// side by side
CameraView(camera.name, mode: .fullScreen)
}
}
}
}
extension Slide{
@RealtimeViewBuilder var presentedItemView: some RealtimeView{
if layout == .fullScreen{
SlideView(slide: slide)
}else{
RealtimeSourceView(sharedContentName)
}
}
}
struct SlideView: RealtimeView{
var slide: Slide
var body: some RealtimeView{
// Basically SlideSourceView but with fixes.
}
}
struct CameraView: RealtimeView{
var sourceName: String
var settings: CameraSourceSettings
var mode: CameraMode
var body: some RealtimeView{
switch mode{
case .fullScreen, .aside3D:
if let cameraBackground = settings.background, settings.key != .none{
chromaKeyedFeed
.background(cameraBackground)
}else{
cameraFeed
}
case .pictureInPicture(let corner):
cameraFeed
.clipped(Circle())
.frame(width: 100, height: 100, alignment: corner)
case .chromaKeyOverlay(let edge):
chromaKeyedFeed
.frame(width: 300, alignment: edge)
}
}
var cameraFeed: some RealtimeView{
RealtimeSourceView(sourceName)
.lutFilter(settings.lut) // optional lut
}
var chromaKeyedFeed: some RealtimeView{
switch settings.key{
case none: cameraFeed
case regular(let keySettings):
RealtimeSourceView(sourceName)
.chromaKeyFilter(keySettings) // put the chroma key filter first
.lutFilter(settings.lut)
case virtual(let keySettings):
cameraFeed // include the lut
.virtualChromaKey(keySettings)
}
}
}
struct SlideView: RealtimeView{
var body: some View{
if slide.layout.isHorizontal{
lowerOrUpperThirdLayout
}else{
if settings.outputDimensions.isVertical{
VStack(spacing: 0){
regularLayout
}
}else{
HStack(spacing: 0) {
regularLayout
}
}
}
.frame(maxHeight: slide.layout.expandsVertically ? .infinity : nil)
.background{
if design.backgroundStyle == .union{
self.backgroundColor
}
}
.frame(width: outputSize.width * layoutScale, height: height, alignment: slide.layout.outerAlignment)
.foregroundColor(design.darkText ? .black : .white)
}
@RealtimeViewBuilder var regularLayout: some RealtimeView {
VStack(
alignment: slide.layout.innerAlignment(
hasImage: slide.hasInlineImages
),
spacing: elementSpacing
) {
if slide.layout == .center, !settings.outputDimensions.isVertical, slide.fragments.contains(where: {$0.fragmentType == .image}){
// show images first, in a row in the center layout
HStack(alignment: .center, spacing: 20 * layoutScale){
eachFragment(alignment: .center, centered: false) { $0.element.asSlideSyntax == .image}
}
eachFragment(alignment: .top, centered: slide.layout == .center) { $0.element.asSlideSyntax != .footnote && $0.element.asSlideSyntax != .image}
}else{
eachFragment(alignment: .top, centered: slide.layout == .center) { $0.element.asSlideSyntax != .footnote}
}
}
.multilineTextAlignment(slide.layout.innerAlignment(hasImage: slide.hasInlineImages).textAlignment)
.padding(20 * layoutScale)
.frame(maxHeight: slide.layout == .fullScreenEphemeral ? nil : .infinity)
.frame(width: contentWidth)
.background {
self.backgroundColor
.captured(id: .contentBackgroundZone, group: .contentBackgroundZone, fragmentType: .zoneOfInterest)
.handleTaps(element: .contentBackgroundZone)
}
}
@RealtimeViewBuilder var lowerOrUpperThirdLayout: some RealtimeView{
HStack(alignment: .center, spacing: 20 * layoutScale){
// CENTER IT
Spacer()
// IMAGE ON LEFT
eachFragment(alignment: .center, centered: false) { $0.element.asSlideSyntax == .image}
.frame(
width: lowerThirdTextFrame == nil ? nil : max(180 * layoutScale,lowerThirdTextFrame!.height),
height: lowerThirdTextFrame == nil ? nil : max(180 * layoutScale, lowerThirdTextFrame!.height)
)
// ALL TEXT CONTENT VERTICALLY
VStack(alignment: slide.fragments.filter({$0.fragmentType == .image}).count == 0 ? .center : .leading, spacing: 0) {
eachFragment(alignment: .center, centered: false, textScale: 0.8, filter: {$0.element.asSlideSyntax != .image && $0.element.asSlideSyntax != .footnote })
}
.frame(minHeight: 20 * layoutScale)
.trackingFrame(frame: $lowerThirdTextFrame, in: .global, mode: .height, label: "lowerOrUpperThirdLayout")
// CENTER IT
Spacer()
}
.padding(.vertical, 20 * layoutScale)
.frame(maxWidth: .infinity, minHeight: 40 * layoutScale)
.background {
self.backgroundColor
.captured(id: .contentBackgroundZone, group: .contentBackgroundZone, fragmentType: .zoneOfInterest)
.handleTaps(element: .contentBackgroundZone)
}
}
@RealtimeViewBuilder func eachFragment(fragments: [SlideFragment]?=nil, alignment: VerticalAlignment, centered: Bool, textScale: CGFloat=1.0, filter shouldInclude:((SlideFragment)->Bool)?=nil)->some View{
let fragments:[SlideFragment] = (fragments ?? slide.fragments).filter({ (shouldInclude != nil) ? shouldInclude?($0) == true : true})
ForEach(fragments){ fragment in
Group {
switch fragment.element {
case .title(let text, let level):
self.title(text, scale: SlideSyntax.titleScale(level) * textScale * slide.layout.textScale, centered: slide.layout == .center)
.layoutPriority(Double(level))
case .bullet(text: let text, bulletCharacter: let bulletCharacter):
self.bullet(text, bulletCharacter: bulletCharacter, alignment: alignment, centered: centered, bulletScale: slide.bulletScale)
.multilineTextAlignment(centered ? .center : .leading).fixedSize(horizontal: false, vertical: true)
case .image(let filename):
if let nsImage = document?.image(named: filename){
Image(nsImage: nsImage)
.resizable()
.aspectRatio(contentMode: .fit)
}else{
let _ = print("Could not find image named \(filename)")
}
case .quotation(let quotation):
self.quotation(quotation, centered: centered, fragment: fragment)
case .footnote(let footnote, let label):
self.footnote(text: footnote, label: label )
case .table(let rows):
self.table(rows: rows)
case .spacer:
Spacer()
}
}
.textCase(design.textCase)
.shadow(color: .black.opacity(design.darkText ? 0 : 0.95), radius: 0, x: shadowSize * layoutScale, y: shadowSize * layoutScale )
// .padding(shadowSize * 2 * layoutScale) // make sure we grab those shadows
// .drawingGroup(opaque: false)
.background(Color.white.opacity(0.001))
.padding(design.padding.multiplied(by: layoutScale))
.captured(id: fragment.id, group: .fragment, index: fragment.index, fragmentType: fragment.fragmentType)
.handleTaps(element: CapturedElementIdentifier(id: fragment.id, group: .fragment, index: fragment.index, fragmentType: fragment.fragmentType))
.background {
if slidePreview, let design = slide.design ?? document?.config.slideDesign, design != .clean, let backgroundColor = design.backgroundColor, fragment.fragmentType != .image{
Color(nsColor: backgroundColor.nsColor)
}
}
}
}
}
Stack(axis: axis, order: order){
SlideView(slide: slide)
if let slide.presentedItem{
slide.presentedItemView
.overlay(CameraView(pictureInPicture: .circle)
.overlay(VideoPencilView(canvasID: card.id))
.asideEffect(mode == .aside3D)
}else{
CameraView(camera.name, mode: .fullScreen)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment