chore: initial commit (Phase 3 scaffold)
Some checks are pending
ci / validate (push) Waiting to run

This commit is contained in:
CxAI Ops 2026-05-16 10:52:05 -05:00
commit 5b384c5baa
55 changed files with 2111 additions and 0 deletions

15
.editorconfig Normal file
View File

@ -0,0 +1,15 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
[*.{yml,yaml,json,md}]
indent_size = 2
[Makefile]
indent_style = tab

36
.gitea/workflows/ci.yml Normal file
View File

@ -0,0 +1,36 @@
name: ci
on:
push:
branches: [main, master]
pull_request: {}
workflow_dispatch: {}
# We run a Linux runner; macOS xcodebuild is not available here.
# This workflow validates structure (yaml, file presence) and basic Swift.
jobs:
validate:
runs-on: cxai-hostinger
container: debian:bookworm
steps:
- name: Install deps
run: |
apt-get update -qq
apt-get install -y --no-install-recommends git ca-certificates curl python3-yaml >/dev/null
- uses: actions/checkout@v4
- name: Lint workflow YAML
run: |
python3 - <<'PY'
import sys, yaml, glob
for f in glob.glob('.gitea/workflows/*.yml') + glob.glob('.github/workflows/*.yml'):
try:
yaml.safe_load(open(f))
except Exception as e:
print(f'YAML ERROR {f}: {e}'); sys.exit(1)
print('workflows ok')
PY
- name: Project sanity
run: |
test -f README.md || (echo "missing README" && exit 1)
test -f Makefile || (echo "missing Makefile" && exit 1)
ls *.xcodeproj >/dev/null 2>&1 && echo "xcodeproj: $(ls -d *.xcodeproj)" || true
[ -f Package.swift ] && echo "Package.swift present" || true

27
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: ci
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
name: build (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-14]
steps:
- uses: actions/checkout@v4
- name: Show toolchain
run: |
xcodebuild -version || true
swift --version || true
- name: Build
run: make build
- name: Test
run: make test
- name: Lint
run: make lint

54
.gitignore vendored Normal file
View File

@ -0,0 +1,54 @@
# macOS
.DS_Store
.AppleDouble
.LSOverride
Icon?
# Xcode
build/
DerivedData/
*.xcworkspace/xcuserdata/
*.xcodeproj/xcuserdata/
*.xcodeproj/project.xcworkspace/xcuserdata/
*.xcuserstate
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
# SwiftPM
.build/
.swiftpm/xcode/
.swiftpm/configuration/
Package.resolved
# CocoaPods / Carthage
Pods/
Carthage/Build/
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output/
# Coverage
*.gcno
*.gcda
*.profdata
*.profraw
coverage/
# Editors
.vscode/
.idea/
*.swp
# Local
*.local
secrets.env

16
CHANGELOG.md Normal file
View File

@ -0,0 +1,16 @@
# Changelog
All notable changes to this project will be documented here.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
### Added
- Repository scaffolding: README, LICENSE (MIT), .gitignore, Makefile,
CONTRIBUTING, SECURITY, CODEOWNERS, .editorconfig, CI workflow.
## [0.1.0] - 2026-04-22
### Added
- Initial safari-extension scaffold for CxLLM-SAFARI.

6
CODEOWNERS Normal file
View File

@ -0,0 +1,6 @@
# Default owners for everything in this repo.
* @CxAI-LLM/maintainers
# Build & CI
/.github/ @CxAI-LLM/devops
/Makefile @CxAI-LLM/devops

23
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,23 @@
# Contributing to CxLLM-SAFARI
Thanks for taking the time to contribute!
## Workflow
1. Open an issue describing the change before sending a PR for non-trivial work.
2. Fork / branch off `main`. Use a descriptive branch name (`feat/...`, `fix/...`).
3. Keep commits scoped and use **Conventional Commits** (`feat:`, `fix:`,
`docs:`, `refactor:`, `test:`, `chore:`).
4. Run `make lint` and `make test` before pushing.
5. Open a PR — CI must pass before review.
## Coding style
- Swift: `swiftformat` defaults + `swiftlint` rules from the umbrella repo.
- Objective-C / C / C++: clang-format `-style=Google`.
- No tabs in Swift; 4-space indent in C/C++.
## Code of conduct
By contributing you agree to abide by the project's Code of Conduct
(see the umbrella `cxllm-code` repo).

View File

