Adjustments for the rewrite code. Text field adjustments
|
|
@ -7,25 +7,12 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
983EEA302E5F22DA00E19094 /* SwiftData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98EAE6592E5F1E890050E579 /* SwiftData.framework */; };
|
||||
983EEA312E5F22DA00E19094 /* SwiftData.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 98EAE6592E5F1E890050E579 /* SwiftData.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
9825C2092ED77CF3007D8698 /* SwiftData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9825C2082ED77CF3007D8698 /* SwiftData.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
983EEA322E5F22DA00E19094 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
dstPath = "";
|
||||
dstSubfolder = Frameworks;
|
||||
files = (
|
||||
983EEA312E5F22DA00E19094 /* SwiftData.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
9825C2082ED77CF3007D8698 /* SwiftData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftData.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS26.1.sdk/System/Library/Frameworks/SwiftData.framework; sourceTree = DEVELOPER_DIR; };
|
||||
98EAE6332E5F15E80050E579 /* Neon Vision Editor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Neon Vision Editor.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
98EAE6592E5F1E890050E579 /* SwiftData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftData.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS26.0.sdk/System/Library/Frameworks/SwiftData.framework; sourceTree = DEVELOPER_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
|
|
@ -40,7 +27,7 @@
|
|||
98EAE6302E5F15E80050E579 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
files = (
|
||||
983EEA302E5F22DA00E19094 /* SwiftData.framework in Frameworks */,
|
||||
9825C2092ED77CF3007D8698 /* SwiftData.framework in Frameworks */,
|
||||
);
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
|
@ -66,7 +53,7 @@
|
|||
98EAE6532E5F175B0050E579 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
98EAE6592E5F1E890050E579 /* SwiftData.framework */,
|
||||
9825C2082ED77CF3007D8698 /* SwiftData.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -81,7 +68,6 @@
|
|||
98EAE62F2E5F15E80050E579 /* Sources */,
|
||||
98EAE6302E5F15E80050E579 /* Frameworks */,
|
||||
98EAE6312E5F15E80050E579 /* Resources */,
|
||||
983EEA322E5F22DA00E19094 /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -101,7 +87,7 @@
|
|||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 2600;
|
||||
LastUpgradeCheck = 2600;
|
||||
LastUpgradeCheck = 2610;
|
||||
TargetAttributes = {
|
||||
98EAE6322E5F15E80050E579 = {
|
||||
CreatedOnToolsVersion = 26.0;
|
||||
|
|
@ -178,6 +164,7 @@
|
|||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = CS727NF72U;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
|
|
@ -201,6 +188,7 @@
|
|||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
|
|
@ -240,6 +228,7 @@
|
|||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = CS727NF72U;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
|
|
@ -256,6 +245,7 @@
|
|||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
};
|
||||
name = Release;
|
||||
|
|
@ -265,15 +255,29 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||
AUTOMATION_APPLE_EVENTS = NO;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = CS727NF72U;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_FILE_ACCESS_DOWNLOADS_FOLDER = readwrite;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SELECTED_FILES = readonly;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_LOCATION = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PRINTING = NO;
|
||||
ENABLE_RESOURCE_ACCESS_USB = NO;
|
||||
ENABLE_USER_SELECTED_FILES = readwrite;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Neon Vision Editor";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
|
|
@ -297,6 +301,12 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_JIT = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO;
|
||||
RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO;
|
||||
SDKROOT = auto;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
|
||||
|
|
@ -316,15 +326,29 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||
AUTOMATION_APPLE_EVENTS = NO;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = CS727NF72U;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_FILE_ACCESS_DOWNLOADS_FOLDER = readwrite;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SELECTED_FILES = readonly;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_LOCATION = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PRINTING = NO;
|
||||
ENABLE_RESOURCE_ACCESS_USB = NO;
|
||||
ENABLE_USER_SELECTED_FILES = readwrite;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Neon Vision Editor";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
|
|
@ -348,6 +372,12 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_JIT = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO;
|
||||
RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO;
|
||||
SDKROOT = auto;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
|
||||
|
|
|
|||
28
Neon Vision Editor/AI-Modell.swift
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import Foundation
|
||||
|
||||
struct GrokAPIClient {
|
||||
let apiKey: String
|
||||
private let baseURL = URL(string: "https://api.x.ai/v1")!
|
||||
|
||||
func generateText(prompt: String, maxTokens: Int = 100) async throws -> String {
|
||||
var request = URLRequest(url: baseURL.appendingPathComponent("generate"))
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
let body: [String: Any] = [
|
||||
"model": "grok-4",
|
||||
"prompt": prompt,
|
||||
"max_tokens": maxTokens
|
||||
]
|
||||
request.httpBody = try JSONSerialization.data(withJSONObject: body)
|
||||
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
||||
throw URLError(.badServerResponse)
|
||||
}
|
||||
|
||||
let json = try JSONDecoder().decode([String: String].self, from: data)
|
||||
return json["text"] ?? ""
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"red" : "0.694",
|
||||
"green" : "0.302",
|
||||
"blue" : "0.831",
|
||||
"alpha" : "1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "NeonVisionEditor-16.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "NeonVisionEditor-32 1.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "NeonVisionEditor-32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "NeonVisionEditor-64.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "NeonVisionEditor-128.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "NeonVisionEditor-256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "NeonVisionEditor-256 1.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "NeonVisionEditor-512 1.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "NeonVisionEditor-512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "NeonVision Editor.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 756 B |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
|
@ -1,48 +1,84 @@
|
|||
import SwiftUI
|
||||
import SwiftData
|
||||
import Combine
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
struct TabData: Identifiable {
|
||||
let id = UUID()
|
||||
var name: String
|
||||
var content: String
|
||||
var language: String
|
||||
var fileURL: URL?
|
||||
}
|
||||
|
||||
@MainActor
|
||||
class EditorViewModel: ObservableObject {
|
||||
@Published var tabs: [Tab] = []
|
||||
@Published var selectedTab: Tab?
|
||||
@Published var tabs: [TabData] = []
|
||||
@Published var selectedTabID: UUID?
|
||||
@Published var showSidebar: Bool = true
|
||||
|
||||
func addNewTab(context: ModelContext) {
|
||||
let newTab = Tab(name: "New Tab \(tabs.count + 1)", content: "", language: "swift")
|
||||
tabs.append(newTab)
|
||||
context.insert(newTab)
|
||||
selectedTab = newTab
|
||||
try? context.save()
|
||||
@Published var isBrainDumpMode: Bool = false
|
||||
@Published var showingRename: Bool = false
|
||||
@Published var renameText: String = ""
|
||||
|
||||
var selectedTab: TabData? {
|
||||
get { tabs.first(where: { $0.id == selectedTabID }) }
|
||||
set { selectedTabID = newValue?.id }
|
||||
}
|
||||
|
||||
func openFile() {
|
||||
let openPanel = NSOpenPanel()
|
||||
openPanel.allowedContentTypes = [.text, .sourceCode, .swiftSource, .pythonScript, .javaScript, .html, .css, .cSource, .cppSource, .json, .markdown]
|
||||
openPanel.allowsMultipleSelection = false
|
||||
openPanel.canChooseDirectories = false
|
||||
openPanel.canChooseFiles = true
|
||||
|
||||
guard openPanel.runModal() == .OK, let url = openPanel.url else { return }
|
||||
do {
|
||||
let content = try String(contentsOf: url, encoding: .utf8)
|
||||
let language = languageMap[url.pathExtension.lowercased()] ?? "plaintext"
|
||||
let newTab = Tab(name: url.lastPathComponent, content: content, language: language, fileURL: url)
|
||||
tabs.append(newTab)
|
||||
selectedTab = newTab
|
||||
if let context = try? ModelContext(ModelContainer(for: Tab.self)) {
|
||||
context.insert(newTab)
|
||||
try? context.save()
|
||||
}
|
||||
} catch {
|
||||
print("Error opening file: \(error)")
|
||||
|
||||
private let languageMap: [String: String] = [
|
||||
"swift": "swift",
|
||||
"py": "python",
|
||||
"js": "javascript",
|
||||
"html": "html",
|
||||
"css": "css",
|
||||
"c": "c",
|
||||
"cpp": "cpp",
|
||||
"h": "c",
|
||||
"json": "json",
|
||||
"md": "markdown"
|
||||
]
|
||||
|
||||
init() {
|
||||
addNewTab()
|
||||
}
|
||||
|
||||
func addNewTab() {
|
||||
let newTab = TabData(name: "Untitled \(tabs.count + 1)", content: "", language: "swift", fileURL: nil)
|
||||
tabs.append(newTab)
|
||||
selectedTabID = newTab.id
|
||||
}
|
||||
|
||||
func renameTab(tab: TabData, newName: String) {
|
||||
if let index = tabs.firstIndex(where: { $0.id == tab.id }) {
|
||||
tabs[index].name = newName
|
||||
}
|
||||
}
|
||||
|
||||
func saveFile(tab: Tab) {
|
||||
if let url = tab.fileURL {
|
||||
|
||||
func updateTabContent(tab: TabData, content: String) {
|
||||
if let index = tabs.firstIndex(where: { $0.id == tab.id }) {
|
||||
tabs[index].content = content
|
||||
}
|
||||
}
|
||||
|
||||
func updateTabLanguage(tab: TabData, language: String) {
|
||||
if let index = tabs.firstIndex(where: { $0.id == tab.id }) {
|
||||
tabs[index].language = language
|
||||
}
|
||||
}
|
||||
|
||||
func closeTab(tab: TabData) {
|
||||
tabs.removeAll { $0.id == tab.id }
|
||||
if tabs.isEmpty {
|
||||
addNewTab()
|
||||
} else if selectedTabID == tab.id {
|
||||
selectedTabID = tabs.first?.id
|
||||
}
|
||||
}
|
||||
|
||||
func saveFile(tab: TabData) {
|
||||
guard let index = tabs.firstIndex(where: { $0.id == tab.id }) else { return }
|
||||
if let url = tabs[index].fileURL {
|
||||
do {
|
||||
try tab.content.write(to: url, atomically: true, encoding: .utf8)
|
||||
print("Saved to \(url.path)")
|
||||
try tabs[index].content.write(to: url, atomically: true, encoding: .utf8)
|
||||
} catch {
|
||||
print("Error saving file: \(error)")
|
||||
}
|
||||
|
|
@ -50,28 +86,51 @@ class EditorViewModel: ObservableObject {
|
|||
saveFileAs(tab: tab)
|
||||
}
|
||||
}
|
||||
|
||||
func saveFileAs(tab: Tab) {
|
||||
let savePanel = NSSavePanel()
|
||||
savePanel.allowedContentTypes = [.text, .sourceCode, .swiftSource, .pythonScript, .javaScript, .html, .css, .cSource, .cppSource, .json, .markdown]
|
||||
savePanel.nameFieldStringValue = tab.name
|
||||
|
||||
guard savePanel.runModal() == .OK, let url = savePanel.url else { return }
|
||||
do {
|
||||
try tab.content.write(to: url, atomically: true, encoding: .utf8)
|
||||
tab.fileURL = url
|
||||
tab.name = url.lastPathComponent
|
||||
if let context = try? ModelContext(ModelContainer(for: Tab.self)) {
|
||||
try? context.save()
|
||||
|
||||
func saveFileAs(tab: TabData) {
|
||||
guard let index = tabs.firstIndex(where: { $0.id == tab.id }) else { return }
|
||||
let panel = NSSavePanel()
|
||||
panel.nameFieldStringValue = tabs[index].name
|
||||
panel.allowedContentTypes = [.text, .swiftSource, .pythonScript, .javaScript, .html, .css, .cSource, .json, UTType(importedAs: "public.markdown")]
|
||||
|
||||
Task {
|
||||
if panel.runModal() == .OK, let url = panel.url {
|
||||
do {
|
||||
try tabs[index].content.write(to: url, atomically: true, encoding: .utf8)
|
||||
tabs[index].fileURL = url
|
||||
tabs[index].name = url.lastPathComponent
|
||||
} catch {
|
||||
print("Error saving file: \(error)")
|
||||
}
|
||||
}
|
||||
print("Saved as \(url.path)")
|
||||
} catch {
|
||||
print("Error saving file: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
let languageMap: [String: String] = [
|
||||
"swift": "swift", "py": "python", "js": "javascript", "html": "html", "css": "css",
|
||||
"c": "c", "cpp": "cpp", "json": "json", "md": "markdown"
|
||||
]
|
||||
}
|
||||
|
||||
func openFile() {
|
||||
let panel = NSOpenPanel()
|
||||
panel.allowedContentTypes = [.text, .sourceCode, .swiftSource, .pythonScript, .javaScript, .html, .css, .cSource, .json, UTType(importedAs: "public.markdown")]
|
||||
panel.allowsMultipleSelection = false
|
||||
panel.canChooseDirectories = false
|
||||
|
||||
Task {
|
||||
if panel.runModal() == .OK, let url = panel.url {
|
||||
do {
|
||||
let content = try String(contentsOf: url, encoding: .utf8)
|
||||
let newTab = TabData(name: url.lastPathComponent,
|
||||
content: content,
|
||||
language: languageMap[url.pathExtension.lowercased()] ?? "swift",
|
||||
fileURL: url)
|
||||
tabs.append(newTab)
|
||||
selectedTabID = newTab.id
|
||||
} catch {
|
||||
print("Error opening file: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func wordCount(for text: String) -> Int {
|
||||
text.components(separatedBy: .whitespacesAndNewlines)
|
||||
.filter { !$0.isEmpty }.count
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
class GrokAPIClient {
|
||||
private let apiKey: String
|
||||
private let baseURL = "https://api.x.ai/v1"
|
||||
|
||||
init(apiKey: String) {
|
||||
self.apiKey = apiKey
|
||||
}
|
||||
|
||||
func generateText(prompt: String, model: String = "grok-3-beta", maxTokens: Int = 500) async throws -> String {
|
||||
let url = URL(string: "\(baseURL)/chat/completions")!
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
let body: [String: Any] = [
|
||||
"model": model,
|
||||
"messages": [
|
||||
["role": "user", "content": prompt]
|
||||
],
|
||||
"max_tokens": maxTokens
|
||||
]
|
||||
request.httpBody = try JSONSerialization.data(withJSONObject: body)
|
||||
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
||||
throw NSError(domain: "GrokAPI", code: -1, userInfo: [NSLocalizedDescriptionKey: "API request failed"])
|
||||
}
|
||||
|
||||
let json = try JSONDecoder().decode(GrokResponse.self, from: data)
|
||||
return json.choices.first?.message.content ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
struct GrokResponse: Codable {
|
||||
struct Choice: Codable {
|
||||
struct Message: Codable {
|
||||
let content: String
|
||||
}
|
||||
let message: Message
|
||||
}
|
||||
let choices: [Choice]
|
||||
}
|
||||
|
|
@ -1,112 +1,159 @@
|
|||
import SwiftUI
|
||||
import SwiftData
|
||||
import FoundationModels
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
var viewModel: EditorViewModel?
|
||||
var modelContext: ModelContext?
|
||||
|
||||
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
||||
guard let viewModel = viewModel, let modelContext = modelContext else {
|
||||
return .terminateNow
|
||||
}
|
||||
|
||||
for tab in viewModel.tabs {
|
||||
if !tab.content.isEmpty && tab.fileURL == nil {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Save changes to \"\(tab.name)\" before quitting?"
|
||||
alert.informativeText = "Your changes will be lost if you don't save them."
|
||||
alert.addButton(withTitle: "Save")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.addButton(withTitle: "Don't Save")
|
||||
alert.alertStyle = .warning
|
||||
|
||||
switch alert.runModal() {
|
||||
case .alertFirstButtonReturn: // Save
|
||||
viewModel.saveFileAs(tab: tab)
|
||||
case .alertSecondButtonReturn: // Cancel
|
||||
return .terminateCancel
|
||||
case .alertThirdButtonReturn: // Don't Save
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all tabs on quit
|
||||
for tab in viewModel.tabs {
|
||||
modelContext.delete(tab)
|
||||
}
|
||||
try? modelContext.save()
|
||||
|
||||
return .terminateNow
|
||||
}
|
||||
enum AIModel: String, Identifiable {
|
||||
case appleIntelligence = "Apple Intelligence"
|
||||
case grok = "Grok"
|
||||
|
||||
var id: String { rawValue }
|
||||
}
|
||||
|
||||
@main
|
||||
struct NeonVisionEditorApp: App {
|
||||
@StateObject private var viewModel = EditorViewModel()
|
||||
@State private var appDelegate = AppDelegate()
|
||||
@State private var modelContainer: ModelContainer?
|
||||
|
||||
@State private var showGrokError: Bool = false
|
||||
@State private var grokErrorMessage: String = ""
|
||||
@State private var selectedAIModel: AIModel = .appleIntelligence
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
.environmentObject(viewModel)
|
||||
.environment(\.modelContext, modelContainer?.mainContext ?? ModelContext(ModelContainer(for: Tab.self)))
|
||||
.frame(minWidth: 1000, minHeight: 600)
|
||||
.onAppear {
|
||||
if modelContainer == nil {
|
||||
do {
|
||||
let container = try ModelContainer(for: Tab.self)
|
||||
modelContainer = container
|
||||
appDelegate.viewModel = viewModel
|
||||
appDelegate.modelContext = container.mainContext
|
||||
NSApplication.shared.delegate = appDelegate
|
||||
} catch {
|
||||
print("Failed to create ModelContainer: \(error)")
|
||||
}
|
||||
}
|
||||
.environment(\.showGrokError, $showGrokError)
|
||||
.environment(\.grokErrorMessage, $grokErrorMessage)
|
||||
.environment(\.selectedAIModel, $selectedAIModel)
|
||||
.frame(minWidth: 600, minHeight: 400)
|
||||
.background(.ultraThinMaterial)
|
||||
.overlay(.ultraThinMaterial.opacity(0.2)) // Fallback for liquidGlassEffect
|
||||
.task {
|
||||
// Pre-warm Apple Intelligence model
|
||||
let session = LanguageModelSession(model: SystemLanguageModel())
|
||||
session.prewarm()
|
||||
}
|
||||
}
|
||||
.defaultSize(width: 1000, height: 600)
|
||||
.commands {
|
||||
CommandGroup(replacing: .newItem) {
|
||||
CommandMenu("File") {
|
||||
Button("New Tab") {
|
||||
if let context = modelContainer?.mainContext {
|
||||
viewModel.addNewTab(context: context)
|
||||
}
|
||||
viewModel.addNewTab()
|
||||
}
|
||||
.keyboardShortcut("t", modifiers: .command)
|
||||
}
|
||||
CommandMenu("File") {
|
||||
|
||||
Button("Open File...") {
|
||||
viewModel.openFile()
|
||||
}
|
||||
.keyboardShortcut("o", modifiers: .command)
|
||||
|
||||
|
||||
Button("Save") {
|
||||
if let selectedTab = viewModel.selectedTab {
|
||||
viewModel.saveFile(tab: selectedTab)
|
||||
if let tab = viewModel.selectedTab {
|
||||
viewModel.saveFile(tab: tab)
|
||||
}
|
||||
}
|
||||
.keyboardShortcut("s", modifiers: .command)
|
||||
.disabled(viewModel.selectedTab == nil)
|
||||
|
||||
|
||||
Button("Save As...") {
|
||||
if let selectedTab = viewModel.selectedTab {
|
||||
viewModel.saveFileAs(tab: selectedTab)
|
||||
if let tab = viewModel.selectedTab {
|
||||
viewModel.saveFileAs(tab: tab)
|
||||
}
|
||||
}
|
||||
.disabled(viewModel.selectedTab == nil)
|
||||
}
|
||||
|
||||
CommandMenu("View") {
|
||||
Button(viewModel.showSidebar ? "Hide Sidebar" : "Show Sidebar") {
|
||||
viewModel.showSidebar.toggle()
|
||||
|
||||
Button("Rename") {
|
||||
viewModel.showingRename = true
|
||||
viewModel.renameText = viewModel.selectedTab?.name ?? "Untitled"
|
||||
}
|
||||
.keyboardShortcut("b", modifiers: [.command, .shift])
|
||||
.disabled(viewModel.selectedTab == nil)
|
||||
|
||||
Button("Close Tab") {
|
||||
if let tab = viewModel.selectedTab {
|
||||
viewModel.closeTab(tab: tab)
|
||||
}
|
||||
}
|
||||
.keyboardShortcut("w", modifiers: .command)
|
||||
.disabled(viewModel.selectedTab == nil)
|
||||
}
|
||||
|
||||
CommandMenu("Language") {
|
||||
ForEach(["swift", "python", "javascript", "html", "css", "c", "cpp", "json", "markdown"], id: \.self) { lang in
|
||||
Button(lang.capitalized) {
|
||||
if let tab = viewModel.selectedTab {
|
||||
viewModel.updateTabLanguage(tab: tab, language: lang)
|
||||
}
|
||||
}
|
||||
.disabled(viewModel.selectedTab == nil)
|
||||
}
|
||||
}
|
||||
|
||||
CommandMenu("View") {
|
||||
Toggle("Toggle Sidebar", isOn: $viewModel.showSidebar)
|
||||
.keyboardShortcut("s", modifiers: [.command, .option])
|
||||
|
||||
Toggle("Brain Dump Mode", isOn: $viewModel.isBrainDumpMode)
|
||||
.keyboardShortcut("d", modifiers: [.command, .shift])
|
||||
}
|
||||
|
||||
CommandMenu("Tools") {
|
||||
Button("Suggest Code") {
|
||||
Task {
|
||||
if let tab = viewModel.selectedTab {
|
||||
switch selectedAIModel {
|
||||
case .appleIntelligence:
|
||||
let session = LanguageModelSession(model: SystemLanguageModel())
|
||||
let prompt = "System: Output a code suggestion for this \(tab.language) code.\nUser: \(tab.content.prefix(1000))"
|
||||
do {
|
||||
let suggestion = try await session.respond(to: prompt)
|
||||
viewModel.updateTabContent(tab: tab, content: tab.content + "\n\n// Apple Intelligence Suggestion:\n" + suggestion.content)
|
||||
} catch {
|
||||
grokErrorMessage = error.localizedDescription
|
||||
showGrokError = true
|
||||
}
|
||||
case .grok:
|
||||
let client = GrokAPIClient(apiKey: "your-xai-api-key") // Replace with your xAI API key from https://x.ai/api
|
||||
let prompt = "Suggest improvements for this \(tab.language) code: \(tab.content.prefix(1000))"
|
||||
do {
|
||||
let suggestion = try await client.generateText(prompt: prompt, maxTokens: 200)
|
||||
viewModel.updateTabContent(tab: tab, content: tab.content + "\n\n// Grok Suggestion:\n" + suggestion)
|
||||
} catch {
|
||||
grokErrorMessage = error.localizedDescription
|
||||
showGrokError = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.keyboardShortcut("g", modifiers: [.command, .shift])
|
||||
.disabled(viewModel.selectedTab == nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ShowGrokErrorKey: EnvironmentKey {
|
||||
static let defaultValue: Binding<Bool> = .constant(false)
|
||||
}
|
||||
|
||||
struct GrokErrorMessageKey: EnvironmentKey {
|
||||
static let defaultValue: Binding<String> = .constant("")
|
||||
}
|
||||
|
||||
struct SelectedAIModelKey: EnvironmentKey {
|
||||
static let defaultValue: Binding<AIModel> = .constant(.appleIntelligence)
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
var showGrokError: Binding<Bool> {
|
||||
get { self[ShowGrokErrorKey.self] }
|
||||
set { self[ShowGrokErrorKey.self] = newValue }
|
||||
}
|
||||
|
||||
var grokErrorMessage: Binding<String> {
|
||||
get { self[GrokErrorMessageKey.self] }
|
||||
set { self[GrokErrorMessageKey.self] = newValue }
|
||||
}
|
||||
|
||||
var selectedAIModel: Binding<AIModel> {
|
||||
get { self[SelectedAIModelKey.self] }
|
||||
set { self[SelectedAIModelKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
Neon Vision Editor/Recources/AppIcon.icon/Assets/5.png
Normal file
|
After Width: | Height: | Size: 623 KiB |
29
Neon Vision Editor/Recources/AppIcon.icon/icon.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"fill" : {
|
||||
"automatic-gradient" : "extended-srgb:0.00000,0.53333,1.00000,1.00000"
|
||||
},
|
||||
"groups" : [
|
||||
{
|
||||
"layers" : [
|
||||
{
|
||||
"image-name" : "5.png",
|
||||
"name" : "5"
|
||||
}
|
||||
],
|
||||
"shadow" : {
|
||||
"kind" : "neutral",
|
||||
"opacity" : 0.5
|
||||
},
|
||||
"translucency" : {
|
||||
"enabled" : true,
|
||||
"value" : 0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"supported-platforms" : {
|
||||
"circles" : [
|
||||
"watchOS"
|
||||
],
|
||||
"squares" : "shared"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.772",
|
||||
"green" : "0.532",
|
||||
"red" : "0.898"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.816",
|
||||
"green" : "0.261",
|
||||
"red" : "0.569"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
22
Neon Vision Editor/Recources/Assets.xcassets/NeonVision Editor.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "NeonVision Editor.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "NeonVision Editor 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 439 KiB After Width: | Height: | Size: 439 KiB |
BIN
Neon Vision Editor/Recources/Assets.xcassets/NeonVision Editor.imageset/NeonVision Editor.png
vendored
Normal file
|
After Width: | Height: | Size: 439 KiB |