mirror of
https://github.com/phranck/TUIkit.git
synced 2026-06-20 09:54:37 +00:00
refactor: Rename package from SwiftTUI to TUIKit
BREAKING CHANGE: Package name changed due to name collision with existing rensbreur/SwiftTUI package. Changes: - Rename package from SwiftTUI to TUIKit in Package.swift - Rename Sources/SwiftTUI to Sources/TUIKit - Rename Sources/SwiftTUIExample to Sources/TUIKitExample - Rename Tests/SwiftTUITests to Tests/TUIKitTests - Rename SwiftTUI.swift to TUIKit.swift - Update all imports: import SwiftTUI -> import TUIKit - Update all code references: SwiftTUI.renderToBuffer -> TUIKit.renderToBuffer - Update documentation comments - Rename swiftTUIVersion to tuiKitVersion All 181 tests passing.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// AppState.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// Global state management for the example app.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
// MARK: - Demo Page Enum
|
||||
|
||||
/// The available demo pages in the example app.
|
||||
enum DemoPage: Int, CaseIterable {
|
||||
case menu = 0
|
||||
case textStyles = 1
|
||||
case colors = 2
|
||||
case containers = 3
|
||||
case overlays = 4
|
||||
case layout = 5
|
||||
case buttons = 6
|
||||
}
|
||||
|
||||
// MARK: - App State
|
||||
|
||||
/// Global state for the example app.
|
||||
///
|
||||
/// This class manages the current page and menu selection.
|
||||
/// Changes trigger automatic re-renders via `AppState`.
|
||||
///
|
||||
/// Status bar items are now managed declaratively via the
|
||||
/// `.statusBarItems()` modifier in `ContentView`.
|
||||
final class ExampleAppState: @unchecked Sendable {
|
||||
static let shared = ExampleAppState()
|
||||
|
||||
/// The current page being displayed.
|
||||
var currentPage: DemoPage = .menu {
|
||||
didSet {
|
||||
AppState.shared.setNeedsRender()
|
||||
}
|
||||
}
|
||||
|
||||
/// The selected menu index.
|
||||
var menuSelection: Int = 0 {
|
||||
didSet { AppState.shared.setNeedsRender() }
|
||||
}
|
||||
|
||||
/// Binding for menu selection.
|
||||
var menuSelectionBinding: Binding<Int> {
|
||||
Binding(
|
||||
get: { self.menuSelection },
|
||||
set: { self.menuSelection = $0 }
|
||||
)
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// DemoSection.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// A reusable section component for organizing demo content.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
/// A section with a styled title and content.
|
||||
///
|
||||
/// Used to group related demo content with a yellow underlined title.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```swift
|
||||
/// DemoSection("Basic Features") {
|
||||
/// Text("Feature 1")
|
||||
/// Text("Feature 2")
|
||||
/// }
|
||||
/// ```
|
||||
struct DemoSection<Content: View>: View {
|
||||
let title: String
|
||||
let content: Content
|
||||
|
||||
init(_ title: String, @ViewBuilder content: () -> Content) {
|
||||
self.title = title
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text(title)
|
||||
.bold()
|
||||
.underline()
|
||||
.foregroundColor(.yellow)
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// HeaderView.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// A reusable header component for demo pages.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
/// A styled header with title on the left and version on the right.
|
||||
///
|
||||
/// Used at the top of each demo page to provide consistent branding
|
||||
/// and optional subtitle.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```swift
|
||||
/// HeaderView(
|
||||
/// title: "My Demo",
|
||||
/// subtitle: "An optional description"
|
||||
/// )
|
||||
/// ```
|
||||
struct HeaderView: View {
|
||||
let title: String
|
||||
let subtitle: String?
|
||||
|
||||
init(title: String, subtitle: String? = nil) {
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
Text(title)
|
||||
.bold()
|
||||
.foregroundColor(.cyan)
|
||||
Spacer()
|
||||
Text("TUIKit v\(tuiKitVersion)")
|
||||
.dim()
|
||||
}
|
||||
if let sub = subtitle {
|
||||
Text(sub)
|
||||
.dim()
|
||||
.italic()
|
||||
}
|
||||
Divider(character: "═")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// The main content view that routes between demo pages.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
// MARK: - Content View (Page Router)
|
||||
|
||||
/// The main content view that switches between pages.
|
||||
///
|
||||
/// This view acts as a router, displaying the appropriate demo page
|
||||
/// based on the current state. It uses the `.statusBarItems()` modifier
|
||||
/// to declaratively set context-sensitive status bar items.
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
let state = ExampleAppState.shared
|
||||
|
||||
// Show current page based on state
|
||||
pageContent(for: state.currentPage)
|
||||
.onKeyPress { event in
|
||||
switch event.key {
|
||||
case .escape:
|
||||
// ESC goes back to menu (or exits if already on menu)
|
||||
if state.currentPage != .menu {
|
||||
state.currentPage = .menu
|
||||
return true // Consumed
|
||||
}
|
||||
return false // Let default handler exit the app
|
||||
|
||||
default:
|
||||
return false // Let other handlers process
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func pageContent(for page: DemoPage) -> some View {
|
||||
switch page {
|
||||
case .menu:
|
||||
MainMenuPage()
|
||||
.statusBarItems {
|
||||
StatusBarItem(shortcut: Shortcut.arrowsUpDown, label: "nav")
|
||||
StatusBarItem(shortcut: Shortcut.enter, label: "select", key: .enter)
|
||||
StatusBarItem(shortcut: Shortcut.range("1", "6"), label: "jump")
|
||||
StatusBarItem(shortcut: Shortcut.quit, label: "quit")
|
||||
}
|
||||
case .textStyles:
|
||||
TextStylesPage()
|
||||
.statusBarItems(subPageItems)
|
||||
case .colors:
|
||||
ColorsPage()
|
||||
.statusBarItems(subPageItems)
|
||||
case .containers:
|
||||
ContainersPage()
|
||||
.statusBarItems(subPageItems)
|
||||
case .overlays:
|
||||
OverlaysPage()
|
||||
.statusBarItems(subPageItems)
|
||||
case .layout:
|
||||
LayoutPage()
|
||||
.statusBarItems(subPageItems)
|
||||
case .buttons:
|
||||
ButtonsPage()
|
||||
.statusBarItems(subPageItems)
|
||||
}
|
||||
}
|
||||
|
||||
/// Common status bar items for sub-pages.
|
||||
private var subPageItems: [any StatusBarItemProtocol] {
|
||||
[
|
||||
StatusBarItem(shortcut: Shortcut.escape, label: "back") {
|
||||
ExampleAppState.shared.currentPage = .menu
|
||||
},
|
||||
StatusBarItem(shortcut: Shortcut.arrowsUpDown, label: "scroll"),
|
||||
StatusBarItem(shortcut: Shortcut.quit, label: "quit")
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// ButtonsPage.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// Demonstrates button and focus system capabilities.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
/// Buttons and focus demo page.
|
||||
///
|
||||
/// Shows interactive button features including:
|
||||
/// - Different button styles (default, primary, success, destructive)
|
||||
/// - Disabled buttons
|
||||
/// - Plain style (no border)
|
||||
/// - ButtonRow for horizontal groups
|
||||
/// - Focus navigation with Tab
|
||||
struct ButtonsPage: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 1) {
|
||||
HeaderView(title: "Buttons & Focus Demo")
|
||||
|
||||
DemoSection("Button Styles") {
|
||||
HStack(spacing: 2) {
|
||||
Button("Default") {
|
||||
// Default style button action
|
||||
}
|
||||
Button("Primary", style: .primary) {
|
||||
// Primary button action
|
||||
}
|
||||
Button("Success", style: .success) {
|
||||
// Success button action
|
||||
}
|
||||
Button("Destructive", style: .destructive) {
|
||||
// Destructive button action
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DemoSection("Disabled Button") {
|
||||
HStack(spacing: 2) {
|
||||
Button("Enabled") { }
|
||||
Button("Disabled") { }.disabled()
|
||||
}
|
||||
}
|
||||
|
||||
DemoSection("Plain Style (No Border)") {
|
||||
HStack(spacing: 2) {
|
||||
Button("Link 1", style: .plain) { }
|
||||
Button("Link 2", style: .plain) { }
|
||||
}
|
||||
}
|
||||
|
||||
DemoSection("ButtonRow (Horizontal Group)") {
|
||||
ButtonRow(spacing: 3) {
|
||||
Button("Cancel") { }
|
||||
Button("Save", style: .primary) { }
|
||||
}
|
||||
}
|
||||
|
||||
DemoSection("Focus Navigation") {
|
||||
VStack {
|
||||
Text("Use [Tab] to move focus between buttons")
|
||||
.dim()
|
||||
Text("Use [Enter] or [Space] to press the focused button")
|
||||
.dim()
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// ColorsPage.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// Demonstrates color capabilities.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
/// Colors demo page.
|
||||
///
|
||||
/// Shows various color options including:
|
||||
/// - Standard ANSI colors (8 colors)
|
||||
/// - Bright colors (8 colors)
|
||||
/// - RGB colors (24-bit true color)
|
||||
/// - Semantic colors (primary, success, warning, error)
|
||||
struct ColorsPage: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 1) {
|
||||
HeaderView(title: "Colors Demo")
|
||||
|
||||
DemoSection("Standard ANSI Colors") {
|
||||
HStack(spacing: 2) {
|
||||
Text("Black").foregroundColor(.black).background(.white)
|
||||
Text("Red").foregroundColor(.red)
|
||||
Text("Green").foregroundColor(.green)
|
||||
Text("Yellow").foregroundColor(.yellow)
|
||||
}
|
||||
HStack(spacing: 2) {
|
||||
Text("Blue").foregroundColor(.blue)
|
||||
Text("Magenta").foregroundColor(.magenta)
|
||||
Text("Cyan").foregroundColor(.cyan)
|
||||
Text("White").foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
|
||||
DemoSection("Bright Colors") {
|
||||
HStack(spacing: 2) {
|
||||
Text("Bright Red").foregroundColor(.brightRed)
|
||||
Text("Bright Green").foregroundColor(.brightGreen)
|
||||
Text("Bright Yellow").foregroundColor(.brightYellow)
|
||||
Text("Bright Blue").foregroundColor(.brightBlue)
|
||||
}
|
||||
}
|
||||
|
||||
DemoSection("RGB Colors (24-bit)") {
|
||||
HStack(spacing: 2) {
|
||||
Text("Orange").foregroundColor(.rgb(255, 128, 0))
|
||||
Text("Pink").foregroundColor(.rgb(255, 105, 180))
|
||||
Text("Teal").foregroundColor(.rgb(0, 128, 128))
|
||||
Text("Purple").foregroundColor(.rgb(128, 0, 128))
|
||||
}
|
||||
}
|
||||
|
||||
DemoSection("Semantic Colors") {
|
||||
HStack(spacing: 2) {
|
||||
Text("Primary").foregroundColor(.primary)
|
||||
Text("Success").foregroundColor(.success)
|
||||
Text("Warning").foregroundColor(.warning)
|
||||
Text("Error").foregroundColor(.error)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// ContainersPage.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// Demonstrates container view capabilities.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
/// Container views demo page.
|
||||
///
|
||||
/// Shows various container views including:
|
||||
/// - Card (bordered container with padding)
|
||||
/// - Box (simple bordered container)
|
||||
/// - Panel (container with title in border)
|
||||
/// - All available border styles
|
||||
struct ContainersPage: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 1) {
|
||||
HeaderView(title: "Container Views Demo")
|
||||
|
||||
HStack(spacing: 2) {
|
||||
// Card example
|
||||
VStack(alignment: .leading) {
|
||||
Text("Card").bold().foregroundColor(.yellow)
|
||||
Card(borderStyle: .rounded, borderColor: .cyan) {
|
||||
Text("A Card view")
|
||||
Text("with padding").dim()
|
||||
}
|
||||
}
|
||||
|
||||
// Box example
|
||||
VStack(alignment: .leading) {
|
||||
Text("Box").bold().foregroundColor(.yellow)
|
||||
Box(.doubleLine, color: .green) {
|
||||
Text("Simple Box")
|
||||
}
|
||||
}
|
||||
|
||||
// Panel example
|
||||
VStack(alignment: .leading) {
|
||||
Text("Panel").bold().foregroundColor(.yellow)
|
||||
Panel("Info", borderStyle: .line, titleColor: .magenta) {
|
||||
Text("Title in border")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DemoSection("Border Styles") {
|
||||
HStack(spacing: 1) {
|
||||
Box(.line) { Text("line") }
|
||||
Box(.rounded) { Text("rounded") }
|
||||
Box(.doubleLine) { Text("double") }
|
||||
Box(.heavy) { Text("heavy") }
|
||||
Box(.block) { Text("block") }
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// LayoutPage.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// Demonstrates layout system capabilities.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
/// Layout system demo page.
|
||||
///
|
||||
/// Shows various layout options including:
|
||||
/// - VStack (vertical stacking)
|
||||
/// - HStack (horizontal stacking)
|
||||
/// - Spacer (flexible space)
|
||||
/// - Padding and frame modifiers
|
||||
struct LayoutPage: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 1) {
|
||||
HeaderView(title: "Layout System Demo")
|
||||
|
||||
DemoSection("VStack (Vertical)") {
|
||||
Box(.rounded, color: .brightBlack) {
|
||||
VStack(spacing: 0) {
|
||||
Text("Item 1")
|
||||
Text("Item 2")
|
||||
Text("Item 3")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DemoSection("HStack (Horizontal)") {
|
||||
Box(.rounded, color: .brightBlack) {
|
||||
HStack(spacing: 2) {
|
||||
Text("Left")
|
||||
Text("Center")
|
||||
Text("Right")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DemoSection("Spacer") {
|
||||
Box(.rounded, color: .brightBlack) {
|
||||
HStack {
|
||||
Text("Start")
|
||||
Spacer()
|
||||
Text("End")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DemoSection("Padding & Frame") {
|
||||
HStack(spacing: 2) {
|
||||
VStack {
|
||||
Text(".padding()").dim()
|
||||
Text("Padded")
|
||||
.padding(EdgeInsets(all: 1))
|
||||
.border(.line)
|
||||
}
|
||||
VStack {
|
||||
Text(".frame()").dim()
|
||||
Text("Framed")
|
||||
.frame(width: 15, alignment: .center)
|
||||
.border(.line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// MainMenuPage.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// The main menu page with navigation to all demos.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
/// The main menu page.
|
||||
///
|
||||
/// Displays a centered menu with all available demos and
|
||||
/// feature highlight boxes at the bottom.
|
||||
struct MainMenuPage: View {
|
||||
var body: some View {
|
||||
let state = ExampleAppState.shared
|
||||
|
||||
VStack(spacing: 1) {
|
||||
HeaderView(
|
||||
title: "TUIKit Example App",
|
||||
subtitle: "A SwiftUI-like framework for Terminal User Interfaces"
|
||||
)
|
||||
|
||||
Spacer(minLength: 1)
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
Menu(
|
||||
title: "Select a Demo",
|
||||
items: [
|
||||
MenuItem(label: "Text Styles", shortcut: "1"),
|
||||
MenuItem(label: "Colors", shortcut: "2"),
|
||||
MenuItem(label: "Container Views", shortcut: "3"),
|
||||
MenuItem(label: "Overlays & Modals", shortcut: "4"),
|
||||
MenuItem(label: "Layout System", shortcut: "5"),
|
||||
MenuItem(label: "Buttons & Focus", shortcut: "6")
|
||||
],
|
||||
selection: state.menuSelectionBinding,
|
||||
onSelect: { index in
|
||||
// Navigate to the selected page
|
||||
if let page = DemoPage(rawValue: index + 1) {
|
||||
state.currentPage = page
|
||||
}
|
||||
},
|
||||
selectedColor: .cyan,
|
||||
borderStyle: .rounded,
|
||||
borderColor: .brightBlack
|
||||
)
|
||||
Spacer()
|
||||
}
|
||||
|
||||
Spacer(minLength: 1)
|
||||
|
||||
// Feature highlights (centered)
|
||||
HStack {
|
||||
Spacer()
|
||||
HStack(spacing: 3) {
|
||||
featureBox("Pure Swift", "No ncurses")
|
||||
featureBox("Declarative", "SwiftUI-like")
|
||||
featureBox("Composable", "View protocol")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a small feature highlight box.
|
||||
private func featureBox(_ title: String, _ subtitle: String) -> some View {
|
||||
VStack {
|
||||
Text(title)
|
||||
.bold()
|
||||
.foregroundColor(.green)
|
||||
Text(subtitle)
|
||||
.dim()
|
||||
}
|
||||
.padding(EdgeInsets(horizontal: 2, vertical: 1))
|
||||
.border(.rounded, color: .brightBlack)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// OverlaysPage.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// Demonstrates overlay and modal capabilities.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
/// Overlays and modals demo page.
|
||||
///
|
||||
/// Shows the overlay system including:
|
||||
/// - `.overlay()` modifier
|
||||
/// - `.dimmed()` modifier
|
||||
/// - `.modal()` helper
|
||||
/// - Note: The status bar is NOT dimmed by modals!
|
||||
struct OverlaysPage: View {
|
||||
var body: some View {
|
||||
// Background content with modal overlay
|
||||
backgroundContent
|
||||
.modal {
|
||||
Alert(
|
||||
title: "Modal Alert",
|
||||
message: "This alert overlays dimmed content!",
|
||||
borderStyle: .rounded,
|
||||
borderColor: .yellow,
|
||||
titleColor: .yellow
|
||||
) {
|
||||
HStack(spacing: 3) {
|
||||
Text("[OK]").bold().foregroundColor(.green)
|
||||
Text("[Cancel]").foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var backgroundContent: some View {
|
||||
VStack(spacing: 1) {
|
||||
HeaderView(title: "Overlays & Modals Demo")
|
||||
|
||||
DemoSection("Overlay System Features") {
|
||||
Text("• .overlay() modifier - layer content on top")
|
||||
Text("• .dimmed() modifier - reduce visual emphasis")
|
||||
Text("• .modal() helper - combines dimmed + centered overlay")
|
||||
}
|
||||
|
||||
DemoSection("This page demonstrates a modal overlay") {
|
||||
Text("The content behind is dimmed automatically")
|
||||
Text("Note: The status bar is NOT dimmed!")
|
||||
.bold()
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// TextStylesPage.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// Demonstrates text styling capabilities.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
/// Text styles demo page.
|
||||
///
|
||||
/// Shows various text styling options including:
|
||||
/// - Basic styles (bold, italic, underline, etc.)
|
||||
/// - Combined styles
|
||||
/// - Special effects (blink, inverted)
|
||||
struct TextStylesPage: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 1) {
|
||||
HeaderView(title: "Text Styles Demo")
|
||||
|
||||
DemoSection("Basic Styles") {
|
||||
Text("Normal text - no styling applied")
|
||||
Text("Bold text").bold()
|
||||
Text("Italic text").italic()
|
||||
Text("Underlined text").underline()
|
||||
Text("Strikethrough text").strikethrough()
|
||||
Text("Dimmed text").dim()
|
||||
}
|
||||
|
||||
DemoSection("Combined Styles") {
|
||||
Text("Bold + Italic").bold().italic()
|
||||
Text("Bold + Underline").bold().underline()
|
||||
Text("Bold + Color").bold().foregroundColor(.cyan)
|
||||
Text("Italic + Dim").italic().dim()
|
||||
Text("All combined").bold().italic().underline().foregroundColor(.magenta)
|
||||
}
|
||||
|
||||
DemoSection("Special Effects") {
|
||||
Text("Blinking text (if terminal supports)").blink()
|
||||
Text("Inverted colors").inverted()
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// main.swift
|
||||
// TUIKitExample
|
||||
//
|
||||
// Entry point for the TUIKit example application.
|
||||
//
|
||||
// This app demonstrates TUIKit capabilities through various demo pages.
|
||||
// Use the menu to navigate between demos.
|
||||
//
|
||||
|
||||
import TUIKit
|
||||
|
||||
// MARK: - Main App
|
||||
|
||||
/// The main example application.
|
||||
struct ExampleApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the app
|
||||
ExampleApp.main()
|
||||
Reference in New Issue
Block a user