Files
TUIkit/Sources/TUIkitExample/Pages/ImageFilePage.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

94 lines
3.1 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
// ImageFilePage.swift
//
// Created by LAYERED.work
// License: MIT
import Foundation
import TUIkit
/// Image demo page for loading an image from the local filesystem.
///
/// Displays a bundled demo image and provides status bar items to
/// cycle through character set, color mode, and dithering settings.
struct ImageFilePage: View {
@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 {
Spacer()
if let path = Bundle.module.path(forResource: "demo-image", ofType: "jpg", inDirectory: "Resources") {
Image(.file(path))
.imagePlaceholder("Loading image...")
.imagePlaceholderSpinner(true)
} else {
Text("Resource not found: demo-image.jpg")
.foregroundStyle(.error)
}
Spacer()
}
.padding(.bottom, 1)
Spacer()
}
.imageCharacterSet(charSet)
.imageColorMode(colorMode)
.imageDithering(dithering)
.statusBarItems(statusBarItems)
.appHeader {
HStack {
Text("Image (File)").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 ImageFilePage {
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"
}
}
}