From 4f3419415595fb7c8353f401e0bc661dc28c02f9 Mon Sep 17 00:00:00 2001 From: Dongyu Zhao Date: Fri, 8 Aug 2025 23:47:56 +0800 Subject: [PATCH 1/2] Setup Canvas framework --- Sources/CodeCanvas/CodeCanvas.swift | 35 ++++++------- .../CodeCanvas/Console/CodeCanvasTUI.swift | 7 +++ Sources/CodeCanvas/Document.swift | 10 ---- .../CodeCanvas/Models/CodeCanvasBench.swift | 27 ++++++++++ .../Models/CodeCanvasExtension.swift | 11 ++++ .../CodeCanvas/Models/CodeCanvasFile.swift | 11 ++++ Sources/CodeCanvas/TUI.swift | 13 ----- .../CodeCanvas/Views/CodeCanvasShell.swift | 39 ++++++++++++++ .../Views/Contents/CodeBenchContainer.swift | 51 +++++++++++++++++++ .../Contents/CodeInspectorContainer.swift | 15 ++++++ .../Views/Contents/CodeSpaceContainer.swift | 11 ++++ .../Views/Controls/CodeBenchSelector.swift | 43 ++++++++++++++++ .../CodeCanvasShowCase.swift | 46 ++++++++++++----- .../Extensions/ClientRestfulExtension.swift | 32 ++++++++++++ .../Extensions/CodeEditorExtension.swift | 33 ++++++++++++ Tests/CodeCanvasTests/CodeCanvasTests.swift | 20 ++++---- 16 files changed, 336 insertions(+), 68 deletions(-) create mode 100644 Sources/CodeCanvas/Console/CodeCanvasTUI.swift delete mode 100644 Sources/CodeCanvas/Document.swift create mode 100644 Sources/CodeCanvas/Models/CodeCanvasBench.swift create mode 100644 Sources/CodeCanvas/Models/CodeCanvasExtension.swift create mode 100644 Sources/CodeCanvas/Models/CodeCanvasFile.swift delete mode 100644 Sources/CodeCanvas/TUI.swift create mode 100644 Sources/CodeCanvas/Views/CodeCanvasShell.swift create mode 100644 Sources/CodeCanvas/Views/Contents/CodeBenchContainer.swift create mode 100644 Sources/CodeCanvas/Views/Contents/CodeInspectorContainer.swift create mode 100644 Sources/CodeCanvas/Views/Contents/CodeSpaceContainer.swift create mode 100644 Sources/CodeCanvas/Views/Controls/CodeBenchSelector.swift create mode 100644 Sources/CodeCanvasShowCase/Extensions/ClientRestfulExtension.swift create mode 100644 Sources/CodeCanvasShowCase/Extensions/CodeEditorExtension.swift diff --git a/Sources/CodeCanvas/CodeCanvas.swift b/Sources/CodeCanvas/CodeCanvas.swift index bd9df08..fb1eb3e 100644 --- a/Sources/CodeCanvas/CodeCanvas.swift +++ b/Sources/CodeCanvas/CodeCanvas.swift @@ -1,31 +1,26 @@ -import Foundation - #if canImport(SwiftUI) import SwiftUI -#endif -/// Public entry-points for the CodeCanvas module. -public enum CodeCanvasAPI { - /// Format the document text (placeholder using SwiftFormat later). - public static func format(_ doc: CodeDocument) -> CodeDocument { - // TODO: integrate real formatting via swift-format when needed. - return doc +public struct CodeCanvas: View { + private let extensions: [CodeCanvasExtension] + + public init(extensions: [CodeCanvasExtension]) { + self.extensions = extensions + } + + public var body: some View { + CodeCanvasShell(extensions: extensions) } } -#if canImport(SwiftUI) -/// A minimal SwiftUI-based editor view for Apple platforms. -public struct CodeCanvasView: View { - @State private var text: String +#else - public init(initialText: String = "// Welcome to CodeCanvas\n") { - self._text = State(initialValue: initialText) - } +public struct CodeCanvas { + public init() {} - public var body: some View { - TextEditor(text: $text) - .font(.system(.body, design: .monospaced)) - .padding() + public func start() { + CodeCanvasTUI().start() } } + #endif diff --git a/Sources/CodeCanvas/Console/CodeCanvasTUI.swift b/Sources/CodeCanvas/Console/CodeCanvasTUI.swift new file mode 100644 index 0000000..5658576 --- /dev/null +++ b/Sources/CodeCanvas/Console/CodeCanvasTUI.swift @@ -0,0 +1,7 @@ +import Foundation + +public struct CodeCanvasTUI { + public func start() { + + } +} diff --git a/Sources/CodeCanvas/Document.swift b/Sources/CodeCanvas/Document.swift deleted file mode 100644 index 8d617b5..0000000 --- a/Sources/CodeCanvas/Document.swift +++ /dev/null @@ -1,10 +0,0 @@ -import Foundation - -/// A minimal in-memory text document used by CodeCanvas. -public struct CodeDocument: Sendable, Equatable { - public var text: String - - public init(text: String = "") { - self.text = text - } -} diff --git a/Sources/CodeCanvas/Models/CodeCanvasBench.swift b/Sources/CodeCanvas/Models/CodeCanvasBench.swift new file mode 100644 index 0000000..b51f9f5 --- /dev/null +++ b/Sources/CodeCanvas/Models/CodeCanvasBench.swift @@ -0,0 +1,27 @@ +#if canImport(SwiftUI) +import Foundation +import SwiftUI + +public protocol CodeCanvasBenchAction { + var id: String { get } + var title: String { get } + var icon: String { get } + func perform(on bench: CodeCanvasBench) +} + +public protocol CodeCanvasBenchComponent{ + var id: String { get } + var title: String { get } + var icon: String { get } + var actions: [CodeCanvasBenchAction] { get } + func content() -> AnyView +} + +public protocol CodeCanvasBench { + var id: String { get } + var name: String { get } + var icon: String { get } + var components: [CodeCanvasBenchComponent] { get } +} + +#endif \ No newline at end of file diff --git a/Sources/CodeCanvas/Models/CodeCanvasExtension.swift b/Sources/CodeCanvas/Models/CodeCanvasExtension.swift new file mode 100644 index 0000000..0230ad7 --- /dev/null +++ b/Sources/CodeCanvas/Models/CodeCanvasExtension.swift @@ -0,0 +1,11 @@ +public struct CodeCanvasExtension { + public let name: String + public let icon: String + public let benches: [CodeCanvasBench] + + public init(name: String, icon: String, benches: [CodeCanvasBench]) { + self.name = name + self.icon = icon + self.benches = benches + } +} diff --git a/Sources/CodeCanvas/Models/CodeCanvasFile.swift b/Sources/CodeCanvas/Models/CodeCanvasFile.swift new file mode 100644 index 0000000..8febc4c --- /dev/null +++ b/Sources/CodeCanvas/Models/CodeCanvasFile.swift @@ -0,0 +1,11 @@ +public struct CodeCanvasFile { + public let id: String + public let name: String + public let content: String + + public init(id: String, name: String, content: String) { + self.id = id + self.name = name + self.content = content + } +} diff --git a/Sources/CodeCanvas/TUI.swift b/Sources/CodeCanvas/TUI.swift deleted file mode 100644 index ec4eab0..0000000 --- a/Sources/CodeCanvas/TUI.swift +++ /dev/null @@ -1,13 +0,0 @@ -#if !canImport(SwiftUI) -import Foundation - -/// Minimal TUI stub for non-Apple platforms (Linux/Windows). -public struct CodeCanvasTUI { - public init() {} - - public func start(with document: CodeDocument) { - // Placeholder TUI: just print the text for now. - print(document.text) - } -} -#endif diff --git a/Sources/CodeCanvas/Views/CodeCanvasShell.swift b/Sources/CodeCanvas/Views/CodeCanvasShell.swift new file mode 100644 index 0000000..356950d --- /dev/null +++ b/Sources/CodeCanvas/Views/CodeCanvasShell.swift @@ -0,0 +1,39 @@ +#if canImport(SwiftUI) + import SwiftUI + + public struct CodeCanvasShell: View { + private let extensions: [CodeCanvasExtension] + + @State private var showInspector = false + + public init(extensions: [CodeCanvasExtension]) { + self.extensions = extensions + } + + public var body: some View { + NavigationSplitView { + CodeBenchContainer(benches: extensions.flatMap { $0.benches }) // Pass the benches from the selected extension + } detail: { + CodeSpaceContainer() + } + .inspector( + isPresented: $showInspector, + content: { + CodeInspectorContainer() + #if os(macOS) + .inspectorColumnWidth(min: 340, ideal: 340, max: 680) + #endif + .toolbar { + Spacer() + Button(action: { + showInspector.toggle() + }) { + Label("Toggle Inspector", systemImage: "sidebar.right") + } + } + } + ) + } + } + +#endif diff --git a/Sources/CodeCanvas/Views/Contents/CodeBenchContainer.swift b/Sources/CodeCanvas/Views/Contents/CodeBenchContainer.swift new file mode 100644 index 0000000..e189bad --- /dev/null +++ b/Sources/CodeCanvas/Views/Contents/CodeBenchContainer.swift @@ -0,0 +1,51 @@ +#if canImport(SwiftUI) + import SwiftUI + + public struct CodeBenchContainer: View { + private let benches: [CodeCanvasBench] + @State private var selectedBench: (any CodeCanvasBench)? + + public init(benches: [CodeCanvasBench]) { + self.benches = benches + _selectedBench = State(initialValue: benches.first) + } + + public var body: some View { + HStack(spacing: 0) { + CodeBenchSelector(benches: benches) { bench in + selectedBench = bench + } + .frame(maxHeight: .infinity) + + if let selectedBench { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + ForEach(selectedBench.components, id: \.id) { component in + DisclosureGroup { + component.content() + } label: { + HStack { + Text(component.title).font(.headline) + Spacer() + ForEach(component.actions, id: \.id) { action in + Button { + action.perform(on: selectedBench) + } label: { + Image(systemName: action.icon) + } + .buttonStyle(.plain) + .help(action.title) + } + } + } + } + } + } + } else { + Spacer() + } + } + } +} + +#endif diff --git a/Sources/CodeCanvas/Views/Contents/CodeInspectorContainer.swift b/Sources/CodeCanvas/Views/Contents/CodeInspectorContainer.swift new file mode 100644 index 0000000..9b583ed --- /dev/null +++ b/Sources/CodeCanvas/Views/Contents/CodeInspectorContainer.swift @@ -0,0 +1,15 @@ +#if canImport(SwiftUI) + import SwiftUI + + public struct CodeInspectorContainer: View { + public init() {} + + public var body: some View { + VStack { + Spacer() + Text("Inspector") + Spacer() + } + } + } +#endif diff --git a/Sources/CodeCanvas/Views/Contents/CodeSpaceContainer.swift b/Sources/CodeCanvas/Views/Contents/CodeSpaceContainer.swift new file mode 100644 index 0000000..8a4a4bd --- /dev/null +++ b/Sources/CodeCanvas/Views/Contents/CodeSpaceContainer.swift @@ -0,0 +1,11 @@ +#if canImport(SwiftUI) + import SwiftUI + + public struct CodeSpaceContainer: View { + public init() {} + + public var body: some View { + Text("Canvas") + } + } +#endif diff --git a/Sources/CodeCanvas/Views/Controls/CodeBenchSelector.swift b/Sources/CodeCanvas/Views/Controls/CodeBenchSelector.swift new file mode 100644 index 0000000..1798a49 --- /dev/null +++ b/Sources/CodeCanvas/Views/Controls/CodeBenchSelector.swift @@ -0,0 +1,43 @@ +#if canImport(SwiftUI) +import SwiftUI + +public struct CodeBenchSelector: View { + private let benches: [CodeCanvasBench] + private let onSelect: (CodeCanvasBench) -> Void + + @State private var selectedID: String? + + public init(benches: [CodeCanvasBench], onSelect: @escaping (CodeCanvasBench) -> Void) { + self.benches = benches + self.onSelect = onSelect + self._selectedID = State(initialValue: benches.first?.id) + } + + public var body: some View { + VStack(spacing: 15) { + ForEach(benches, id: \.id) { bench in + Button(action: { + selectedID = bench.id + onSelect(bench) + }) { + Image(systemName: bench.icon) + .font(.title) + .symbolVariant(selectedID == bench.id ? .fill : .none) + .frame(width: 50, height: 50) + .foregroundColor(selectedID == bench.id ? .accentColor : .secondary) + .background( + Color.secondary.opacity(selectedID == bench.id ? 0.25 : 0) + ) + .clipShape(RoundedRectangle(cornerRadius: 8)) + } + .buttonStyle(.plain) + .help(bench.name) + } + Spacer() + } + .padding(.vertical) + .frame(width: 60) + } +} + +#endif diff --git a/Sources/CodeCanvasShowCase/CodeCanvasShowCase.swift b/Sources/CodeCanvasShowCase/CodeCanvasShowCase.swift index 81aec8e..db34fe8 100644 --- a/Sources/CodeCanvasShowCase/CodeCanvasShowCase.swift +++ b/Sources/CodeCanvasShowCase/CodeCanvasShowCase.swift @@ -1,24 +1,42 @@ -import Foundation import CodeCanvas +import Foundation #if canImport(SwiftUI) -import SwiftUI + import SwiftUI -@main -struct CodeCanvasShowCaseApp: App { + @main + struct CodeCanvasShowCaseApp: App { + #if os(macOS) + // Ensure the app shows in Dock, has menu bar, and supports full screen when launched as a SwiftPM executable + @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + #endif var body: some Scene { - WindowGroup { - CodeCanvasView(initialText: "// CodeCanvas Showcase\nprint(\"Hello, world!\")\n") - } + WindowGroup { + CodeCanvas( + extensions: [ + CodeCanvasExtension(name: "Editor", icon: "code", benches: [CodeEditorBench()]), + CodeCanvasExtension(name: "Restful", icon: "network", benches: [ClientRestfulBench()]), + ] + ) + } + } + } + #if os(macOS) + import AppKit + final class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ notification: Notification) { + // Switch activation policy to regular so we have Dock icon and menu bar + NSApp.setActivationPolicy(.regular) + NSApp.activate(ignoringOtherApps: true) + } } -} + #endif #else -@main -struct CodeCanvasShowCaseCLI { + @main + struct CodeCanvasShowCaseCLI { static func main() { - let doc = CodeDocument(text: "// CodeCanvas Showcase CLI\nprint(\"Hello, world!\")\n") - let tui = CodeCanvasTUI() - tui.start(with: doc) + let tui = CodeCanvas() + tui.start() } -} + } #endif diff --git a/Sources/CodeCanvasShowCase/Extensions/ClientRestfulExtension.swift b/Sources/CodeCanvasShowCase/Extensions/ClientRestfulExtension.swift new file mode 100644 index 0000000..6cb272c --- /dev/null +++ b/Sources/CodeCanvasShowCase/Extensions/ClientRestfulExtension.swift @@ -0,0 +1,32 @@ +#if canImport(SwiftUI) +import SwiftUI +import CodeCanvas + +public struct ClientRestfulTraceCollection: CodeCanvasBenchComponent { + public let id: String = UUID().uuidString + + public let title: String = "Traces" + + public let icon: String = "network" + + public var actions: [any CodeCanvasBenchAction] = [] + + public func content() -> AnyView { + AnyView( + VStack { + Text("No traces available") + .foregroundColor(.secondary) + .padding() + } + ) + } +} + +public struct ClientRestfulBench: CodeCanvasBench { + public let id: String = UUID().uuidString + public let name: String = "Restful Client" + public let icon: String = "network" + public let components: [CodeCanvasBenchComponent] = [ClientRestfulTraceCollection()] +} + +#endif diff --git a/Sources/CodeCanvasShowCase/Extensions/CodeEditorExtension.swift b/Sources/CodeCanvasShowCase/Extensions/CodeEditorExtension.swift new file mode 100644 index 0000000..d3c6ee1 --- /dev/null +++ b/Sources/CodeCanvasShowCase/Extensions/CodeEditorExtension.swift @@ -0,0 +1,33 @@ +#if canImport(SwiftUI) +import SwiftUI +import CodeCanvas + +public struct CodeCanvasBenchFileCollection: CodeCanvasBenchComponent { + + public let id: String = UUID().uuidString + + public let title: String = "File Collection" + + public let icon: String = "folder" + + public var actions: [any CodeCanvasBenchAction] = [] + + public func content() -> AnyView { + AnyView( + VStack { + Text("No files available") + .foregroundColor(.secondary) + .padding() + } + ) + } +} + +public struct CodeEditorBench: CodeCanvasBench { + public let id: String = UUID().uuidString + public let name: String = "Editor" + public let icon: String = "document" + public let components: [CodeCanvasBenchComponent] = [CodeCanvasBenchFileCollection(), CodeCanvasBenchFileCollection()] +} + +#endif diff --git a/Tests/CodeCanvasTests/CodeCanvasTests.swift b/Tests/CodeCanvasTests/CodeCanvasTests.swift index 7fdc579..a448a10 100644 --- a/Tests/CodeCanvasTests/CodeCanvasTests.swift +++ b/Tests/CodeCanvasTests/CodeCanvasTests.swift @@ -1,15 +1,13 @@ -import XCTest +import Testing @testable import CodeCanvas +#if canImport(SwiftUI) +import SwiftUI +#endif -final class CodeCanvasTests: XCTestCase { - func testDocumentInit() { - let d = CodeDocument(text: "hello") - XCTAssertEqual(d.text, "hello") - } +struct CodeCanvasTests { +#if canImport(SwiftUI) - func testFormatIsIdentityForNow() { - let d = CodeDocument(text: "print(\"hi\")") - let formatted = CodeCanvasAPI.format(d) - XCTAssertEqual(formatted, d) - } +#else + +#endif } From 7aa28a7a3fa17a181ae1ee8777bac6d67b2cee30 Mon Sep 17 00:00:00 2001 From: Dongyu Zhao Date: Sat, 9 Aug 2025 16:54:36 +0800 Subject: [PATCH 2/2] feat: add workspace state and plugin communication --- .../CodeCanvas/Models/CodeCanvasBench.swift | 2 +- .../Models/CodeCanvasExtension.swift | 2 + .../CodeCanvas/Models/CodeCanvasFile.swift | 2 +- .../Models/CodeCanvasWorkspace.swift | 7 +++ .../CodeCanvas/State/CodeCanvasStore.swift | 17 ++++++ .../CodeCanvas/Views/CodeCanvasShell.swift | 4 +- .../Views/Contents/CodeBenchContainer.swift | 2 + .../Views/Contents/CodeSpaceContainer.swift | 8 ++- .../Extensions/ClientRestfulExtension.swift | 45 ++++++++++++---- .../Extensions/CodeEditorExtension.swift | 54 ++++++++++++++----- 10 files changed, 117 insertions(+), 26 deletions(-) create mode 100644 Sources/CodeCanvas/Models/CodeCanvasWorkspace.swift create mode 100644 Sources/CodeCanvas/State/CodeCanvasStore.swift diff --git a/Sources/CodeCanvas/Models/CodeCanvasBench.swift b/Sources/CodeCanvas/Models/CodeCanvasBench.swift index b51f9f5..9e11b0b 100644 --- a/Sources/CodeCanvas/Models/CodeCanvasBench.swift +++ b/Sources/CodeCanvas/Models/CodeCanvasBench.swift @@ -24,4 +24,4 @@ public protocol CodeCanvasBench { var components: [CodeCanvasBenchComponent] { get } } -#endif \ No newline at end of file +#endif diff --git a/Sources/CodeCanvas/Models/CodeCanvasExtension.swift b/Sources/CodeCanvas/Models/CodeCanvasExtension.swift index 0230ad7..275b1ab 100644 --- a/Sources/CodeCanvas/Models/CodeCanvasExtension.swift +++ b/Sources/CodeCanvas/Models/CodeCanvasExtension.swift @@ -1,3 +1,4 @@ +#if canImport(SwiftUI) public struct CodeCanvasExtension { public let name: String public let icon: String @@ -9,3 +10,4 @@ public struct CodeCanvasExtension { self.benches = benches } } +#endif diff --git a/Sources/CodeCanvas/Models/CodeCanvasFile.swift b/Sources/CodeCanvas/Models/CodeCanvasFile.swift index 8febc4c..5e2d5d0 100644 --- a/Sources/CodeCanvas/Models/CodeCanvasFile.swift +++ b/Sources/CodeCanvas/Models/CodeCanvasFile.swift @@ -1,4 +1,4 @@ -public struct CodeCanvasFile { +public struct CodeCanvasFile: Identifiable { public let id: String public let name: String public let content: String diff --git a/Sources/CodeCanvas/Models/CodeCanvasWorkspace.swift b/Sources/CodeCanvas/Models/CodeCanvasWorkspace.swift new file mode 100644 index 0000000..352dae3 --- /dev/null +++ b/Sources/CodeCanvas/Models/CodeCanvasWorkspace.swift @@ -0,0 +1,7 @@ +#if canImport(SwiftUI) +import SwiftUI + +public protocol CodeCanvasWorkspace: Identifiable { + func content() -> AnyView +} +#endif diff --git a/Sources/CodeCanvas/State/CodeCanvasStore.swift b/Sources/CodeCanvas/State/CodeCanvasStore.swift new file mode 100644 index 0000000..59289c1 --- /dev/null +++ b/Sources/CodeCanvas/State/CodeCanvasStore.swift @@ -0,0 +1,17 @@ +#if canImport(SwiftUI) +import SwiftUI + +public final class CodeCanvasStore: ObservableObject { + @Published public var workspace: (any CodeCanvasWorkspace)? + + public init() {} + + public func open(_ workspace: any CodeCanvasWorkspace) { + self.workspace = workspace + } + + public func clear() { + workspace = nil + } +} +#endif diff --git a/Sources/CodeCanvas/Views/CodeCanvasShell.swift b/Sources/CodeCanvas/Views/CodeCanvasShell.swift index 356950d..e16ff31 100644 --- a/Sources/CodeCanvas/Views/CodeCanvasShell.swift +++ b/Sources/CodeCanvas/Views/CodeCanvasShell.swift @@ -5,6 +5,7 @@ private let extensions: [CodeCanvasExtension] @State private var showInspector = false + @StateObject private var store = CodeCanvasStore() public init(extensions: [CodeCanvasExtension]) { self.extensions = extensions @@ -12,10 +13,11 @@ public var body: some View { NavigationSplitView { - CodeBenchContainer(benches: extensions.flatMap { $0.benches }) // Pass the benches from the selected extension + CodeBenchContainer(benches: extensions.flatMap { $0.benches }) } detail: { CodeSpaceContainer() } + .environmentObject(store) .inspector( isPresented: $showInspector, content: { diff --git a/Sources/CodeCanvas/Views/Contents/CodeBenchContainer.swift b/Sources/CodeCanvas/Views/Contents/CodeBenchContainer.swift index e189bad..bc60aaa 100644 --- a/Sources/CodeCanvas/Views/Contents/CodeBenchContainer.swift +++ b/Sources/CodeCanvas/Views/Contents/CodeBenchContainer.swift @@ -4,6 +4,7 @@ public struct CodeBenchContainer: View { private let benches: [CodeCanvasBench] @State private var selectedBench: (any CodeCanvasBench)? + @EnvironmentObject private var store: CodeCanvasStore public init(benches: [CodeCanvasBench]) { self.benches = benches @@ -14,6 +15,7 @@ HStack(spacing: 0) { CodeBenchSelector(benches: benches) { bench in selectedBench = bench + store.clear() } .frame(maxHeight: .infinity) diff --git a/Sources/CodeCanvas/Views/Contents/CodeSpaceContainer.swift b/Sources/CodeCanvas/Views/Contents/CodeSpaceContainer.swift index 8a4a4bd..3746d01 100644 --- a/Sources/CodeCanvas/Views/Contents/CodeSpaceContainer.swift +++ b/Sources/CodeCanvas/Views/Contents/CodeSpaceContainer.swift @@ -2,10 +2,16 @@ import SwiftUI public struct CodeSpaceContainer: View { + @EnvironmentObject private var store: CodeCanvasStore + public init() {} public var body: some View { - Text("Canvas") + if let workspace = store.workspace { + workspace.content() + } else { + Text("Canvas") + } } } #endif diff --git a/Sources/CodeCanvasShowCase/Extensions/ClientRestfulExtension.swift b/Sources/CodeCanvasShowCase/Extensions/ClientRestfulExtension.swift index 6cb272c..693cae1 100644 --- a/Sources/CodeCanvasShowCase/Extensions/ClientRestfulExtension.swift +++ b/Sources/CodeCanvasShowCase/Extensions/ClientRestfulExtension.swift @@ -2,23 +2,49 @@ import SwiftUI import CodeCanvas +public struct ClientRestfulWorkspace: CodeCanvasWorkspace { + public let id = UUID().uuidString + let trace: String + + public func content() -> AnyView { + AnyView( + VStack { + Text("Restful Client") + .font(.headline) + Text(trace) + } + .padding() + ) + } +} + public struct ClientRestfulTraceCollection: CodeCanvasBenchComponent { public let id: String = UUID().uuidString - public let title: String = "Traces" - public let icon: String = "network" - public var actions: [any CodeCanvasBenchAction] = [] + private let traces: [String] + + public init(traces: [String] = ["GET /users", "POST /login"]) { + self.traces = traces + } + public func content() -> AnyView { - AnyView( - VStack { - Text("No traces available") - .foregroundColor(.secondary) - .padding() + AnyView(TraceListView(traces: traces)) + } + + struct TraceListView: View { + @EnvironmentObject var store: CodeCanvasStore + let traces: [String] + + var body: some View { + List(traces, id: \.self) { trace in + Button(trace) { + store.open(ClientRestfulWorkspace(trace: trace)) + } } - ) + } } } @@ -28,5 +54,4 @@ public struct ClientRestfulBench: CodeCanvasBench { public let icon: String = "network" public let components: [CodeCanvasBenchComponent] = [ClientRestfulTraceCollection()] } - #endif diff --git a/Sources/CodeCanvasShowCase/Extensions/CodeEditorExtension.swift b/Sources/CodeCanvasShowCase/Extensions/CodeEditorExtension.swift index d3c6ee1..caf90c3 100644 --- a/Sources/CodeCanvasShowCase/Extensions/CodeEditorExtension.swift +++ b/Sources/CodeCanvasShowCase/Extensions/CodeEditorExtension.swift @@ -2,24 +2,55 @@ import SwiftUI import CodeCanvas -public struct CodeCanvasBenchFileCollection: CodeCanvasBenchComponent { +public struct CodeEditorWorkspace: CodeCanvasWorkspace { + public let id = UUID().uuidString + let file: CodeCanvasFile - public let id: String = UUID().uuidString + public func content() -> AnyView { + AnyView( + VStack(alignment: .leading) { + Text("Editing: \(file.name)") + .font(.headline) + ScrollView { + Text(file.content) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + .padding() + ) + } +} +public struct CodeCanvasBenchFileCollection: CodeCanvasBenchComponent { + public let id: String = UUID().uuidString public let title: String = "File Collection" - public let icon: String = "folder" - public var actions: [any CodeCanvasBenchAction] = [] + private let files: [CodeCanvasFile] + + public init(files: [CodeCanvasFile] = [ + CodeCanvasFile(id: "1", name: "Example.swift", content: "print(\"Hello\")"), + CodeCanvasFile(id: "2", name: "Data.json", content: "{\n \"value\": 42\n}") + ]) { + self.files = files + } + public func content() -> AnyView { - AnyView( - VStack { - Text("No files available") - .foregroundColor(.secondary) - .padding() + AnyView(FileCollectionView(files: files)) + } + + struct FileCollectionView: View { + @EnvironmentObject var store: CodeCanvasStore + let files: [CodeCanvasFile] + + var body: some View { + List(files) { file in + Button(file.name) { + store.open(CodeEditorWorkspace(file: file)) + } } - ) + } } } @@ -27,7 +58,6 @@ public struct CodeEditorBench: CodeCanvasBench { public let id: String = UUID().uuidString public let name: String = "Editor" public let icon: String = "document" - public let components: [CodeCanvasBenchComponent] = [CodeCanvasBenchFileCollection(), CodeCanvasBenchFileCollection()] + public let components: [CodeCanvasBenchComponent] = [CodeCanvasBenchFileCollection()] } - #endif