Skip to content
Merged
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
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ jobs:
run: |
set -euo pipefail
swift package --allow-writing-to-directory ./public generate-documentation \
--target ImageForgeKit \
--target ImagePlaygroundBridge \
--disable-indexing \
--output-path ./public \
--transform-for-static-hosting \
--hosting-base-path imageforgekit
--hosting-base-path image-playground-bridge

cat > ./public/index.html <<'INDEX'
<!doctype html>
<meta http-equiv="refresh" content="0; url=/imageforgekit/documentation/imageforgekit/" />
<title>ImageForgeKit Documentation</title>
<meta http-equiv="refresh" content="0; url=/image-playground-bridge/documentation/imageplaygroundbridge/" />
<title>ImagePlaygroundBridge Documentation</title>
INDEX

- name: Upload Pages Artifact
Expand Down
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# ImageForgeKit
# ImagePlaygroundBridge

## Context

ImageForgeKit integrates Apple's system-managed Image Playground experience. Read [README.md](README.md) and the DocC catalog before changing public behavior.
ImagePlaygroundBridge integrates Apple's system-managed Image Playground experience. Read [README.md](README.md) and the DocC catalog before changing public behavior.

Keep application persistence, host media storage, and product-specific UI outside the package. Limit main-actor isolation to framework presentation boundaries and keep portable values `Sendable`.

Expand Down Expand Up @@ -96,9 +96,9 @@ Replace these examples with exact repository paths:

| Role | Physical folder |
|---|---|
| Package sources | `Sources/ImageForgeKit/` |
| DocC catalog | `Sources/ImageForgeKit/ImageForgeKit.docc/` |
| Unit tests | `Tests/ImageForgeKitTests/` |
| Package sources | `Sources/ImagePlaygroundBridge/` |
| DocC catalog | `Sources/ImagePlaygroundBridge/ImagePlaygroundBridge.docc/` |
| Unit tests | `Tests/ImagePlaygroundBridgeTests/` |

## Stack

Expand Down
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Changelog

All notable changes to ImageForgeKit are documented here.
All notable changes to ImagePlaygroundBridge are documented here.

## Unreleased

### Changed

- Rename the package, product, module, public API, documentation, and repository-facing links to ImagePlaygroundBridge.

### Added

- Bootstrapped the Swift package, DocC catalog, CI/CD workflows, shared AgentGuidelines integration, and repository policy.
Expand Down
12 changes: 6 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import PackageDescription

