Files
TUIkit/Sources/TUIkitExample/Pages/ImageURLPage.swift
T
phranck b3d563040a Feat: Add Image view with ASCII art rendering, bracketed paste, and input filtering
- Add Image view rendering local files and URLs as colored ASCII art
- Add CSTBImage C target wrapping stb_image for cross-platform image decoding
- Add ASCIIConverter with block, ASCII, and braille character sets
- Support trueColor, ANSI-256, grayscale, and mono color modes
- Add Floyd-Steinberg dithering for improved visual quality
- Add async image loading with URLImageCache for URL sources
- Add bracketed paste mode for bulk text insertion in text fields
- Add TextContentType modifier for input character filtering
- Add ContentMode enum and aspectRatio(_:contentMode:) View modifier
- Add text-input priority in key dispatch to prevent shortcut conflicts
- Add Image (File) and Image (URL) demo pages to example app
- Update DocC documentation with new symbols and layout table
2026-02-14 00:43:22 +01:00

115 lines
3.8 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 🖥 TUIKit Terminal UI Kit for Swift
// ImageURLPage.swift
//
// Created by LAYERED.work
// License: MIT
import Foundation
import TUIkit
/// Image demo page for loading an image from a URL.
///
/// Provides a text field for entering an image URL. After pressing
/// Enter the image is downloaded and rendered. Status bar items allow
/// cycling through character set, color mode, and dithering settings.
struct ImageURLPage: View {
@State var imageURL: String = ""
@State var activeURL: String = ""
@State var charSetIndex: Int = 0
@State var colorModeIndex: Int = 0
@State var ditheringOn: Bool = false
var body: some View {
let charSet = Self.charSets[charSetIndex]
let colorMode = Self.colorModes[colorModeIndex]
let dithering: DitheringMode = ditheringOn ? .floydSteinberg : .none
VStack(alignment: .leading) {
HStack(spacing: 1) {
Text("URL:")
.foregroundStyle(.palette.foregroundSecondary)
TextField("Enter image URL...", text: $imageURL)
.onSubmit {
activeURL = imageURL
}
.textContentType(.url)
}
.padding(.bottom, 1)
if !activeURL.isEmpty {
HStack {
Spacer()
Image(.url(activeURL))
.imagePlaceholder("Downloading...")
.imagePlaceholderSpinner(true)
.border(color: .palette.border)
Spacer()
}
} else {
HStack {
Spacer()
Text("Press Enter to load the image")
.foregroundStyle(.palette.foregroundTertiary)
.italic()
Spacer()
}
Spacer()
}
Spacer()
}
.imageCharacterSet(charSet)
.imageColorMode(colorMode)
.imageDithering(dithering)
.statusBarItems(statusBarItems)
.appHeader {
HStack {
Text("Image (URL)").bold().foregroundStyle(.palette.accent)
Spacer()
Text("TUIkit v\(tuiKitVersion)").foregroundStyle(.palette.foregroundTertiary)
}
}
}
private var statusBarItems: [any StatusBarItemProtocol] {
[
StatusBarItem(shortcut: Shortcut.escape, label: "back"),
StatusBarItem(shortcut: "c", label: Self.charSetLabel(charSetIndex)) {
charSetIndex = (charSetIndex + 1) % Self.charSets.count
},
StatusBarItem(shortcut: "m", label: Self.colorModeLabel(colorModeIndex)) {
colorModeIndex = (colorModeIndex + 1) % Self.colorModes.count
},
StatusBarItem(shortcut: "d", label: ditheringOn ? "dither:on" : "dither:off") {
ditheringOn.toggle()
},
StatusBarItem(shortcut: Shortcut.arrowsUpDown, label: "scroll"),
]
}
}
// MARK: - Modifier Options
extension ImageURLPage {
static let charSets: [ASCIICharacterSet] = [.blocks, .ascii, .braille]
static let colorModes: [ASCIIColorMode] = [.trueColor, .ansi256, .grayscale, .mono]
static func charSetLabel(_ index: Int) -> String {
switch charSets[index] {
case .ascii: return "chars:ascii"
case .blocks: return "chars:blocks"
case .braille: return "chars:braille"
}
}
static func colorModeLabel(_ index: Int) -> String {
switch colorModes[index] {
case .trueColor: return "color:true"
case .ansi256: return "color:256"
case .grayscale: return "color:gray"
case .mono: return "color:mono"
}
}
}