@ -0,0 +1,909 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
20F2B0CE2F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 20F2B0CD2F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
20F2B0D82F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 20F2B0D72F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
20F2B0CF2F99764000E7D2D9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 20F2B08B2F99763E00E7D2D9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 20F2B0CC2F99764000E7D2D9;
remoteInfo = "CxLLM-SAFARI Extension (iOS)";
};
20F2B0D92F99764000E7D2D9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 20F2B08B2F99763E00E7D2D9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 20F2B0D62F99764000E7D2D9;
remoteInfo = "CxLLM-SAFARI Extension (macOS)";
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
20F2B1002F99764000E7D2D9 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
20F2B0CE2F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
20F2B10A2F99764000E7D2D9 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
20F2B0D82F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
20F2B0A92F99764000E7D2D9 /* CxLLM-SAFARI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "CxLLM-SAFARI.app"; sourceTree = BUILT_PRODUCTS_DIR; };
20F2B0BF2F99764000E7D2D9 /* CxLLM-SAFARI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "CxLLM-SAFARI.app"; sourceTree = BUILT_PRODUCTS_DIR; };
20F2B0CD2F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "CxLLM-SAFARI Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
20F2B0D72F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "CxLLM-SAFARI Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
20F2B0FB2F99764000E7D2D9 /* Exceptions for "Shared (App)" folder in "CxLLM-SAFARI (iOS)" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
"/Localized: Resources/Main.html",
Assets.xcassets,
Resources/Icon.png,
Resources/Script.js,
Resources/Style.css,
ViewController.m,
);
target = 20F2B0A82F99764000E7D2D9 /* CxLLM-SAFARI (iOS) */;
};
20F2B0FF2F99764000E7D2D9 /* Exceptions for "iOS (Extension)" folder in "CxLLM-SAFARI Extension (iOS)" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = 20F2B0CC2F99764000E7D2D9 /* CxLLM-SAFARI Extension (iOS) */;
};
20F2B1042F99764000E7D2D9 /* Exceptions for "iOS (App)" folder in "CxLLM-SAFARI (iOS)" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = 20F2B0A82F99764000E7D2D9 /* CxLLM-SAFARI (iOS) */;
};
20F2B1052F99764000E7D2D9 /* Exceptions for "Shared (App)" folder in "CxLLM-SAFARI (macOS)" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
"/Localized: Resources/Main.html",
Assets.xcassets,
Resources/Icon.png,
Resources/Script.js,
Resources/Style.css,
ViewController.m,
);
target = 20F2B0BE2F99764000E7D2D9 /* CxLLM-SAFARI (macOS) */;
};
20F2B1092F99764000E7D2D9 /* Exceptions for "macOS (Extension)" folder in "CxLLM-SAFARI Extension (macOS)" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = 20F2B0D62F99764000E7D2D9 /* CxLLM-SAFARI Extension (macOS) */;
};
20F2B10E2F99764000E7D2D9 /* Exceptions for "Shared (Extension)" folder in "CxLLM-SAFARI Extension (iOS)" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Resources/_locales,
Resources/background.js,
Resources/content.js,
Resources/images,
Resources/manifest.json,
Resources/popup.css,
Resources/popup.html,
Resources/popup.js,
SafariWebExtensionHandler.m,
);
target = 20F2B0CC2F99764000E7D2D9 /* CxLLM-SAFARI Extension (iOS) */;
};
20F2B10F2F99764000E7D2D9 /* Exceptions for "Shared (Extension)" folder in "CxLLM-SAFARI Extension (macOS)" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Resources/_locales,
Resources/background.js,
Resources/content.js,
Resources/images,
Resources/manifest.json,
Resources/popup.css,
Resources/popup.html,
Resources/popup.js,
SafariWebExtensionHandler.m,
);
target = 20F2B0D62F99764000E7D2D9 /* CxLLM-SAFARI Extension (macOS) */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
20F2B08F2F99763E00E7D2D9 /* Shared (App) */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
20F2B0FB2F99764000E7D2D9 /* Exceptions for "Shared (App)" folder in "CxLLM-SAFARI (iOS)" target */,
20F2B1052F99764000E7D2D9 /* Exceptions for "Shared (App)" folder in "CxLLM-SAFARI (macOS)" target */,
);
path = "Shared (App)";
sourceTree = "<group>";
};
20F2B0992F99764000E7D2D9 /* Shared (Extension) */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
20F2B10E2F99764000E7D2D9 /* Exceptions for "Shared (Extension)" folder in "CxLLM-SAFARI Extension (iOS)" target */,
20F2B10F2F99764000E7D2D9 /* Exceptions for "Shared (Extension)" folder in "CxLLM-SAFARI Extension (macOS)" target */,
);
explicitFolders = (
Resources/_locales,
Resources/images,
);
path = "Shared (Extension)";
sourceTree = "<group>";
};
20F2B0AB2F99764000E7D2D9 /* iOS (App) */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
20F2B1042F99764000E7D2D9 /* Exceptions for "iOS (App)" folder in "CxLLM-SAFARI (iOS)" target */,
);
path = "iOS (App)";
sourceTree = "<group>";
};
20F2B0C02F99764000E7D2D9 /* macOS (App) */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = "macOS (App)";
sourceTree = "<group>";
};
20F2B0D12F99764000E7D2D9 /* iOS (Extension) */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
20F2B0FF2F99764000E7D2D9 /* Exceptions for "iOS (Extension)" folder in "CxLLM-SAFARI Extension (iOS)" target */,
);
path = "iOS (Extension)";
sourceTree = "<group>";
};
20F2B0DB2F99764000E7D2D9 /* macOS (Extension) */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
20F2B1092F99764000E7D2D9 /* Exceptions for "macOS (Extension)" folder in "CxLLM-SAFARI Extension (macOS)" target */,
);
path = "macOS (Extension)";
sourceTree = "<group>";
};
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
20F2B0A62F99764000E7D2D9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
20F2B0BC2F99764000E7D2D9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
20F2B0CA2F99764000E7D2D9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
20F2B0D42F99764000E7D2D9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
20F2B08A2F99763E00E7D2D9 = {
isa = PBXGroup;
children = (
20F2B08F2F99763E00E7D2D9 /* Shared (App) */,
20F2B0992F99764000E7D2D9 /* Shared (Extension) */,
20F2B0AB2F99764000E7D2D9 /* iOS (App) */,
20F2B0C02F99764000E7D2D9 /* macOS (App) */,
20F2B0D12F99764000E7D2D9 /* iOS (Extension) */,
20F2B0DB2F99764000E7D2D9 /* macOS (Extension) */,
20F2B0AA2F99764000E7D2D9 /* Products */,
);
sourceTree = "<group>";
};
20F2B0AA2F99764000E7D2D9 /* Products */ = {
isa = PBXGroup;
children = (
20F2B0A92F99764000E7D2D9 /* CxLLM-SAFARI.app */,
20F2B0BF2F99764000E7D2D9 /* CxLLM-SAFARI.app */,
20F2B0CD2F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex */,
20F2B0D72F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
20F2B0A82F99764000E7D2D9 /* CxLLM-SAFARI (iOS) */ = {
isa = PBXNativeTarget;
buildConfigurationList = 20F2B1012F99764000E7D2D9 /* Build configuration list for PBXNativeTarget "CxLLM-SAFARI (iOS)" */;
buildPhases = (
20F2B0A52F99764000E7D2D9 /* Sources */,
20F2B0A62F99764000E7D2D9 /* Frameworks */,
20F2B0A72F99764000E7D2D9 /* Resources */,
20F2B1002F99764000E7D2D9 /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
20F2B0D02F99764000E7D2D9 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
20F2B0AB2F99764000E7D2D9 /* iOS (App) */,
);
name = "CxLLM-SAFARI (iOS)";
packageProductDependencies = (
);
productName = "CxLLM-SAFARI (iOS)";
productReference = 20F2B0A92F99764000E7D2D9 /* CxLLM-SAFARI.app */;
productType = "com.apple.product-type.application";
};
20F2B0BE2F99764000E7D2D9 /* CxLLM-SAFARI (macOS) */ = {
isa = PBXNativeTarget;
buildConfigurationList = 20F2B10B2F99764000E7D2D9 /* Build configuration list for PBXNativeTarget "CxLLM-SAFARI (macOS)" */;
buildPhases = (
20F2B0BB2F99764000E7D2D9 /* Sources */,
20F2B0BC2F99764000E7D2D9 /* Frameworks */,
20F2B0BD2F99764000E7D2D9 /* Resources */,
20F2B10A2F99764000E7D2D9 /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
20F2B0DA2F99764000E7D2D9 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
20F2B0C02F99764000E7D2D9 /* macOS (App) */,
);
name = "CxLLM-SAFARI (macOS)";
packageProductDependencies = (
);
productName = "CxLLM-SAFARI (macOS)";
productReference = 20F2B0BF2F99764000E7D2D9 /* CxLLM-SAFARI.app */;
productType = "com.apple.product-type.application";
};
20F2B0CC2F99764000E7D2D9 /* CxLLM-SAFARI Extension (iOS) */ = {
isa = PBXNativeTarget;
buildConfigurationList = 20F2B0FC2F99764000E7D2D9 /* Build configuration list for PBXNativeTarget "CxLLM-SAFARI Extension (iOS)" */;
buildPhases = (
20F2B0C92F99764000E7D2D9 /* Sources */,
20F2B0CA2F99764000E7D2D9 /* Frameworks */,
20F2B0CB2F99764000E7D2D9 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
20F2B0D12F99764000E7D2D9 /* iOS (Extension) */,
);
name = "CxLLM-SAFARI Extension (iOS)";
packageProductDependencies = (
);
productName = "CxLLM-SAFARI Extension (iOS)";
productReference = 20F2B0CD2F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex */;
productType = "com.apple.product-type.app-extension";
};
20F2B0D62F99764000E7D2D9 /* CxLLM-SAFARI Extension (macOS) */ = {
isa = PBXNativeTarget;
buildConfigurationList = 20F2B1062F99764000E7D2D9 /* Build configuration list for PBXNativeTarget "CxLLM-SAFARI Extension (macOS)" */;
buildPhases = (
20F2B0D32F99764000E7D2D9 /* Sources */,
20F2B0D42F99764000E7D2D9 /* Frameworks */,
20F2B0D52F99764000E7D2D9 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
20F2B0DB2F99764000E7D2D9 /* macOS (Extension) */,
);
name = "CxLLM-SAFARI Extension (macOS)";
packageProductDependencies = (
);
productName = "CxLLM-SAFARI Extension (macOS)";
productReference = 20F2B0D72F99764000E7D2D9 /* CxLLM-SAFARI Extension.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
20F2B08B2F99763E00E7D2D9 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastUpgradeCheck = 2630;
TargetAttributes = {
20F2B0A82F99764000E7D2D9 = {
CreatedOnToolsVersion = 26.3;
};
20F2B0BE2F99764000E7D2D9 = {
CreatedOnToolsVersion = 26.3;
};
20F2B0CC2F99764000E7D2D9 = {
CreatedOnToolsVersion = 26.3;
};
20F2B0D62F99764000E7D2D9 = {
CreatedOnToolsVersion = 26.3;
};
};
};
buildConfigurationList = 20F2B08E2F99763E00E7D2D9 /* Build configuration list for PBXProject "CxLLM-SAFARI" */;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 20F2B08A2F99763E00E7D2D9;
minimizedProjectReferenceProxies = 1;
preferredProjectObjectVersion = 77;
productRefGroup = 20F2B0AA2F99764000E7D2D9 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
20F2B0A82F99764000E7D2D9 /* CxLLM-SAFARI (iOS) */,
20F2B0BE2F99764000E7D2D9 /* CxLLM-SAFARI (macOS) */,
20F2B0CC2F99764000E7D2D9 /* CxLLM-SAFARI Extension (iOS) */,
20F2B0D62F99764000E7D2D9 /* CxLLM-SAFARI Extension (macOS) */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
20F2B0A72F99764000E7D2D9 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
20F2B0BD2F99764000E7D2D9 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
20F2B0CB2F99764000E7D2D9 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
20F2B0D52F99764000E7D2D9 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
20F2B0A52F99764000E7D2D9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
20F2B0BB2F99764000E7D2D9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
20F2B0C92F99764000E7D2D9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
20F2B0D32F99764000E7D2D9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
20F2B0D02F99764000E7D2D9 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 20F2B0CC2F99764000E7D2D9 /* CxLLM-SAFARI Extension (iOS) */;
targetProxy = 20F2B0CF2F99764000E7D2D9 /* PBXContainerItemProxy */;
};
20F2B0DA2F99764000E7D2D9 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 20F2B0D62F99764000E7D2D9 /* CxLLM-SAFARI Extension (macOS) */;
targetProxy = 20F2B0D92F99764000E7D2D9 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
20F2B0FD2F99764000E7D2D9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = DKWVC9FQJY;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (Extension)/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "CxLLM-SAFARI Extension";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
);
PRODUCT_BUNDLE_IDENTIFIER = "cxai-studio.CxLLM-SAFARI.Extension";
PRODUCT_NAME = "CxLLM-SAFARI Extension";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
20F2B0FE2F99764000E7D2D9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = DKWVC9FQJY;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (Extension)/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "CxLLM-SAFARI Extension";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
);
PRODUCT_BUNDLE_IDENTIFIER = "cxai-studio.CxLLM-SAFARI.Extension";
PRODUCT_NAME = "CxLLM-SAFARI Extension";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
20F2B1022F99764000E7D2D9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = DKWVC9FQJY;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (App)/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "CxLLM-SAFARI";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
"-framework",
WebKit,
);
PRODUCT_BUNDLE_IDENTIFIER = "cxai-studio.CxLLM-SAFARI";
PRODUCT_NAME = "CxLLM-SAFARI";
SDKROOT = iphoneos;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
20F2B1032F99764000E7D2D9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = DKWVC9FQJY;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (App)/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "CxLLM-SAFARI";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
"-framework",
WebKit,
);
PRODUCT_BUNDLE_IDENTIFIER = "cxai-studio.CxLLM-SAFARI";
PRODUCT_NAME = "CxLLM-SAFARI";
SDKROOT = iphoneos;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
20F2B1072F99764000E7D2D9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = DKWVC9FQJY;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_USER_SELECTED_FILES = readonly;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "macOS (Extension)/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "CxLLM-SAFARI Extension";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
);
PRODUCT_BUNDLE_IDENTIFIER = "cxai-studio.CxLLM-SAFARI.Extension";
PRODUCT_NAME = "CxLLM-SAFARI Extension";
SDKROOT = macosx;
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
};
name = Debug;
};
20F2B1082F99764000E7D2D9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = DKWVC9FQJY;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_USER_SELECTED_FILES = readonly;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "macOS (Extension)/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "CxLLM-SAFARI Extension";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
);
PRODUCT_BUNDLE_IDENTIFIER = "cxai-studio.CxLLM-SAFARI.Extension";
PRODUCT_NAME = "CxLLM-SAFARI Extension";
SDKROOT = macosx;
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
};
name = Release;
};
20F2B10C2F99764000E7D2D9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = DKWVC9FQJY;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
ENABLE_USER_SELECTED_FILES = readonly;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "CxLLM-SAFARI";
INFOPLIST_KEY_NSMainStoryboardFile = Main;
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
"-framework",
WebKit,
);
PRODUCT_BUNDLE_IDENTIFIER = "cxai-studio.CxLLM-SAFARI";
PRODUCT_NAME = "CxLLM-SAFARI";
REGISTER_APP_GROUPS = YES;
SDKROOT = macosx;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
};
name = Debug;
};
20F2B10D2F99764000E7D2D9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = DKWVC9FQJY;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
ENABLE_USER_SELECTED_FILES = readonly;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "CxLLM-SAFARI";
INFOPLIST_KEY_NSMainStoryboardFile = Main;
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
"-framework",
WebKit,
);
PRODUCT_BUNDLE_IDENTIFIER = "cxai-studio.CxLLM-SAFARI";
PRODUCT_NAME = "CxLLM-SAFARI";
REGISTER_APP_GROUPS = YES;
SDKROOT = macosx;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
};
name = Release;
};
20F2B1102F99764000E7D2D9 /* 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;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = DKWVC9FQJY;
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;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
};
name = Debug;
};
20F2B1112F99764000E7D2D9 /* 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;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = DKWVC9FQJY;
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;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
20F2B08E2F99763E00E7D2D9 /* Build configuration list for PBXProject "CxLLM-SAFARI" */ = {
isa = XCConfigurationList;
buildConfigurations = (
20F2B1102F99764000E7D2D9 /* Debug */,
20F2B1112F99764000E7D2D9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
20F2B0FC2F99764000E7D2D9 /* Build configuration list for PBXNativeTarget "CxLLM-SAFARI Extension (iOS)" */ = {
isa = XCConfigurationList;
buildConfigurations = (
20F2B0FD2F99764000E7D2D9 /* Debug */,
20F2B0FE2F99764000E7D2D9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
20F2B1012F99764000E7D2D9 /* Build configuration list for PBXNativeTarget "CxLLM-SAFARI (iOS)" */ = {
isa = XCConfigurationList;
buildConfigurations = (
20F2B1022F99764000E7D2D9 /* Debug */,
20F2B1032F99764000E7D2D9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
20F2B1062F99764000E7D2D9 /* Build configuration list for PBXNativeTarget "CxLLM-SAFARI Extension (macOS)" */ = {
isa = XCConfigurationList;
buildConfigurations = (
20F2B1072F99764000E7D2D9 /* Debug */,
20F2B1082F99764000E7D2D9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
20F2B10B2F99764000E7D2D9 /* Build configuration list for PBXNativeTarget "CxLLM-SAFARI (macOS)" */ = {
isa = XCConfigurationList;
buildConfigurations = (
20F2B10C2F99764000E7D2D9 /* Debug */,
20F2B10D2F99764000E7D2D9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 20F2B08B2F99763E00E7D2D9 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 CxAI-LLM
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

43
Makefile Normal file
View File

@ -0,0 +1,43 @@
# Makefile — common entry points for CxLLM-SAFARI
SHELL := /bin/bash
.DEFAULT_GOAL := help
PROJECT := CxLLM-SAFARI
SCHEME ?= $(PROJECT)
CONFIG ?= Debug
DERIVED ?= build
.PHONY: help build test lint clean fmt info
help: ## show this help
@grep -E '^[a-zA-Z_-]+:.*## ' $(MAKEFILE_LIST) | awk -F':.*## ' '{printf " %-12s %s\n", $$1, $$2}'
info: ## show project info
@echo "project : $(PROJECT)"
@echo "scheme : $(SCHEME)"
@echo "config : $(CONFIG)"
@echo "derived : $(DERIVED)"
build: ## xcodebuild build (skip if no .xcodeproj)
@if ls *.xcodeproj 1>/dev/null 2>&1; then \
xcodebuild -project "$(PROJECT).xcodeproj" -scheme "$(SCHEME)" -configuration "$(CONFIG)" -derivedDataPath "$(DERIVED)" build | xcbeautify || true ; \
elif [ -f Package.swift ]; then \
swift build -c $(shell echo "$(CONFIG)" | tr '[:upper:]' '[:lower:]') ; \
else echo "no buildable target" ; fi
test: ## xcodebuild test
@if ls *.xcodeproj 1>/dev/null 2>&1; then \
xcodebuild -project "$(PROJECT).xcodeproj" -scheme "$(SCHEME)" -configuration "$(CONFIG)" -derivedDataPath "$(DERIVED)" test | xcbeautify || true ; \
elif [ -f Package.swift ]; then \
swift test ; \
else echo "no testable target" ; fi
lint: ## swiftlint + swiftformat (no-op if missing)
@command -v swiftlint >/dev/null && swiftlint --quiet || echo "swiftlint not installed"
@command -v swiftformat >/dev/null && swiftformat --lint . || echo "swiftformat not installed"
fmt: ## swiftformat in-place
@command -v swiftformat >/dev/null && swiftformat . || echo "swiftformat not installed"
clean: ## remove build artifacts
rm -rf "$(DERIVED)" .build DerivedData

53
README.md Normal file
View File

@ -0,0 +1,53 @@
# CxLLM-SAFARI
> CxLLM Safari Web Extension
Safari Web Extension (iOS + macOS shared) that injects the CxLLM assistant overlay into web pages and forwards selections to the runtime.
[![ci](https://git.cxllm-studio.com/CxAI-LLM/CxLLM-SAFARI/actions/workflows/ci.yml/badge.svg)](https://git.cxllm-studio.com/CxAI-LLM/CxLLM-SAFARI/actions)
[![license](https://img.shields.io/badge/license-MIT-7C3AED)](LICENSE)
[![category](https://img.shields.io/badge/category-safari-extension-1F6FEB)](#)
## Overview
`CxLLM-SAFARI` is part of the **CxLLM** product family — a layered runtime that
spans Apple kernel extensions, user-space frameworks, host applications, and
spatial / web surfaces. This module focuses on **safari-extension** concerns and is
intentionally narrow so that the CxLLM monorepo can compose targets without
pulling in unrelated dependencies.
## Repository layout
- `CxLLM-SAFARI/` — primary source folder (matches the Xcode group / SwiftPM root).
- `CxLLM-SAFARI.xcodeproj` — Xcode project (where applicable).
- `Makefile` — common entry points (`build`, `test`, `lint`, `clean`).
- `.github/workflows/ci.yml` — CI pipeline (Xcode build + lint).
- `docs/` — architecture notes and ADRs.
## Quick start
```bash
# Build (defaults to Debug for the host platform):
make build
# Run the full test suite (unit + UI where applicable):
make test
# Static analysis + format check:
make lint
```
## Versioning
This module follows [Semantic Versioning 2.0](https://semver.org/) and is
released in lock-step with the umbrella **CxLLM** product line. See
[`CHANGELOG.md`](CHANGELOG.md) for the user-facing change log.
## Security
Please report security issues per [`SECURITY.md`](SECURITY.md). Do **not**
open public issues for vulnerabilities.
## License
Released under the [MIT License](LICENSE) © 2026 CxAI-LLM.

19
SECURITY.md Normal file
View File

@ -0,0 +1,19 @@
# Security policy for CxLLM-SAFARI
## Reporting a vulnerability
Please email **security@cxllm-studio.com** with:
- A description of the vulnerability and its impact.
- Steps to reproduce, ideally with a minimal proof-of-concept.
- The affected version(s) / commit SHAs.
We aim to acknowledge within **2 business days** and to publish a fix or
mitigation within **30 days** for high-severity issues.
Do **not** open a public Gitea / GitHub issue for vulnerabilities.
## Supported versions
Only the `main` branch and the most recent tagged release receive security
updates.

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,85 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="stylesheet" href="../Style.css">
<script src="../Script.js" defer></script>
</head>
<body>
<img src="../Icon.png" width="128" height="128" alt="CxLLM-SAFARI Icon">
<p class="platform-ios">You can turn on CxLLM-SAFARIs Safari extension in Settings.</p>
<p class="platform-mac state-unknown">You can turn on CxLLM-SAFARIs extension in Safari Extensions preferences.</p>
<p class="platform-mac state-on">CxLLM-SAFARIs extension is currently on. You can turn it off in Safari Extensions preferences.</p>
<p class="platform-mac state-off">CxLLM-SAFARIs extension is currently off. You can turn it on in Safari Extensions preferences.</p>
<button class="platform-mac open-preferences">Quit and Open Safari Extensions Preferences…</button>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@ -0,0 +1,24 @@
function show(platform, enabled, useSettingsInsteadOfPreferences) {
document.body.classList.add(`platform-${platform}`);
if (useSettingsInsteadOfPreferences) {
document.getElementsByClassName('platform-mac state-on')[0].innerText = "CxLLM-SAFARIs extension is currently on. You can turn it off in the Extensions section of Safari Settings.";
document.getElementsByClassName('platform-mac state-off')[0].innerText = "CxLLM-SAFARIs extension is currently off. You can turn it on in the Extensions section of Safari Settings.";
document.getElementsByClassName('platform-mac state-unknown')[0].innerText = "You can turn on CxLLM-SAFARIs extension in the Extensions section of Safari Settings.";
document.getElementsByClassName('platform-mac open-preferences')[0].innerText = "Quit and Open Safari Settings…";
}
if (typeof enabled === "boolean") {
document.body.classList.toggle(`state-on`, enabled);
document.body.classList.toggle(`state-off`, !enabled);
} else {
document.body.classList.remove(`state-on`);
document.body.classList.remove(`state-off`);
}
}
function openPreferences() {
webkit.messageHandlers.controller.postMessage("open-preferences");
}
document.querySelector("button.open-preferences").addEventListener("click", openPreferences);

View File

@ -0,0 +1,61 @@
* {
-webkit-user-select: none;
-webkit-user-drag: none;
cursor: default;
}
:root {
color-scheme: light dark;
--spacing: 20px;
}
html {
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
gap: var(--spacing);
margin: 0 calc(var(--spacing) * 2);
height: 100%;
font: -apple-system-short-body;
text-align: center;
}
body:not(.platform-mac, .platform-ios) :is(.platform-mac, .platform-ios) {
display: none;
}
body.platform-ios .platform-mac {
display: none;
}
body.platform-mac .platform-ios {
display: none;
}
body.platform-ios .platform-mac {
display: none;
}
body:not(.state-on, .state-off) :is(.state-on, .state-off) {
display: none;
}
body.state-on :is(.state-off, .state-unknown) {
display: none;
}
body.state-off :is(.state-on, .state-unknown) {
display: none;
}
button {
font-size: 1em;
}

View File

@ -0,0 +1,26 @@
//
// ViewController.h
// Shared (App)
//
// Created by Stephen Carter on 4/22/26.
//
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import <UIKit/UIKit.h>
typedef UIViewController PlatformViewController;
#elif TARGET_OS_OSX
#import <Cocoa/Cocoa.h>
typedef NSViewController PlatformViewController;
#endif
@interface ViewController : PlatformViewController
@end

View File

@ -0,0 +1,76 @@
//
// ViewController.m
// Shared (App)
//
// Created by Stephen Carter on 4/22/26.
//
#import "ViewController.h"
#import <WebKit/WebKit.h>
#if TARGET_OS_OSX
#import <SafariServices/SafariServices.h>
#endif
static NSString * const extensionBundleIdentifier = @"cxai-studio.CxLLM-SAFARI.Extension";
@interface ViewController () <WKNavigationDelegate, WKScriptMessageHandler>
@property (nonatomic) IBOutlet WKWebView *webView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_webView.navigationDelegate = self;
#if TARGET_OS_IOS
_webView.scrollView.scrollEnabled = NO;
#endif
[_webView.configuration.userContentController addScriptMessageHandler:self name:@"controller"];
[_webView loadFileURL:[NSBundle.mainBundle URLForResource:@"Main" withExtension:@"html"] allowingReadAccessToURL:NSBundle.mainBundle.resourceURL];
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
#if TARGET_OS_IOS
[webView evaluateJavaScript:@"show('ios')" completionHandler:nil];
#elif TARGET_OS_OSX
[webView evaluateJavaScript:@"show('mac')" completionHandler:nil];
[SFSafariExtensionManager getStateOfSafariExtensionWithIdentifier:extensionBundleIdentifier completionHandler:^(SFSafariExtensionState *state, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (!state) {
// Insert code to inform the user something went wrong.
return;
}
NSString *isExtensionEnabledAsString = state.isEnabled ? @"true" : @"false";
if (@available(macOS 13, *))
[webView evaluateJavaScript:[NSString stringWithFormat:@"show('mac', %@, true)", isExtensionEnabledAsString] completionHandler:nil];
else
[webView evaluateJavaScript:[NSString stringWithFormat:@"show('mac', %@, false)", isExtensionEnabledAsString] completionHandler:nil];
});
}];
#endif
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
#if TARGET_OS_OSX
if (![message.body isEqualToString:@"open-preferences"])
return;
[SFSafariApplication showPreferencesForExtensionWithIdentifier:extensionBundleIdentifier completionHandler:^(NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[NSApp terminate:self];
});
}];
#endif
}
@end

View File

@ -0,0 +1,10 @@
{
"extension_name": {
"message": "CxLLM-SAFARI",
"description": "The display name for the extension."
},
"extension_description": {
"message": "This is CxLLM-SAFARI. You should tell us what your extension does here.",
"description": "Description of what the extension does."
}
}

View File

@ -0,0 +1,6 @@
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log("Received request: ", request);
if (request.greeting === "hello")
return Promise.resolve({ farewell: "goodbye" });
});

View File

@ -0,0 +1,7 @@
browser.runtime.sendMessage({ greeting: "hello" }).then((response) => {
console.log("Received response: ", response);
});
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log("Received request: ", request);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.9375 15.9453">
<path d="M7.96875 15.9375C12.3281 15.9375 15.9375 12.3203 15.9375 7.96875C15.9375 3.60938 12.3203 0 7.96094 0C3.60938 0 0 3.60938 0 7.96875C0 12.3203 3.61719 15.9375 7.96875 15.9375ZM7.96875 14.6094C4.28125 14.6094 1.33594 11.6562 1.33594 7.96875C1.33594 4.28125 4.27344 1.32812 7.96094 1.32812C11.6484 1.32812 14.6094 4.28125 14.6094 7.96875C14.6094 11.6562 11.6562 14.6094 7.96875 14.6094Z" />
<path d="M4.53906 8.50781C4.53906 8.70312 4.69531 8.84375 4.89844 8.84375L7.54688 8.84375L6.13281 12.6406C5.94531 13.1406 6.47656 13.4141 6.80469 13.0078L11.0859 7.63281C11.1719 7.53906 11.2188 7.42969 11.2188 7.32812C11.2188 7.13281 11.0625 6.99219 10.8594 6.99219L8.21094 6.99219L9.625 3.19531C9.8125 2.69531 9.28125 2.42188 8.95312 2.82031L4.67188 8.19531C4.58594 8.29688 4.53906 8.40625 4.53906 8.50781Z" />
</svg>

After

Width:  |  Height:  |  Size: 943 B

View File

@ -0,0 +1,33 @@
{
"manifest_version": 3,
"default_locale": "en",
"name": "__MSG_extension_name__",
"description": "__MSG_extension_description__",
"version": "1.0",
"icons": {
"48": "images/icon-48.png",
"96": "images/icon-96.png",
"128": "images/icon-128.png",
"256": "images/icon-256.png",
"512": "images/icon-512.png"
},
"background": {
"scripts": [ "background.js" ],
"type": "module"
},
"content_scripts": [{
"js": [ "content.js" ],
"matches": [ "*://example.com/*" ]
}],
"action": {
"default_popup": "popup.html",
"default_icon": "images/toolbar-icon.svg"
},
"permissions": [ ]
}

View File

@ -0,0 +1,15 @@
:root {
color-scheme: light dark;
}
body {
width: 100px;
padding: 10px;
font-family: system-ui;
text-align: center;
}
@media (prefers-color-scheme: dark) {
/* Dark Mode styles go here. */
}

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="popup.css">
<script type="module" src="popup.js"></script>
</head>
<body>
<strong>Hello World!</strong>
</body>
</html>

View File

@ -0,0 +1 @@
console.log("Hello World!", browser);

View File

@ -0,0 +1,12 @@
//
// SafariWebExtensionHandler.h
// Shared (Extension)
//
// Created by Stephen Carter on 4/22/26.
//
#import <Foundation/Foundation.h>
@interface SafariWebExtensionHandler : NSObject <NSExtensionRequestHandling>
@end

View File

@ -0,0 +1,43 @@
//
// SafariWebExtensionHandler.m
// Shared (Extension)
//
// Created by Stephen Carter on 4/22/26.
//
#import "SafariWebExtensionHandler.h"
#import <SafariServices/SafariServices.h>
@implementation SafariWebExtensionHandler
- (void)beginRequestWithExtensionContext:(NSExtensionContext *)context {
NSExtensionItem *request = context.inputItems.firstObject;
NSUUID *profile;
if (@available(iOS 17.0, macOS 14.0, *)) {
profile = request.userInfo[SFExtensionProfileKey];
} else {
profile = request.userInfo[@"profile"];
}
id message;
if (@available(iOS 15.0, macOS 11.0, *)) {
message = request.userInfo[SFExtensionMessageKey];
} else {
message = request.userInfo[@"message"];
}
NSLog(@"Received message from browser.runtime.sendNativeMessage: %@ (profile: %@)", message, profile.UUIDString ?: @"none");
NSExtensionItem *response = [[NSExtensionItem alloc] init];
if (@available(iOS 15.0, macOS 11.0, *)) {
response.userInfo = @{ SFExtensionMessageKey: @{ @"echo": message } };
} else {
response.userInfo = @{ @"message": @{ @"echo": message } };
}
[context completeRequestReturningItems:@[ response ] completionHandler:nil];
}
@end

24
docs/ARCHITECTURE.md Normal file
View File

@ -0,0 +1,24 @@
# Architecture — CxLLM-SAFARI
> CxLLM Safari Web Extension
## Goals
- Provide a focused, well-tested safari-extension surface for the CxLLM family.
- Stay deployable on its own (no monorepo coupling).
- Keep the public ABI small and explicitly versioned.
## Boundaries
```text
+----------------------+
client --> | CxLLM-SAFARI |
+----------+-----------+
|
v
CxLLM runtime / kernel
```
## Decisions
- See `docs/adr/` for Architecture Decision Records.

View File

@ -0,0 +1,13 @@
# 1. Record architecture decisions
## Status
Accepted
## Context
We need a lightweight, append-only log of architectural choices.
## Decision
Use Markdown ADRs in `docs/adr/`, numbered sequentially.
## Consequences
Future maintainers can read the chain of decisions without spelunking PR history.

12
iOS (App)/AppDelegate.h Normal file
View File

@ -0,0 +1,12 @@
//
// AppDelegate.h
// iOS (App)
//
// Created by Stephen Carter on 4/22/26.
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@end

21
iOS (App)/AppDelegate.m Normal file
View File

@ -0,0 +1,21 @@
//
// AppDelegate.m
// iOS (App)
//
// Created by Stephen Carter on 4/22/26.
//
#import "AppDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
}
@end

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19085" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19082"/>
<capability name="Image references" minToolsVersion="12.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6HG-Um-bch">
<rect key="frame" x="142" y="385" width="128" height="128"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<imageReference key="image" image="LargeIcon"/>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LargeIcon" width="128" height="128"/>
</resources>
</document>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<wkWebView contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="RDB-ib-igF">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<wkWebViewConfiguration key="configuration">
<audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
<wkPreferences key="preferences"/>
</wkWebViewConfiguration>
</wkWebView>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="webView" destination="RDB-ib-igF" id="avx-RC-qRB"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

25
iOS (App)/Info.plist Normal file
View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
</dict>
</plist>

14
iOS (App)/SceneDelegate.h Normal file
View File

@ -0,0 +1,14 @@
//
// SceneDelegate.h
// iOS (App)
//
// Created by Stephen Carter on 4/22/26.
//
#import <UIKit/UIKit.h>
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@property (strong, nonatomic) UIWindow * window;
@end

12
iOS (App)/SceneDelegate.m Normal file
View File

@ -0,0 +1,12 @@
//
// SceneDelegate.m
// iOS (App)
//
// Created by Stephen Carter on 4/22/26.
//
#import "SceneDelegate.h"
@implementation SceneDelegate
@end

18
iOS (App)/main.m Normal file
View File

@ -0,0 +1,18 @@
//
// main.m
// iOS (App)
//
// Created by Stephen Carter on 4/22/26.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
NSString *appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.Safari.web-extension</string>
<key>NSExtensionPrincipalClass</key>
<string>SafariWebExtensionHandler</string>
</dict>
</dict>
</plist>

12
macOS (App)/AppDelegate.h Normal file
View File

@ -0,0 +1,12 @@
//
// AppDelegate.h
// macOS (App)
//
// Created by Stephen Carter on 4/22/26.
//
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@end

20
macOS (App)/AppDelegate.m Normal file
View File

@ -0,0 +1,20 @@
//
// AppDelegate.m
// macOS (App)
//
// Created by Stephen Carter on 4/22/26.
//
#import "AppDelegate.h"
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
// Override point for customization after application launch.
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
return YES;
}
@end

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="19085" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19085"/>
<plugIn identifier="com.apple.WebKit2IBPlugin" version="19085"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Application-->
<scene sceneID="JPo-4y-FX3">
<objects>
<application id="hnw-xV-0zn" sceneMemberID="viewController">
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="CxLLM-SAFARI" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="CxLLM-SAFARI" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About CxLLM-SAFARI" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Hide CxLLM-SAFARI" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit CxLLM-SAFARI" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="CxLLM-SAFARI Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
</connections>
</application>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider=""/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="76" y="-134"/>
</scene>
<!--Window Controller-->
<scene sceneID="R2V-B0-nI4">
<objects>
<windowController showSeguePresentationStyle="single" id="B8D-0N-5wS" sceneMemberID="viewController">
<window key="window" title="CxLLM-SAFARI" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" animationBehavior="default" id="IQv-IB-iLA">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenNone="YES"/>
<rect key="contentRect" x="196" y="240" width="425" height="325"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<connections>
<outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
</connections>
</window>
<connections>
<segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
</connections>
</windowController>
<customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="250"/>
</scene>
<!--View Controller-->
<scene sceneID="hIz-AP-VOD">
<objects>
<viewController id="XfG-lQ-9wD" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
<view key="view" id="m2S-Jp-Qdl">
<rect key="frame" x="0.0" y="0.0" width="425" height="325"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<wkWebView wantsLayer="YES" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eOr-cG-IQY">
<rect key="frame" x="0.0" y="0.0" width="425" height="325"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<wkWebViewConfiguration key="configuration">
<audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
<wkPreferences key="preferences"/>
</wkWebViewConfiguration>
</wkWebView>
</subviews>
</view>
<connections>
<outlet property="webView" destination="eOr-cG-IQY" id="GFe-mU-dBY"/>
</connections>
</viewController>
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="655"/>
</scene>
</scenes>
</document>

15
macOS (App)/main.m Normal file
View File

@ -0,0 +1,15 @@
//
// main.m
// macOS (App)
//
// Created by Stephen Carter on 4/22/26.
//
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
}
return NSApplicationMain(argc, argv);
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.Safari.web-extension</string>
<key>NSExtensionPrincipalClass</key>
<string>SafariWebExtensionHandler</string>
</dict>
</dict>
</plist>