let package = Package(
name: "ImageForgeKit",
name: "ImagePlaygroundBridge",
platforms: [
.iOS(.v26),
.macOS(.v26),
Expand All @@ -12,8 +12,8 @@ let package = Package(
],
products: [
.library(
name: "ImageForgeKit",
targets: ["ImageForgeKit"]
name: "ImagePlaygroundBridge",
targets: ["ImagePlaygroundBridge"]
)
],
dependencies: [
Expand All @@ -22,14 +22,14 @@ let package = Package(
],
targets: [
.target(
name: "ImageForgeKit",
name: "ImagePlaygroundBridge",
dependencies: [
.product(name: "AppLogger", package: "applogger")
]
),
.testTarget(
name: "ImageForgeKitTests",
dependencies: ["ImageForgeKit"]
name: "ImagePlaygroundBridgeTests",
dependencies: ["ImagePlaygroundBridge"]
),
]
)
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
<a href="https://developer.apple.com/xcode/"><img alt="Xcode Version" src="https://img.shields.io/badge/Xcode-27-50ace8.svg?logo=xcode&logoColor=white"></a>
<a href="https://developer.apple.com/documentation/imageplayground"><img alt="Platforms" src="https://img.shields.io/badge/iOS%20%7C%20macOS%20%7C%20visionOS-26%2B-lightgrey.svg?logo=apple&logoColor=white"></a>
<a href="https://developer.apple.com/documentation/xcode/swift-packages"><img alt="SPM" src="https://img.shields.io/badge/SPM-ready-b68f6a.svg?logo=gitlfs&logoColor=white"></a>
<a href="https://thatfactory.github.io/imageforgekit/documentation/imageforgekit/"><img alt="DocC" src="https://img.shields.io/badge/DocC-documentation-0288D1.svg?logo=bookstack&logoColor=white"></a>
<a href="https://thatfactory.github.io/image-playground-bridge/documentation/imageplaygroundbridge/"><img alt="DocC" src="https://img.shields.io/badge/DocC-documentation-0288D1.svg?logo=bookstack&logoColor=white"></a>
<a href="https://en.wikipedia.org/wiki/MIT_License"><img alt="License" src="https://img.shields.io/badge/License-MIT-67ac5b.svg?logo=googledocs&logoColor=white"></a>
<a href="https://github.com/thatfactory/imageforgekit/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/thatfactory/imageforgekit/actions/workflows/ci.yml/badge.svg"></a>
<a href="https://github.com/thatfactory/imageforgekit/actions/workflows/release.yml"><img alt="Release" src="https://github.com/thatfactory/imageforgekit/actions/workflows/release.yml/badge.svg"></a>
<a href="https://github.com/thatfactory/image-playground-bridge/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/thatfactory/image-playground-bridge/actions/workflows/ci.yml/badge.svg"></a>
<a href="https://github.com/thatfactory/image-playground-bridge/actions/workflows/release.yml"><img alt="Release" src="https://github.com/thatfactory/image-playground-bridge/actions/workflows/release.yml/badge.svg"></a>
</p>

# ImageForgeKit
# ImagePlaygroundBridge

ImageForgeKit is a reusable Apple-platform integration layer for the system-managed Image Playground experience. It centralizes availability checks, concept setup, SwiftUI presentation wiring, cancellation, and temporary result handling while leaving persistence to the host application.
ImagePlaygroundBridge is a reusable Apple-platform integration layer for the system-managed Image Playground experience. It centralizes availability checks, concept setup, SwiftUI presentation wiring, cancellation, and temporary result handling while leaving persistence to the host application.

```swift
let request = ImageForgeRequest(concepts: [.text("a small red house")])
let request = ImagePlaygroundBridgeRequest(concepts: [.text("a small red house")])

content
.imageForgeSheet(
.imagePlaygroundBridgeSheet(
isPresented: $isPresented,
request: request,
onCompletion: { result in
Expand All @@ -26,11 +26,11 @@ content
)
```

The presentation API is available on Apple platforms that support Image Playground. The package also compiles on tvOS so shared multiplatform application targets can resolve it, while ``ImageForgeAvailability/current`` reports unavailable there.
The presentation API is available on Apple platforms that support Image Playground. The package also compiles on tvOS so shared multiplatform application targets can resolve it, while ``ImagePlaygroundBridgeAvailability/current`` reports unavailable there.

## Documentation

API documentation is published with DocC after a GitHub release. See the [ImageForgeKit documentation](https://thatfactory.github.io/imageforgekit/documentation/imageforgekit/).
API documentation is published with DocC after a GitHub release. See the [ImagePlaygroundBridge documentation](https://thatfactory.github.io/image-playground-bridge/documentation/imageplaygroundbridge/).

## Requirements

Expand Down
21 changes: 0 additions & 21 deletions Sources/ImageForgeKit/ImageForgeKit.docc/ImageForgeKit.md

This file was deleted.

2 changes: 0 additions & 2 deletions Sources/ImageForgeKit/ImageForgeKit.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ``ImagePlaygroundBridge``

Integrate Apple's system-managed Image Playground experience while keeping generated-image persistence in the host application.

## Overview

Use ``ImagePlaygroundBridgeAvailability/current`` to decide whether to offer generation, build an ``ImagePlaygroundBridgeRequest`` from portable ``ImagePlaygroundBridgeConcept`` values, and apply `SwiftUI/View/imagePlaygroundBridgeSheet(isPresented:request:onCompletion:onCancellation:)` to a host view.

Image Playground supplies ``ImagePlaygroundBridgeResult/temporaryURL`` only as temporary output. The completion callback is the host application's ownership boundary: copy the file into durable, application-owned storage before returning. ImagePlaygroundBridge never persists images, reads a photo library, or synchronizes media.

## Topics

### Availability and requests

- ``ImagePlaygroundBridgeAvailability``
- ``ImagePlaygroundBridgeConcept``
- ``ImagePlaygroundBridgeRequest``

### Presentation results

- ``ImagePlaygroundBridgeResult``
2 changes: 2 additions & 0 deletions Sources/ImagePlaygroundBridge/ImagePlaygroundBridge.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// A namespace for ImagePlaygroundBridge capabilities and package identity.
public enum ImagePlaygroundBridge {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Foundation
#endif

/// Describes whether the current device can present Image Playground.
public enum ImageForgeAvailability: Equatable, Sendable {
public enum ImagePlaygroundBridgeAvailability: Equatable, Sendable {
/// Image Playground can be presented.
case available

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// A portable concept used to seed the system Image Playground experience.
public enum ImageForgeConcept: Equatable, Sendable {
public enum ImagePlaygroundBridgeConcept: Equatable, Sendable {
/// Lets Image Playground extract useful concepts from prose.
case extracted(text: String, title: String? = nil)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import AppLogger

enum ImageForgeLogging {
enum ImagePlaygroundBridgeLogging {
static let emoji = "🎨"
static let subsystem = "com.thatfactory.imageforgekit"
static let subsystem = "com.thatfactory.imageplaygroundbridge"

enum Event: Equatable {
case cancelled
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Foundation

/// Input for an interactive Image Playground presentation.
public struct ImageForgeRequest: Equatable, Sendable {
public struct ImagePlaygroundBridgeRequest: Equatable, Sendable {
/// Concepts that seed the system-managed experience.
public var concepts: [ImageForgeConcept]
public var concepts: [ImagePlaygroundBridgeConcept]

/// Creates a request from one or more concepts.
public init(concepts: [ImageForgeConcept]) {
public init(concepts: [ImagePlaygroundBridgeConcept]) {
self.concepts = concepts
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// A generated image accepted by the user.
public struct ImageForgeResult: Equatable, Sendable {
public struct ImagePlaygroundBridgeResult: Equatable, Sendable {
/// A temporary file URL that the host must copy before returning from its callback.
public let temporaryURL: URL

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,32 @@
import SwiftUI

extension View {
/// Presents Image Playground and maps its temporary result into ImageForgeKit values.
/// Presents Image Playground and maps its temporary result into ImagePlaygroundBridge values.
///
/// Copy ``ImageForgeResult/temporaryURL`` into host-owned storage synchronously from
/// Copy ``ImagePlaygroundBridgeResult/temporaryURL`` into host-owned storage synchronously from
/// `onCompletion`. The system may remove the temporary file after the callback returns.
public func imageForgeSheet(
public func imagePlaygroundBridgeSheet(
isPresented: Binding<Bool>,
request: ImageForgeRequest,
onCompletion: @escaping (ImageForgeResult) -> Void,
request: ImagePlaygroundBridgeRequest,
onCompletion: @escaping (ImagePlaygroundBridgeResult) -> Void,
onCancellation: (() -> Void)? = nil
) -> some View {
imagePlaygroundSheet(
isPresented: isPresented,
concepts: request.concepts.map(\.imagePlaygroundConcept),
onCompletion: { temporaryURL in
ImageForgeLogging.emit(.completed)
onCompletion(ImageForgeResult(temporaryURL: temporaryURL))
ImagePlaygroundBridgeLogging.emit(.completed)
onCompletion(ImagePlaygroundBridgeResult(temporaryURL: temporaryURL))
},
onCancellation: {
ImageForgeLogging.emit(.cancelled)
ImagePlaygroundBridgeLogging.emit(.cancelled)
onCancellation?()
}
)
}
}

extension ImageForgeConcept {
extension ImagePlaygroundBridgeConcept {
fileprivate var imagePlaygroundConcept: ImagePlaygroundConcept {
switch self {
case .extracted(let text, let title):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import Foundation
import Testing

@testable import ImageForgeKit
@testable import ImagePlaygroundBridge

@Test("The package namespace is available")
func packageNamespaceIsAvailable() {
_ = ImageForgeKit.self
_ = ImagePlaygroundBridge.self
}

@Test("Requests preserve portable concepts")
func requestsPreservePortableConcepts() {
let request = ImageForgeRequest(
let request = ImagePlaygroundBridgeRequest(
concepts: [
.text("a small red house"),
.extracted(text: "A welcoming home", title: "House"),
Expand All @@ -29,11 +29,11 @@ func requestsPreservePortableConcepts() {
func resultsPreserveTemporaryURL() {
let url = URL(filePath: "/tmp/generated-image")

#expect(ImageForgeResult(temporaryURL: url).temporaryURL == url)
#expect(ImagePlaygroundBridgeResult(temporaryURL: url).temporaryURL == url)
}

@Test("Logging messages do not contain generated content")
func loggingMessagesArePrivacySafe() {
#expect(ImageForgeLogging.Event.cancelled.message == "🎨 playground | result=cancelled")
#expect(ImageForgeLogging.Event.completed.message == "🎨 playground | result=completed")
#expect(ImagePlaygroundBridgeLogging.Event.cancelled.message == "🎨 playground | result=cancelled")
#expect(ImagePlaygroundBridgeLogging.Event.completed.message == "🎨 playground | result=completed")
}