diff --git a/.gitignore b/.gitignore index 53ca701..c7787d3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ ## User settings xcuserdata/ +.DS_Store diff --git a/README.md b/README.md deleted file mode 100644 index c5eb317..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# v-browser - diff --git a/etc/v-browser.png b/etc/v-browser.png new file mode 100644 index 0000000..8e87d69 Binary files /dev/null and b/etc/v-browser.png differ diff --git a/v-browser.xcodeproj/project.pbxproj b/v-browser.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6fafe73 --- /dev/null +++ b/v-browser.xcodeproj/project.pbxproj @@ -0,0 +1,629 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 2550BFAA2C9AF8140033B562 /* VectorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2550BFA92C9AF80A0033B562 /* VectorManager.swift */; }; + 2550BFAD2C9AFFFA0033B562 /* SimilaritySearchKit in Frameworks */ = {isa = PBXBuildFile; productRef = 2550BFAC2C9AFFFA0033B562 /* SimilaritySearchKit */; }; + DE4D74282C8D65BD0072A4A7 /* v_browserApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE4D74272C8D65BD0072A4A7 /* v_browserApp.swift */; }; + DE4D742A2C8D65BD0072A4A7 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE4D74292C8D65BD0072A4A7 /* ContentView.swift */; }; + DE4D742C2C8D65BF0072A4A7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DE4D742B2C8D65BF0072A4A7 /* Assets.xcassets */; }; + DE4D742F2C8D65BF0072A4A7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DE4D742E2C8D65BF0072A4A7 /* Preview Assets.xcassets */; }; + DE4D743A2C8D65BF0072A4A7 /* v_browserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE4D74392C8D65BF0072A4A7 /* v_browserTests.swift */; }; + DE4D74442C8D65BF0072A4A7 /* v_browserUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE4D74432C8D65BF0072A4A7 /* v_browserUITests.swift */; }; + DE4D74462C8D65BF0072A4A7 /* v_browserUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE4D74452C8D65BF0072A4A7 /* v_browserUITestsLaunchTests.swift */; }; + DE4D74532C8D6F860072A4A7 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE4D74522C8D6F860072A4A7 /* Logger.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + DE4D74362C8D65BF0072A4A7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DE4D741C2C8D65BD0072A4A7 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DE4D74232C8D65BD0072A4A7; + remoteInfo = "v-browser"; + }; + DE4D74402C8D65BF0072A4A7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DE4D741C2C8D65BD0072A4A7 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DE4D74232C8D65BD0072A4A7; + remoteInfo = "v-browser"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 2550BFA92C9AF80A0033B562 /* VectorManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VectorManager.swift; sourceTree = ""; }; + DE4D74242C8D65BD0072A4A7 /* v-browser.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "v-browser.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + DE4D74272C8D65BD0072A4A7 /* v_browserApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = v_browserApp.swift; sourceTree = ""; }; + DE4D74292C8D65BD0072A4A7 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + DE4D742B2C8D65BF0072A4A7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + DE4D742E2C8D65BF0072A4A7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + DE4D74302C8D65BF0072A4A7 /* v_browser.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = v_browser.entitlements; sourceTree = ""; }; + DE4D74352C8D65BF0072A4A7 /* v-browserTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "v-browserTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + DE4D74392C8D65BF0072A4A7 /* v_browserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = v_browserTests.swift; sourceTree = ""; }; + DE4D743F2C8D65BF0072A4A7 /* v-browserUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "v-browserUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + DE4D74432C8D65BF0072A4A7 /* v_browserUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = v_browserUITests.swift; sourceTree = ""; }; + DE4D74452C8D65BF0072A4A7 /* v_browserUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = v_browserUITestsLaunchTests.swift; sourceTree = ""; }; + DE4D74522C8D6F860072A4A7 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + DE4D74212C8D65BD0072A4A7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2550BFAD2C9AFFFA0033B562 /* SimilaritySearchKit in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DE4D74322C8D65BF0072A4A7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DE4D743C2C8D65BF0072A4A7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + DE4D741B2C8D65BD0072A4A7 = { + isa = PBXGroup; + children = ( + DE4D74262C8D65BD0072A4A7 /* v-browser */, + DE4D74382C8D65BF0072A4A7 /* v-browserTests */, + DE4D74422C8D65BF0072A4A7 /* v-browserUITests */, + DE4D74252C8D65BD0072A4A7 /* Products */, + ); + sourceTree = ""; + }; + DE4D74252C8D65BD0072A4A7 /* Products */ = { + isa = PBXGroup; + children = ( + DE4D74242C8D65BD0072A4A7 /* v-browser.app */, + DE4D74352C8D65BF0072A4A7 /* v-browserTests.xctest */, + DE4D743F2C8D65BF0072A4A7 /* v-browserUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + DE4D74262C8D65BD0072A4A7 /* v-browser */ = { + isa = PBXGroup; + children = ( + 2550BFA92C9AF80A0033B562 /* VectorManager.swift */, + DE4D74522C8D6F860072A4A7 /* Logger.swift */, + DE4D74272C8D65BD0072A4A7 /* v_browserApp.swift */, + DE4D74292C8D65BD0072A4A7 /* ContentView.swift */, + DE4D742B2C8D65BF0072A4A7 /* Assets.xcassets */, + DE4D74302C8D65BF0072A4A7 /* v_browser.entitlements */, + DE4D742D2C8D65BF0072A4A7 /* Preview Content */, + ); + path = "v-browser"; + sourceTree = ""; + }; + DE4D742D2C8D65BF0072A4A7 /* Preview Content */ = { + isa = PBXGroup; + children = ( + DE4D742E2C8D65BF0072A4A7 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + DE4D74382C8D65BF0072A4A7 /* v-browserTests */ = { + isa = PBXGroup; + children = ( + DE4D74392C8D65BF0072A4A7 /* v_browserTests.swift */, + ); + path = "v-browserTests"; + sourceTree = ""; + }; + DE4D74422C8D65BF0072A4A7 /* v-browserUITests */ = { + isa = PBXGroup; + children = ( + DE4D74432C8D65BF0072A4A7 /* v_browserUITests.swift */, + DE4D74452C8D65BF0072A4A7 /* v_browserUITestsLaunchTests.swift */, + ); + path = "v-browserUITests"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + DE4D74232C8D65BD0072A4A7 /* v-browser */ = { + isa = PBXNativeTarget; + buildConfigurationList = DE4D74492C8D65BF0072A4A7 /* Build configuration list for PBXNativeTarget "v-browser" */; + buildPhases = ( + DE4D74202C8D65BD0072A4A7 /* Sources */, + DE4D74212C8D65BD0072A4A7 /* Frameworks */, + DE4D74222C8D65BD0072A4A7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "v-browser"; + packageProductDependencies = ( + 2550BFAC2C9AFFFA0033B562 /* SimilaritySearchKit */, + ); + productName = "v-browser"; + productReference = DE4D74242C8D65BD0072A4A7 /* v-browser.app */; + productType = "com.apple.product-type.application"; + }; + DE4D74342C8D65BF0072A4A7 /* v-browserTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DE4D744C2C8D65BF0072A4A7 /* Build configuration list for PBXNativeTarget "v-browserTests" */; + buildPhases = ( + DE4D74312C8D65BF0072A4A7 /* Sources */, + DE4D74322C8D65BF0072A4A7 /* Frameworks */, + DE4D74332C8D65BF0072A4A7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DE4D74372C8D65BF0072A4A7 /* PBXTargetDependency */, + ); + name = "v-browserTests"; + productName = "v-browserTests"; + productReference = DE4D74352C8D65BF0072A4A7 /* v-browserTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DE4D743E2C8D65BF0072A4A7 /* v-browserUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DE4D744F2C8D65BF0072A4A7 /* Build configuration list for PBXNativeTarget "v-browserUITests" */; + buildPhases = ( + DE4D743B2C8D65BF0072A4A7 /* Sources */, + DE4D743C2C8D65BF0072A4A7 /* Frameworks */, + DE4D743D2C8D65BF0072A4A7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DE4D74412C8D65BF0072A4A7 /* PBXTargetDependency */, + ); + name = "v-browserUITests"; + productName = "v-browserUITests"; + productReference = DE4D743F2C8D65BF0072A4A7 /* v-browserUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DE4D741C2C8D65BD0072A4A7 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1540; + LastUpgradeCheck = 1600; + TargetAttributes = { + DE4D74232C8D65BD0072A4A7 = { + CreatedOnToolsVersion = 15.4; + }; + DE4D74342C8D65BF0072A4A7 = { + CreatedOnToolsVersion = 15.4; + TestTargetID = DE4D74232C8D65BD0072A4A7; + }; + DE4D743E2C8D65BF0072A4A7 = { + CreatedOnToolsVersion = 15.4; + TestTargetID = DE4D74232C8D65BD0072A4A7; + }; + }; + }; + buildConfigurationList = DE4D741F2C8D65BD0072A4A7 /* Build configuration list for PBXProject "v-browser" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = DE4D741B2C8D65BD0072A4A7; + packageReferences = ( + 2550BFAB2C9AFFFA0033B562 /* XCRemoteSwiftPackageReference "similarity-search-kit" */, + ); + productRefGroup = DE4D74252C8D65BD0072A4A7 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DE4D74232C8D65BD0072A4A7 /* v-browser */, + DE4D74342C8D65BF0072A4A7 /* v-browserTests */, + DE4D743E2C8D65BF0072A4A7 /* v-browserUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + DE4D74222C8D65BD0072A4A7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DE4D742F2C8D65BF0072A4A7 /* Preview Assets.xcassets in Resources */, + DE4D742C2C8D65BF0072A4A7 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DE4D74332C8D65BF0072A4A7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DE4D743D2C8D65BF0072A4A7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + DE4D74202C8D65BD0072A4A7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DE4D742A2C8D65BD0072A4A7 /* ContentView.swift in Sources */, + 2550BFAA2C9AF8140033B562 /* VectorManager.swift in Sources */, + DE4D74532C8D6F860072A4A7 /* Logger.swift in Sources */, + DE4D74282C8D65BD0072A4A7 /* v_browserApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DE4D74312C8D65BF0072A4A7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DE4D743A2C8D65BF0072A4A7 /* v_browserTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DE4D743B2C8D65BF0072A4A7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DE4D74462C8D65BF0072A4A7 /* v_browserUITestsLaunchTests.swift in Sources */, + DE4D74442C8D65BF0072A4A7 /* v_browserUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + DE4D74372C8D65BF0072A4A7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DE4D74232C8D65BD0072A4A7 /* v-browser */; + targetProxy = DE4D74362C8D65BF0072A4A7 /* PBXContainerItemProxy */; + }; + DE4D74412C8D65BF0072A4A7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DE4D74232C8D65BD0072A4A7 /* v-browser */; + targetProxy = DE4D74402C8D65BF0072A4A7 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + DE4D74472C8D65BF0072A4A7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + DE4D74482C8D65BF0072A4A7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + 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"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + DE4D744A2C8D65BF0072A4A7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = "v-browser/v_browser.entitlements"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_ASSET_PATHS = "\"v-browser/Preview Content\""; + DEVELOPMENT_TEAM = RQQ6N82NA8; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "v-browser"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "dqj.v-browser"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + DE4D744B2C8D65BF0072A4A7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = "v-browser/v_browser.entitlements"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_ASSET_PATHS = "\"v-browser/Preview Content\""; + DEVELOPMENT_TEAM = RQQ6N82NA8; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "v-browser"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "dqj.v-browser"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + DE4D744D2C8D65BF0072A4A7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = RQQ6N82NA8; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "dqj.v-browserTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/v-browser.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/v-browser"; + }; + name = Debug; + }; + DE4D744E2C8D65BF0072A4A7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = RQQ6N82NA8; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "dqj.v-browserTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/v-browser.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/v-browser"; + }; + name = Release; + }; + DE4D74502C8D65BF0072A4A7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = RQQ6N82NA8; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "dqj.v-browserUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = "v-browser"; + }; + name = Debug; + }; + DE4D74512C8D65BF0072A4A7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = RQQ6N82NA8; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "dqj.v-browserUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = "v-browser"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + DE4D741F2C8D65BD0072A4A7 /* Build configuration list for PBXProject "v-browser" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DE4D74472C8D65BF0072A4A7 /* Debug */, + DE4D74482C8D65BF0072A4A7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DE4D74492C8D65BF0072A4A7 /* Build configuration list for PBXNativeTarget "v-browser" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DE4D744A2C8D65BF0072A4A7 /* Debug */, + DE4D744B2C8D65BF0072A4A7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DE4D744C2C8D65BF0072A4A7 /* Build configuration list for PBXNativeTarget "v-browserTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DE4D744D2C8D65BF0072A4A7 /* Debug */, + DE4D744E2C8D65BF0072A4A7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DE4D744F2C8D65BF0072A4A7 /* Build configuration list for PBXNativeTarget "v-browserUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DE4D74502C8D65BF0072A4A7 /* Debug */, + DE4D74512C8D65BF0072A4A7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 2550BFAB2C9AFFFA0033B562 /* XCRemoteSwiftPackageReference "similarity-search-kit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ZachNagengast/similarity-search-kit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.0.15; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 2550BFAC2C9AFFFA0033B562 /* SimilaritySearchKit */ = { + isa = XCSwiftPackageProductDependency; + package = 2550BFAB2C9AFFFA0033B562 /* XCRemoteSwiftPackageReference "similarity-search-kit" */; + productName = SimilaritySearchKit; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = DE4D741C2C8D65BD0072A4A7 /* Project object */; +} diff --git a/v-browser.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/v-browser.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/v-browser.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/v-browser.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/v-browser.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/v-browser.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/v-browser.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/v-browser.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..94295a0 --- /dev/null +++ b/v-browser.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "a351174101179e10377155e91d3d1a4a5648760804ce27142488847d1ca85286", + "pins" : [ + { + "identity" : "similarity-search-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ZachNagengast/similarity-search-kit.git", + "state" : { + "revision" : "9bec54706c8124bb9b20a3a06d566066a9f55712", + "version" : "0.0.15" + } + } + ], + "version" : 3 +} diff --git a/v-browser/Assets.xcassets/AccentColor.colorset/Contents.json b/v-browser/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/v-browser/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/v-browser/Assets.xcassets/AppIcon.appiconset/1024.png b/v-browser/Assets.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 0000000..cbb595f Binary files /dev/null and b/v-browser/Assets.xcassets/AppIcon.appiconset/1024.png differ diff --git a/v-browser/Assets.xcassets/AppIcon.appiconset/128.png b/v-browser/Assets.xcassets/AppIcon.appiconset/128.png new file mode 100644 index 0000000..cec8345 Binary files /dev/null and b/v-browser/Assets.xcassets/AppIcon.appiconset/128.png differ diff --git a/v-browser/Assets.xcassets/AppIcon.appiconset/16.png b/v-browser/Assets.xcassets/AppIcon.appiconset/16.png new file mode 100644 index 0000000..3dd5283 Binary files /dev/null and b/v-browser/Assets.xcassets/AppIcon.appiconset/16.png differ diff --git a/v-browser/Assets.xcassets/AppIcon.appiconset/256.png b/v-browser/Assets.xcassets/AppIcon.appiconset/256.png new file mode 100644 index 0000000..7e69c85 Binary files /dev/null and b/v-browser/Assets.xcassets/AppIcon.appiconset/256.png differ diff --git a/v-browser/Assets.xcassets/AppIcon.appiconset/32.png b/v-browser/Assets.xcassets/AppIcon.appiconset/32.png new file mode 100644 index 0000000..63a5d19 Binary files /dev/null and b/v-browser/Assets.xcassets/AppIcon.appiconset/32.png differ diff --git a/v-browser/Assets.xcassets/AppIcon.appiconset/512.png b/v-browser/Assets.xcassets/AppIcon.appiconset/512.png new file mode 100644 index 0000000..161a4c9 Binary files /dev/null and b/v-browser/Assets.xcassets/AppIcon.appiconset/512.png differ diff --git a/v-browser/Assets.xcassets/AppIcon.appiconset/64.png b/v-browser/Assets.xcassets/AppIcon.appiconset/64.png new file mode 100644 index 0000000..01b959d Binary files /dev/null and b/v-browser/Assets.xcassets/AppIcon.appiconset/64.png differ diff --git a/v-browser/Assets.xcassets/AppIcon.appiconset/Contents.json b/v-browser/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2003d5b --- /dev/null +++ b/v-browser/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1 @@ +{"images":[{"size":"128x128","expected-size":"128","filename":"128.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"256x256","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"128x128","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"256x256","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"512x512","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"16","filename":"16.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"64","filename":"64.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"512x512","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"}]} \ No newline at end of file diff --git a/v-browser/Assets.xcassets/Contents.json b/v-browser/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/v-browser/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/v-browser/ContentView.swift b/v-browser/ContentView.swift new file mode 100644 index 0000000..0b17972 --- /dev/null +++ b/v-browser/ContentView.swift @@ -0,0 +1,344 @@ +// +// ContentView.swift +// v-browser +// +// Created by Du Qingjie on 2024/09/08. +// + +import SwiftUI +import WebKit +import AppKit + +// WebPage structure +class WebPage: NSObject, ObservableObject, Identifiable, WKNavigationDelegate, WKUIDelegate { + let id = UUID() + var url: String + var title: String + var summary: String + var wkWebView: WKWebView? + var view: ContentView? + + @Published var canGoBack = false + @Published var canGoForward = false + @Published var favicon: Image? + @Published var showCloseIcon = false + + init(view: ContentView, url: String, title: String, summary: String) { + self.view = view + self.url = url + self.title = title + self.summary = summary + super.init() + self.wkWebView = WebKit.WKWebView() + self.wkWebView?.navigationDelegate = self + self.wkWebView?.uiDelegate = self + if let url = URL(string: url) { + let request = URLRequest(url: url) + self.wkWebView?.load(request) + self.loadFavicon(for: url) + } + } + + func loadFavicon(for url: URL) { + let faviconURL = url.appendingPathComponent("favicon.ico") + + URLSession.shared.dataTask(with: faviconURL) { data, response, error in + if let data = data, let nsImage = NSImage(data: data) { + DispatchQueue.main.async { + self.favicon = Image(nsImage: nsImage) // Set the fetched favicon + } + } else { + DispatchQueue.main.async { + // Favicon not found, load the app icon instead + self.favicon = Image(nsImage: NSApp.applicationIconImage) + } + } + }.resume() + } + + func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { + if let url = navigationAction.request.url { + // Instead of creating a new web view, loaod the URL in the current web view + webView.load(URLRequest(url: url)) + canGoBack = true + } + return nil // Return nil to prevent creating a new window + } + + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + DispatchQueue.main.async { + self.canGoBack = webView.canGoBack + self.canGoForward = webView.canGoForward + self.url = webView.url?.absoluteString ?? "" + self.loadFavicon(for: URL(string: self.url)!) + self.objectWillChange.send() // Notify SwiftUI that a change occurred + let cur = self.view?.selectedTab + self.view?.selectedTab = nil + self.view?.selectedTab = cur + } + } +} + +struct WebViewWrapper: NSViewRepresentable { + var webView: WKWebView + + func makeNSView(context: Context) -> WKWebView { + return webView + } + + func updateNSView(_ nsView: WKWebView, context: Context) { + // No need to update, as the WKWebView is already initialized and managed by WebPage + } +} + +struct ContentView: View { + @State private var tabs: [WebPage] = [ + //WebPage(url: "https://amipro.me", title: "amiPro", summary: "Search the web"), + //WebPage(url: "https://apple.com", title: "Apple", summary: "Apple's official website.") + ] + @State public var selectedTab: WebPage? = nil + @State private var browsingHistory: [WebPage] = [] + @State private var currentURL: String = "" + @State private var leftPanelWidth: CGFloat = 200 + @State private var rightPanelWidth: CGFloat = 200 + @State private var lastDragLocationX: CGFloat = -1000000 + + @State private var curHoverTab: WebPage? + + var body: some View { + VStack { + // Address bar and navigation buttons + HStack { + TextField("Enter URL or Ask AI", text: $currentURL) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .frame(maxWidth: .infinity) + .onSubmit { + openTab(urlTxt: currentURL) + } + Button("Go") { + openTab(urlTxt: currentURL) + } + .padding(.leading, 5) + + Button("<") { + goBack() + } + .disabled(!(selectedTab?.canGoBack ?? false)) + + Button(">") { + goForward() + } + .disabled(!(selectedTab?.canGoForward ?? false)) + } + .padding() + + // Main body with draggable panels + HStack(spacing: 0) { + // Left panel (Tabs) + VStack(spacing: 0) { + ScrollView { + ForEach(tabs) { tab in + HStack{ + if tab.showCloseIcon { + Image(systemName: "xmark.circle.fill") // Show close icon on hover + .onTapGesture { + closeTab(tab: tab) + } + .frame(width: 16, height: 16) + } else { + tab.favicon?.resizable() + .frame(width: 16, height: 16) // Show favicon + } + Button(action: { selectedTab = tab }) { + Text(tab.title) + .lineLimit(1) + .truncationMode(.tail) + .padding(6) + .frame(width: leftPanelWidth - 32, alignment: .leading) + .foregroundColor(tab == selectedTab ? Color.primary : Color.secondary) // Text color + .background( + tab == selectedTab ? Color.accentColor.opacity(0.2) : Color.gray.opacity(0.1) // Adaptive background color + ) + }.buttonStyle(PlainButtonStyle()).frame(height: 21) + }.onHover { hovering in + if nil != curHoverTab{ + curHoverTab?.showCloseIcon = false + } + curHoverTab = tab + tab.showCloseIcon = hovering // Toggle between favicon and close icon + let cur = self.selectedTab + self.selectedTab = nil + self.selectedTab = cur + } + } + } + .frame(width: leftPanelWidth) + .background(Color.gray.opacity(0.1)) + .gesture(DragGesture() + .onChanged { value in + let chg = (-1000000 == lastDragLocationX) ? value.location.x-value.startLocation.x: value.location.x-lastDragLocationX + lastDragLocationX = value.location.x + let newWidth = leftPanelWidth + chg //value.translation.width + if newWidth > 30 && newWidth < 600 { + leftPanelWidth = newWidth + } + } + .onEnded { value in + lastDragLocationX = -1000000 + } + ) + } + + // Draggable divider between tabs and WebView + Rectangle() + .frame(width: 1) + .foregroundColor(.gray) + .gesture(DragGesture() + .onChanged { value in + let chg = (-1000000 == lastDragLocationX) ? value.location.x-value.startLocation.x: value.location.x-lastDragLocationX + lastDragLocationX = value.location.x + let newWidth = leftPanelWidth + chg //value.translation.width + if newWidth > 30 && newWidth < 600 { + leftPanelWidth = newWidth + } + } + .onEnded{value in + lastDragLocationX = -1000000 + } + ) + .onHover { hovering in + if hovering { + NSCursor.resizeLeftRight.set() // Set cursor to left-right resize + } else { + NSCursor.arrow.set() // Reset cursor when not hovering + } + } + + // Center panel (WebView or AI result) + VStack { + if let selectedTab = selectedTab, let webView = selectedTab.wkWebView { + WebViewWrapper(webView: webView) + .id(selectedTab.id) // Force SwiftUI to reinitialize when the tab changes + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + Text("No tab selected") + .font(.headline) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + + Rectangle() + .frame(width: 1) + .foregroundColor(.gray) + .gesture(DragGesture() + .onChanged { value in + let newWidth = rightPanelWidth - value.translation.width + if newWidth > 100 && newWidth < 600 { + rightPanelWidth = newWidth + } + } + ) + .onHover { hovering in + if hovering { + NSCursor.resizeLeftRight.set() // Set cursor to left-right resize + } else { + NSCursor.arrow.set() // Reset cursor when not hovering + } + } + + // Right panel (Browsing History) + VStack { + ScrollView { + ForEach(browsingHistory) { page in + Text(page.title) + .padding(10) + .frame(maxWidth: .infinity, alignment: .leading) + .background(page == selectedTab ? Color.blue.opacity(0.2) : Color.clear) + .cornerRadius(5) + .onTapGesture { + selectedTab = page + } + } + } + .frame(width: rightPanelWidth) + .background(Color.gray.opacity(0.1)) + .gesture(DragGesture() + .onChanged { value in + let newWidth = rightPanelWidth - value.translation.width + if newWidth > 100 && newWidth < 400 { + rightPanelWidth = newWidth + } + } + ) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + + // Bottom bar with system status + HStack { + Text("System Status: OK") + .font(.footnote) + Spacer() + } + .padding([.leading, .bottom]) + .background(Color.gray.opacity(0.1)) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .onAppear { + // Automatically open a tab when the view appears + openTab(urlTxt: "https://amipro.me") + } + } + + func isValidURL(_ urlString: String) -> Bool { + if let url = URL(string: urlString) { + // Check if the URL has a valid scheme (http, https, etc.) + return ((url.scheme) != nil) + } + return false + } + + func openTab(urlTxt: String){ + if isValidURL(urlTxt){ + createTab(url: URL(string: urlTxt)?.absoluteString ?? "", title: "Loading...", summary: "Loading...") + }else if isValidURL("https://"+urlTxt){ + createTab(url: URL(string: "https://"+urlTxt)?.absoluteString ?? "", title: "Loading...", summary: "Loading...") + }else{ + createTab(url: "https://www.google.com/search?q="+urlTxt.replacingOccurrences(of: " ", with: "+"), title: "Loading...", summary: "Loading...") + //try to ask AI + } + } + + func createTab(url: String, title: String, summary: String) { + let newPage = WebPage(view: self, url: url, title: title, summary: summary) + tabs.append(newPage) + selectedTab = newPage + } + + func closeTab(tab: WebPage) { + if let index = tabs.firstIndex(where: { $0.id == tab.id }) { + tabs.remove(at: index) + if selectedTab?.id == tab.id { + selectedTab = tabs.last + } + } + } + + func goBack() { + if let selectedTab = selectedTab, selectedTab.canGoBack == true { + selectedTab.wkWebView?.goBack() + selectedTab.canGoForward = true + } + } + + func goForward() { + if let selectedTab = selectedTab, selectedTab.canGoForward == true { + selectedTab.wkWebView?.goForward() + selectedTab.canGoBack = true + } + } +} + +/*Preview { + ContentView() +}*/ diff --git a/v-browser/Logger.swift b/v-browser/Logger.swift new file mode 100644 index 0000000..929a436 --- /dev/null +++ b/v-browser/Logger.swift @@ -0,0 +1,28 @@ +// +// Logger.swift +// vectorsearch +// +// Created by Du Qingjie on 2024/08/31. +// + +import os +import Foundation + +class Logger { + static let shared = Logger() + + // Correctly initialize with subsystem and category + private let logger = os.Logger(subsystem: Bundle.main.bundleIdentifier ?? "dqj.vectorsearch", category: "VectorSearch") + + func info(_ message: String) { + logger.log("\(message, privacy: .public)") + } + + func err(_ message: String) { + logger.error("\(message, privacy: .public)") + } + + func debug(_ message: String) { + logger.debug("\(message, privacy: .public)") + } +} diff --git a/v-browser/Preview Content/Preview Assets.xcassets/Contents.json b/v-browser/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/v-browser/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/v-browser/VectorManager.swift b/v-browser/VectorManager.swift new file mode 100644 index 0000000..42c0b37 --- /dev/null +++ b/v-browser/VectorManager.swift @@ -0,0 +1,84 @@ +// +// AIRunner.swift +// v-browser +// +// Created by dqj on 2024/09/18. +// + +import SimilaritySearchKit +import Foundation + +class VectorManager: ObservableObject { + static let shared = VectorManager() + + private var similarityIndex: SimilarityIndex? + + init() { + Task { + await load() + } + } + + func load() async { + similarityIndex = await SimilarityIndex( + model: NativeEmbeddings(), + metric: CosineSimilarity() + ) + if let data = UserDefaults.standard.data(forKey: "vector_db") { + let items = try? JSONDecoder().decode([SimilarityIndex.IndexItem].self, from: data) + similarityIndex?.indexItems = items ?? [] + }else{ + similarityIndex?.indexItems = [] + } + } + + func setFileItem(path: String, text: String, modify_time: Int) async{ + guard let index = similarityIndex else { return } + + let curItem = index.getItem(id: path) + if(curItem != nil && Int(curItem?.metadata["mt"] ?? String(Int.max)) ?? Int.max > Int(modify_time)){ + index.updateItem(id: path, text: text, embedding: nil, metadata: ["mt": String(modify_time)] ) + if let encoded = try? JSONEncoder().encode(index.indexItems) { + UserDefaults.standard.set(encoded, forKey: "vector_db") + } + }else if(curItem == nil){ + await index.addItem(id: path, text: text, metadata: ["mt": String(modify_time)]) + if let encoded = try? JSONEncoder().encode(index.indexItems) { + UserDefaults.standard.set(encoded, forKey: "vector_db") + } + } + } + + func search(query: String) async -> [SimilarityIndex.SearchResult]{ + guard let index = similarityIndex else { return []} + + let results = await index.search(query, top: 100) + return results + } + + func deleteFileItem(path: String){ + guard let index = similarityIndex else { return } + + index.removeItem(id: path) + UserDefaults.standard.setValue(index.indexItems, forKey: "vector_db") + } + + func clear() { + guard let index = similarityIndex else { return } + + index.removeAll() + UserDefaults.standard.setValue(index.indexItems, forKey: "vector_db") + } + + func getFileItem(id: String) -> SimilarityIndex.IndexItem?{ + guard let index = similarityIndex else { return nil } + + return index.getItem(id: id) + } +} + +class SummaryBot:ObservableObject{ + +} + + diff --git a/v-browser/v_browser.entitlements b/v-browser/v_browser.entitlements new file mode 100644 index 0000000..625af03 --- /dev/null +++ b/v-browser/v_browser.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + com.apple.security.network.client + + + diff --git a/v-browser/v_browserApp.swift b/v-browser/v_browserApp.swift new file mode 100644 index 0000000..72eb1b9 --- /dev/null +++ b/v-browser/v_browserApp.swift @@ -0,0 +1,17 @@ +// +// v_browserApp.swift +// v-browser +// +// Created by Du Qingjie on 2024/09/08. +// + +import SwiftUI + +@main +struct v_browserApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/v-browserTests/v_browserTests.swift b/v-browserTests/v_browserTests.swift new file mode 100644 index 0000000..39d5c31 --- /dev/null +++ b/v-browserTests/v_browserTests.swift @@ -0,0 +1,36 @@ +// +// v_browserTests.swift +// v-browserTests +// +// Created by Du Qingjie on 2024/09/08. +// + +import XCTest +@testable import v_browser + +final class v_browserTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/v-browserUITests/v_browserUITests.swift b/v-browserUITests/v_browserUITests.swift new file mode 100644 index 0000000..076c826 --- /dev/null +++ b/v-browserUITests/v_browserUITests.swift @@ -0,0 +1,41 @@ +// +// v_browserUITests.swift +// v-browserUITests +// +// Created by Du Qingjie on 2024/09/08. +// + +import XCTest + +final class v_browserUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/v-browserUITests/v_browserUITestsLaunchTests.swift b/v-browserUITests/v_browserUITestsLaunchTests.swift new file mode 100644 index 0000000..52575cc --- /dev/null +++ b/v-browserUITests/v_browserUITestsLaunchTests.swift @@ -0,0 +1,32 @@ +// +// v_browserUITestsLaunchTests.swift +// v-browserUITests +// +// Created by Du Qingjie on 2024/09/08. +// + +import XCTest + +final class v_browserUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +}