├── 1
├── .DS_Store
├── .github
├── ISSUE_TEMPLATE
│ └── feature_request.md
└── workflows
│ ├── iOS-test.yml
│ └── macOS-test.yml
├── .gitignore
├── .swift-version
├── .swiftformat.yml
├── .swiftlint.yml
├── CODE_OF_CONDUCT.md
├── Fragment--iOS--Info.plist
├── Fragment--macOS--Info.plist
├── Fragment-iOS-Tests
└── Fragment_iOS_Tests.swift
├── Fragment-macOS-Tests
└── Fragment_macOS_Tests.swift
├── Fragment.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcuserdata
│ │ └── danhart.xcuserdatad
│ │ └── IDEFindNavigatorScopes.plist
├── xcshareddata
│ ├── xcbaselines
│ │ ├── 52BABB4327E7DE1900999F01.xcbaseline
│ │ │ ├── 21B39069-B75E-4407-A8FF-E868E50C66DA.plist
│ │ │ └── Info.plist
│ │ └── 52BABB5227E7DE3300999F01.xcbaseline
│ │ │ ├── AE7DEE5B-077D-4779-9906-03EA21164357.plist
│ │ │ └── Info.plist
│ └── xcschemes
│ │ ├── iOS.xcscheme
│ │ └── macOS.xcscheme
└── xcuserdata
│ ├── danhart.xcuserdatad
│ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── dhart.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── FragmentSharedTests
└── TestApp.swift
├── LICENSE.md
├── README.md
├── Shared
├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon-macOS.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-1025.png
│ │ ├── Icon-128.png
│ │ ├── Icon-256.png
│ │ ├── Icon-257.png
│ │ ├── Icon-32.png
│ │ ├── Icon-33.png
│ │ ├── Icon-512.png
│ │ ├── Icon-513.png
│ │ └── Icon-65.png
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── switchify_icon_v01-1024.png
│ │ ├── switchify_icon_v01-20.png
│ │ ├── switchify_icon_v01-20@2x.png
│ │ ├── switchify_icon_v01-20@3x.png
│ │ ├── switchify_icon_v01-29.png
│ │ ├── switchify_icon_v01-29@2x.png
│ │ ├── switchify_icon_v01-29@3x.png
│ │ ├── switchify_icon_v01-40.png
│ │ ├── switchify_icon_v01-40@2x.png
│ │ ├── switchify_icon_v01-40@3x.png
│ │ ├── switchify_icon_v01-60@2x.png
│ │ ├── switchify_icon_v01-60@3x.png
│ │ ├── switchify_icon_v01-76.png
│ │ ├── switchify_icon_v01-76@2x.png
│ │ └── switchify_icon_v01-83.5@2x.png
│ └── Contents.json
├── ClipboardHelper.swift
├── Constants.swift
├── Extensions
│ ├── Collections+ifExistsAt.swift
│ └── OctoKitExtensions
│ │ └── GistExtensions
│ │ ├── Gist+Custom.swift
│ │ ├── Gist+language.swift
│ │ └── Gist+text.swift
├── Fragment.xcdatamodeld
│ ├── .xccurrentversion
│ └── Shared.xcdatamodel
│ │ └── contents
├── FragmentApp.swift
├── Handler
│ └── SessionHandler.swift
├── Models
│ ├── Errors
│ │ └── FragmentError.swift
│ ├── Language.swift
│ └── Visibility.swift
├── Views
│ ├── Add
│ │ └── AddGistView.swift
│ ├── AuthenticationView.swift
│ ├── CodeViews
│ │ └── CodeView.swift
│ ├── Container
│ │ ├── ContainerView.swift
│ │ └── ListView.swift
│ ├── GistViews
│ │ └── GistRow.swift
│ ├── MainView.swift
│ ├── SettingsView.swift
│ └── Support
│ │ └── SupportThisAppView.swift
└── WebLauncher.swift
├── Sourceless
└── 2011B4FA-8E52-4464-A976-76AA07DBDDE7.json
├── autocommit.sh
└── macOS
└── macOS.entitlements
/1:
--------------------------------------------------------------------------------
1 | Merge branch 'main' of https://github.com/dan-hart/Fragment
2 | # Please enter a commit message to explain why this merge is necessary,
3 | # especially if it merges an updated upstream into a topic branch.
4 | #
5 | # Lines starting with '#' will be ignored, and an empty message aborts
6 | # the commit.
7 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/.DS_Store
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
22 | [](https://www.buymeacoffee.com/codedbydan)
23 |
--------------------------------------------------------------------------------
/.github/workflows/iOS-test.yml:
--------------------------------------------------------------------------------
1 | name: ios tests
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | test:
7 | runs-on: macos-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - name: Run ios tests
11 | run: xcodebuild test -destination 'name=iPhone 13' -scheme 'iOS' -enableCodeCoverage YES
12 | - name: Upload coverage to Codecov
13 | run: bash <(curl -s https://codecov.io/bash) -J 'Fragment'
14 | - uses: sarisia/actions-status-discord@v1
15 | if: always()
16 | with:
17 | webhook: ${{ secrets.DISCORD_WEBHOOK }}
18 | title: "iOS tests"
19 | description: "Run all iOS tests"
20 | username: Github
21 | avatar_url: ${{ secrets.AVATAR_URL }}
22 |
--------------------------------------------------------------------------------
/.github/workflows/macOS-test.yml:
--------------------------------------------------------------------------------
1 | name: macOS tests
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | test:
7 | runs-on: macos-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - name: Run macOS tests
11 | run: xcodebuild test -scheme 'macOS' -enableCodeCoverage YES
12 | - name: Upload coverage to Codecov
13 | run: bash <(curl -s https://codecov.io/bash) -J 'Fragment'
14 | - uses: sarisia/actions-status-discord@v1
15 | if: always()
16 | with:
17 | webhook: ${{ secrets.DISCORD_WEBHOOK }}
18 | title: "iOS tests"
19 | description: "Run all macOS tests"
20 | username: Github
21 | avatar_url: ${{ secrets.AVATAR_URL }}
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 | .DS_Store
5 |
6 | ## User settings
7 | xcuserdata/
8 |
9 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
10 | *.xcscmblueprint
11 | *.xccheckout
12 |
13 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
14 | build/
15 | DerivedData/
16 | *.moved-aside
17 | *.pbxuser
18 | !default.pbxuser
19 | *.mode1v3
20 | !default.mode1v3
21 | *.mode2v3
22 | !default.mode2v3
23 | *.perspectivev3
24 | !default.perspectivev3
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 |
29 | ## App packaging
30 | *.ipa
31 | *.dSYM.zip
32 | *.dSYM
33 |
34 | ## Playgrounds
35 | timeline.xctimeline
36 | playground.xcworkspace
37 |
38 | # Swift Package Manager
39 | #
40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
41 | # Packages/
42 | # Package.pins
43 | # Package.resolved
44 | # *.xcodeproj
45 | #
46 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
47 | # hence it is not needed unless you have added a package configuration file to your project
48 | # .swiftpm
49 |
50 | .build/
51 |
52 | # CocoaPods
53 | #
54 | # We recommend against adding the Pods directory to your .gitignore. However
55 | # you should judge for yourself, the pros and cons are mentioned at:
56 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
57 | #
58 | # Pods/
59 | #
60 | # Add this line if you want to avoid checking in source code from the Xcode workspace
61 | # *.xcworkspace
62 |
63 | # Carthage
64 | #
65 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
66 | # Carthage/Checkouts
67 |
68 | Carthage/Build/
69 |
70 | # Accio dependency management
71 | Dependencies/
72 | .accio/
73 |
74 | # fastlane
75 | #
76 | # It is recommended to not store the screenshots in the git repo.
77 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
78 | # For more information about the recommended setup visit:
79 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
80 |
81 | fastlane/report.xml
82 | fastlane/Preview.html
83 | fastlane/screenshots/**/*.png
84 | fastlane/test_output
85 |
86 | # Code Injection
87 | #
88 | # After new code Injection tools there's a generated folder /iOSInjectionProject
89 | # https://github.com/johnno1962/injectionforxcode
90 |
91 | iOSInjectionProject/
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.0
2 |
--------------------------------------------------------------------------------
/.swiftformat.yml:
--------------------------------------------------------------------------------
1 | # See: https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md
2 |
3 | --swiftversion 5.0
4 |
5 | # format options
6 | --indent 4
7 | --wraparguments after-first
8 | --wrapparameters after-first
9 | --wrapcollections before-first
10 | --funcattributes same-line
11 | --typeattributes same-line
12 | --groupedextension MARK: - %c
13 | --categorymark MARK: - %c
14 | --lineaftermarks false
15 |
16 | # opt-in
17 | --enable \
18 | modifierorder, \
19 | acronyms, \
20 | andOperator, \
21 | blankLinesBetweenImports, \
22 | blockComments, \
23 | isEmpty, \
24 | markTypes, \
25 | organizeDeclarations, \
26 | sortedSwitchCases, \
27 | wrapConditionalBodies, \
28 | wrapEnumCases, \
29 | wrapSwitchCases
30 |
31 | # rules
32 | --disable \
33 | blankLinesAroundMark, \
34 | emptyBraces, \
35 | linebreakAtEndOfFile, \
36 | unusedArguments, \
37 | wrapMultilineStatementBraces
38 |
39 | # See: https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md
40 |
41 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | # https://realm.github.io/SwiftLint/rule-directory.html
2 |
3 | opt_in_rules:
4 | - fatal_error_message
5 | - collection_alignment
6 | - empty_count
7 | - contains_over_filter_count
8 | - convenience_type
9 | - discarded_notification_center_observer
10 | - discouraged_assert
11 | - discouraged_object_literal
12 | - empty_collection_literal
13 | - empty_string
14 | - empty_xctest_method
15 | - file_name_no_space
16 | - function_default_parameter_at_end
17 | - ibinspectable_in_extension
18 | - identical_operands
19 | - implicitly_unwrapped_optional
20 | - missing_docs
21 | - modifier_order
22 | - multiline_arguments
23 | - multiline_parameters
24 | - orphaned_doc_comment
25 | - overridden_super_call # Very important to remember to call super.viewDidLoad() etc.
26 | - override_in_extension
27 | - redundant_nil_coalescing
28 | - redundant_type_annotation
29 | - strong_iboutlet
30 | - unneeded_parentheses_in_closure_argument
31 | - unused_import
32 | - yoda_condition
33 |
34 | disabled_rules:
35 | - function_parameter_count
36 | - statement_position
37 | - trailing_comma
38 | - unused_closure_parameter
39 | - closure_parameter_position
40 | - class_delegate_protocol
41 | - compiler_protocol_init
42 | - reduce_boolean
43 | - multiple_closures_with_trailing_closure
44 | - no_space_in_method_call
45 | - type_name
46 | - opening_brace
47 | - line_length
48 |
49 | # https://realm.github.io/SwiftLint/rule-directory.html
50 |
51 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | conduct@codedbydan.com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/Fragment--iOS--Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIconName
6 | AppIcon
7 | ITSAppUsesNonExemptEncryption
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Fragment--macOS--Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIconName
6 | AppIcon-macOS
7 | ITSAppUsesNonExemptEncryption
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Fragment-iOS-Tests/Fragment_iOS_Tests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Fragment_iOS_Tests.swift
3 | // Fragment-iOS-Tests
4 | //
5 | // Created by Dan Hart on 3/20/22.
6 | //
7 |
8 | @testable import Fragment
9 | import XCTest
10 |
11 | class Fragment_iOS_Tests: XCTestCase {
12 | func testApp() {
13 | TestApp.appTest()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Fragment-macOS-Tests/Fragment_macOS_Tests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Fragment_macOS_Tests.swift
3 | // Fragment-macOS-Tests
4 | //
5 | // Created by Dan Hart on 3/20/22.
6 | //
7 |
8 | @testable import Fragment
9 | import XCTest
10 |
11 | class Fragment_macOS_Tests: XCTestCase {
12 | func testApp() {
13 | TestApp.appTest()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 55;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 522915B227E778300087530C /* GistRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522915B127E778300087530C /* GistRow.swift */; };
11 | 522915B327E778300087530C /* GistRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522915B127E778300087530C /* GistRow.swift */; };
12 | 522915C027E789E20087530C /* OctoKit in Frameworks */ = {isa = PBXBuildFile; productRef = 522915BF27E789E20087530C /* OctoKit */; };
13 | 5254DDFF27EF8224005BAC56 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = 5254DDFE27EF8224005BAC56 /* SFSafeSymbols */; };
14 | 5254DE0127EF8331005BAC56 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5254DE0027EF8331005BAC56 /* Constants.swift */; };
15 | 5254DE0227EF8331005BAC56 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5254DE0027EF8331005BAC56 /* Constants.swift */; };
16 | 5254DE0427EF837A005BAC56 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = 5254DE0327EF837A005BAC56 /* SFSafeSymbols */; };
17 | 525D85E227E760A60066B147 /* FragmentApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525D85B727E760A40066B147 /* FragmentApp.swift */; };
18 | 525D85E327E760A60066B147 /* FragmentApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525D85B727E760A40066B147 /* FragmentApp.swift */; };
19 | 525D860327E7621D0066B147 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525D860227E7621D0066B147 /* MainView.swift */; };
20 | 525D860527E762610066B147 /* ContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525D860427E762610066B147 /* ContainerView.swift */; };
21 | 525D860827E762B40066B147 /* OctoKit in Frameworks */ = {isa = PBXBuildFile; productRef = 525D860727E762B40066B147 /* OctoKit */; };
22 | 525D860927E763B50066B147 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525D860227E7621D0066B147 /* MainView.swift */; };
23 | 525D860A27E763B80066B147 /* ContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525D860427E762610066B147 /* ContainerView.swift */; };
24 | 526D97C327F0C25B00BD46CE /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 526D97C227F0C25B00BD46CE /* KeychainAccess */; };
25 | 526D97C527F0C26600BD46CE /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 526D97C427F0C26600BD46CE /* KeychainAccess */; };
26 | 526D97C827F0C5BE00BD46CE /* Highlightr in Frameworks */ = {isa = PBXBuildFile; productRef = 526D97C727F0C5BE00BD46CE /* Highlightr */; };
27 | 526D97CA27F0C5C500BD46CE /* Highlightr in Frameworks */ = {isa = PBXBuildFile; productRef = 526D97C927F0C5C500BD46CE /* Highlightr */; };
28 | 526D97D427F0C82D00BD46CE /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526D97D227F0C82D00BD46CE /* Language.swift */; };
29 | 526D97DE27F0CA8600BD46CE /* Gist+text.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526D97DD27F0CA8600BD46CE /* Gist+text.swift */; };
30 | 526D97DF27F0CA8600BD46CE /* Gist+text.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526D97DD27F0CA8600BD46CE /* Gist+text.swift */; };
31 | 526D97E127F0CBCC00BD46CE /* CodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526D97E027F0CBCC00BD46CE /* CodeView.swift */; };
32 | 526D97E227F0CBCC00BD46CE /* CodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526D97E027F0CBCC00BD46CE /* CodeView.swift */; };
33 | 52769DEE27F0CD4C00F5D4E0 /* Gist+language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52769DED27F0CD4C00F5D4E0 /* Gist+language.swift */; };
34 | 52769DEF27F0CD4C00F5D4E0 /* Gist+language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52769DED27F0CD4C00F5D4E0 /* Gist+language.swift */; };
35 | 52769DF027F0D0CB00F5D4E0 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526D97D227F0C82D00BD46CE /* Language.swift */; };
36 | 52769DFC27F0E83300F5D4E0 /* Collections+ifExistsAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52769DFB27F0E83300F5D4E0 /* Collections+ifExistsAt.swift */; };
37 | 52769DFD27F0E83300F5D4E0 /* Collections+ifExistsAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52769DFB27F0E83300F5D4E0 /* Collections+ifExistsAt.swift */; };
38 | 52769E0027F0EEE400F5D4E0 /* Visibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52769DFF27F0EEE400F5D4E0 /* Visibility.swift */; };
39 | 52769E0127F0EEE400F5D4E0 /* Visibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52769DFF27F0EEE400F5D4E0 /* Visibility.swift */; };
40 | 52769E0327F0F06900F5D4E0 /* AddGistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52769E0227F0F06900F5D4E0 /* AddGistView.swift */; };
41 | 52769E0427F0F06900F5D4E0 /* AddGistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52769E0227F0F06900F5D4E0 /* AddGistView.swift */; };
42 | 52769E0627F0FE6A00F5D4E0 /* WebLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52769E0527F0FE6A00F5D4E0 /* WebLauncher.swift */; };
43 | 52769E0727F0FE6A00F5D4E0 /* WebLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52769E0527F0FE6A00F5D4E0 /* WebLauncher.swift */; };
44 | 5283B2E327EC0C240018F19B /* AuthenticationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5283B2E227EC0C240018F19B /* AuthenticationView.swift */; };
45 | 5283B2E427EC0C240018F19B /* AuthenticationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5283B2E227EC0C240018F19B /* AuthenticationView.swift */; };
46 | 52BABB2927E7DCF300999F01 /* Fragment_iOS_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BABB2827E7DCF300999F01 /* Fragment_iOS_Tests.swift */; };
47 | 52BABB3627E7DD5500999F01 /* Fragment_macOS_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BABB3527E7DD5500999F01 /* Fragment_macOS_Tests.swift */; };
48 | 52BABB3E27E7DDB200999F01 /* TestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BABB3D27E7DDB200999F01 /* TestApp.swift */; };
49 | 52BABB3F27E7DDB200999F01 /* TestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BABB3D27E7DDB200999F01 /* TestApp.swift */; };
50 | 52C9ECDA27EC1DC900021911 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 525D85BA27E760A60066B147 /* Assets.xcassets */; };
51 | 52E3297027FBBEAD0059F82B /* SupportThisAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52E3296F27FBBEAD0059F82B /* SupportThisAppView.swift */; };
52 | 52E3297127FBBEAD0059F82B /* SupportThisAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52E3296F27FBBEAD0059F82B /* SupportThisAppView.swift */; };
53 | 5A1C6CFB27FB7E4A00EB8D91 /* SessionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A1C6CFA27FB7E4A00EB8D91 /* SessionHandler.swift */; };
54 | 5A1C6CFC27FB7E4A00EB8D91 /* SessionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A1C6CFA27FB7E4A00EB8D91 /* SessionHandler.swift */; };
55 | 5A2387FF27FB3B1900480C20 /* FragmentError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2387FE27FB3B1900480C20 /* FragmentError.swift */; };
56 | 5A23880027FB3B1900480C20 /* FragmentError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2387FE27FB3B1900480C20 /* FragmentError.swift */; };
57 | 5A23880727FB47E300480C20 /* SwifterSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 5A23880627FB47E300480C20 /* SwifterSwift */; };
58 | 5A23880927FB47F200480C20 /* SwifterSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 5A23880827FB47F200480C20 /* SwifterSwift */; };
59 | 5A23880F27FB4C4E00480C20 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A23880E27FB4C4E00480C20 /* SettingsView.swift */; };
60 | 5A23881027FB4C4E00480C20 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A23880E27FB4C4E00480C20 /* SettingsView.swift */; };
61 | 5A23881627FB674C00480C20 /* ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A23881527FB674C00480C20 /* ListView.swift */; };
62 | 5A23881727FB674C00480C20 /* ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A23881527FB674C00480C20 /* ListView.swift */; };
63 | 5A4C7E5627F3B600006B3A4A /* CodeEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 5A4C7E5527F3B600006B3A4A /* CodeEditor */; };
64 | 5A4C7E5827F3BE78006B3A4A /* CodeEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 5A4C7E5727F3BE78006B3A4A /* CodeEditor */; };
65 | 5AD4DFF327F3AC7A003C2C61 /* DHCacheKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5AD4DFF227F3AC7A003C2C61 /* DHCacheKit */; };
66 | 5AD4DFF527F3AC87003C2C61 /* DHCacheKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5AD4DFF427F3AC87003C2C61 /* DHCacheKit */; };
67 | 5ADC94A627F8E00200EFE347 /* Gist+Custom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ADC94A527F8E00200EFE347 /* Gist+Custom.swift */; };
68 | 5ADC94A727F8E00200EFE347 /* Gist+Custom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ADC94A527F8E00200EFE347 /* Gist+Custom.swift */; };
69 | 5ADC94A927F8EB0600EFE347 /* ClipboardHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ADC94A827F8EB0600EFE347 /* ClipboardHelper.swift */; };
70 | 5ADC94AA27F8EB0600EFE347 /* ClipboardHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ADC94A827F8EB0600EFE347 /* ClipboardHelper.swift */; };
71 | 5AFEF54527F80C91003FA4EE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 525D85BA27E760A60066B147 /* Assets.xcassets */; };
72 | /* End PBXBuildFile section */
73 |
74 | /* Begin PBXContainerItemProxy section */
75 | 52BABB2A27E7DCF300999F01 /* PBXContainerItemProxy */ = {
76 | isa = PBXContainerItemProxy;
77 | containerPortal = 525D85B027E760A40066B147 /* Project object */;
78 | proxyType = 1;
79 | remoteGlobalIDString = 525D85BE27E760A60066B147;
80 | remoteInfo = "Fragment (iOS)";
81 | };
82 | 52BABB3727E7DD5500999F01 /* PBXContainerItemProxy */ = {
83 | isa = PBXContainerItemProxy;
84 | containerPortal = 525D85B027E760A40066B147 /* Project object */;
85 | proxyType = 1;
86 | remoteGlobalIDString = 525D85C427E760A60066B147;
87 | remoteInfo = "Fragment (macOS)";
88 | };
89 | /* End PBXContainerItemProxy section */
90 |
91 | /* Begin PBXFileReference section */
92 | 522915B127E778300087530C /* GistRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GistRow.swift; sourceTree = ""; };
93 | 5254DE0027EF8331005BAC56 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; };
94 | 525D85B727E760A40066B147 /* FragmentApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FragmentApp.swift; sourceTree = ""; };
95 | 525D85BA27E760A60066B147 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
96 | 525D85BF27E760A60066B147 /* Fragment.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Fragment.app; sourceTree = BUILT_PRODUCTS_DIR; };
97 | 525D85C527E760A60066B147 /* Fragment.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Fragment.app; sourceTree = BUILT_PRODUCTS_DIR; };
98 | 525D85C727E760A60066B147 /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = ""; };
99 | 525D860227E7621D0066B147 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; };
100 | 525D860427E762610066B147 /* ContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerView.swift; sourceTree = ""; };
101 | 526D97D227F0C82D00BD46CE /* Language.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Language.swift; sourceTree = ""; };
102 | 526D97DD27F0CA8600BD46CE /* Gist+text.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Gist+text.swift"; sourceTree = ""; };
103 | 526D97E027F0CBCC00BD46CE /* CodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeView.swift; sourceTree = ""; };
104 | 52769DED27F0CD4C00F5D4E0 /* Gist+language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Gist+language.swift"; sourceTree = ""; };
105 | 52769DFB27F0E83300F5D4E0 /* Collections+ifExistsAt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collections+ifExistsAt.swift"; sourceTree = ""; };
106 | 52769DFF27F0EEE400F5D4E0 /* Visibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Visibility.swift; sourceTree = ""; };
107 | 52769E0227F0F06900F5D4E0 /* AddGistView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGistView.swift; sourceTree = ""; };
108 | 52769E0527F0FE6A00F5D4E0 /* WebLauncher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebLauncher.swift; sourceTree = ""; };
109 | 5283B2E227EC0C240018F19B /* AuthenticationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationView.swift; sourceTree = ""; };
110 | 52BABB1C27E7D79E00999F01 /* .swiftformat.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftformat.yml; sourceTree = ""; };
111 | 52BABB1D27E7D79E00999F01 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; };
112 | 52BABB2627E7DCF300999F01 /* Fragment-iOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Fragment-iOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
113 | 52BABB2827E7DCF300999F01 /* Fragment_iOS_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fragment_iOS_Tests.swift; sourceTree = ""; };
114 | 52BABB3327E7DD5500999F01 /* Fragment-macOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Fragment-macOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
115 | 52BABB3527E7DD5500999F01 /* Fragment_macOS_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fragment_macOS_Tests.swift; sourceTree = ""; };
116 | 52BABB3D27E7DDB200999F01 /* TestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestApp.swift; sourceTree = ""; };
117 | 52C9ECD727EC1D5100021911 /* Fragment--iOS--Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Fragment--iOS--Info.plist"; sourceTree = ""; };
118 | 52C9ECD827EC1D5B00021911 /* Fragment--macOS--Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Fragment--macOS--Info.plist"; sourceTree = ""; };
119 | 52E3296F27FBBEAD0059F82B /* SupportThisAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportThisAppView.swift; sourceTree = ""; };
120 | 5A1C6CFA27FB7E4A00EB8D91 /* SessionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionHandler.swift; sourceTree = ""; };
121 | 5A2387FE27FB3B1900480C20 /* FragmentError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FragmentError.swift; sourceTree = ""; };
122 | 5A23880E27FB4C4E00480C20 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; };
123 | 5A23881527FB674C00480C20 /* ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListView.swift; sourceTree = ""; };
124 | 5ADC94A527F8E00200EFE347 /* Gist+Custom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Gist+Custom.swift"; sourceTree = ""; };
125 | 5ADC94A827F8EB0600EFE347 /* ClipboardHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardHelper.swift; sourceTree = ""; };
126 | /* End PBXFileReference section */
127 |
128 | /* Begin PBXFrameworksBuildPhase section */
129 | 525D85BC27E760A60066B147 /* Frameworks */ = {
130 | isa = PBXFrameworksBuildPhase;
131 | buildActionMask = 2147483647;
132 | files = (
133 | 5A23880727FB47E300480C20 /* SwifterSwift in Frameworks */,
134 | 526D97C827F0C5BE00BD46CE /* Highlightr in Frameworks */,
135 | 525D860827E762B40066B147 /* OctoKit in Frameworks */,
136 | 5254DDFF27EF8224005BAC56 /* SFSafeSymbols in Frameworks */,
137 | 526D97C327F0C25B00BD46CE /* KeychainAccess in Frameworks */,
138 | 5A4C7E5827F3BE78006B3A4A /* CodeEditor in Frameworks */,
139 | 5AD4DFF327F3AC7A003C2C61 /* DHCacheKit in Frameworks */,
140 | );
141 | runOnlyForDeploymentPostprocessing = 0;
142 | };
143 | 525D85C227E760A60066B147 /* Frameworks */ = {
144 | isa = PBXFrameworksBuildPhase;
145 | buildActionMask = 2147483647;
146 | files = (
147 | 5A23880927FB47F200480C20 /* SwifterSwift in Frameworks */,
148 | 526D97CA27F0C5C500BD46CE /* Highlightr in Frameworks */,
149 | 522915C027E789E20087530C /* OctoKit in Frameworks */,
150 | 5254DE0427EF837A005BAC56 /* SFSafeSymbols in Frameworks */,
151 | 526D97C527F0C26600BD46CE /* KeychainAccess in Frameworks */,
152 | 5A4C7E5627F3B600006B3A4A /* CodeEditor in Frameworks */,
153 | 5AD4DFF527F3AC87003C2C61 /* DHCacheKit in Frameworks */,
154 | );
155 | runOnlyForDeploymentPostprocessing = 0;
156 | };
157 | 52BABB2327E7DCF300999F01 /* Frameworks */ = {
158 | isa = PBXFrameworksBuildPhase;
159 | buildActionMask = 2147483647;
160 | files = (
161 | );
162 | runOnlyForDeploymentPostprocessing = 0;
163 | };
164 | 52BABB3027E7DD5500999F01 /* Frameworks */ = {
165 | isa = PBXFrameworksBuildPhase;
166 | buildActionMask = 2147483647;
167 | files = (
168 | );
169 | runOnlyForDeploymentPostprocessing = 0;
170 | };
171 | /* End PBXFrameworksBuildPhase section */
172 |
173 | /* Begin PBXGroup section */
174 | 522915B027E7781E0087530C /* GistViews */ = {
175 | isa = PBXGroup;
176 | children = (
177 | 522915B127E778300087530C /* GistRow.swift */,
178 | );
179 | path = GistViews;
180 | sourceTree = "";
181 | };
182 | 522915B427E77D290087530C /* Extensions */ = {
183 | isa = PBXGroup;
184 | children = (
185 | 526D97DB27F0CA6300BD46CE /* OctoKitExtensions */,
186 | 52769DFB27F0E83300F5D4E0 /* Collections+ifExistsAt.swift */,
187 | );
188 | path = Extensions;
189 | sourceTree = "";
190 | };
191 | 522915B827E7855E0087530C /* CodeViews */ = {
192 | isa = PBXGroup;
193 | children = (
194 | 526D97E027F0CBCC00BD46CE /* CodeView.swift */,
195 | );
196 | path = CodeViews;
197 | sourceTree = "";
198 | };
199 | 525D85AF27E760A40066B147 = {
200 | isa = PBXGroup;
201 | children = (
202 | 52C9ECD827EC1D5B00021911 /* Fragment--macOS--Info.plist */,
203 | 52C9ECD727EC1D5100021911 /* Fragment--iOS--Info.plist */,
204 | 52BABB1C27E7D79E00999F01 /* .swiftformat.yml */,
205 | 52BABB1D27E7D79E00999F01 /* .swiftlint.yml */,
206 | 525D85B427E760A40066B147 /* Shared */,
207 | 525D85C627E760A60066B147 /* macOS */,
208 | 52BABB3C27E7DD8B00999F01 /* FragmentSharedTests */,
209 | 52BABB2727E7DCF300999F01 /* Fragment-iOS-Tests */,
210 | 52BABB3427E7DD5500999F01 /* Fragment-macOS-Tests */,
211 | 525D85C027E760A60066B147 /* Products */,
212 | 525D85FE27E761CB0066B147 /* Frameworks */,
213 | );
214 | sourceTree = "";
215 | };
216 | 525D85B427E760A40066B147 /* Shared */ = {
217 | isa = PBXGroup;
218 | children = (
219 | 5A23880127FB3ED000480C20 /* Handler */,
220 | 525D85BA27E760A60066B147 /* Assets.xcassets */,
221 | 525D85B727E760A40066B147 /* FragmentApp.swift */,
222 | 5283B2DE27EC0A1B0018F19B /* Models */,
223 | 522915B427E77D290087530C /* Extensions */,
224 | 525D860127E762140066B147 /* Views */,
225 | 5254DE0027EF8331005BAC56 /* Constants.swift */,
226 | 52769E0527F0FE6A00F5D4E0 /* WebLauncher.swift */,
227 | 5ADC94A827F8EB0600EFE347 /* ClipboardHelper.swift */,
228 | );
229 | path = Shared;
230 | sourceTree = "";
231 | };
232 | 525D85C027E760A60066B147 /* Products */ = {
233 | isa = PBXGroup;
234 | children = (
235 | 525D85BF27E760A60066B147 /* Fragment.app */,
236 | 525D85C527E760A60066B147 /* Fragment.app */,
237 | 52BABB2627E7DCF300999F01 /* Fragment-iOS-Tests.xctest */,
238 | 52BABB3327E7DD5500999F01 /* Fragment-macOS-Tests.xctest */,
239 | );
240 | name = Products;
241 | sourceTree = "";
242 | };
243 | 525D85C627E760A60066B147 /* macOS */ = {
244 | isa = PBXGroup;
245 | children = (
246 | 525D85C727E760A60066B147 /* macOS.entitlements */,
247 | );
248 | path = macOS;
249 | sourceTree = "";
250 | };
251 | 525D85FE27E761CB0066B147 /* Frameworks */ = {
252 | isa = PBXGroup;
253 | children = (
254 | );
255 | name = Frameworks;
256 | sourceTree = "";
257 | };
258 | 525D860127E762140066B147 /* Views */ = {
259 | isa = PBXGroup;
260 | children = (
261 | 52E3296E27FBBE980059F82B /* Support */,
262 | 5A23881427FB673600480C20 /* Container */,
263 | 52769DFE27F0EED100F5D4E0 /* Add */,
264 | 525D860227E7621D0066B147 /* MainView.swift */,
265 | 5283B2E227EC0C240018F19B /* AuthenticationView.swift */,
266 | 522915B827E7855E0087530C /* CodeViews */,
267 | 522915B027E7781E0087530C /* GistViews */,
268 | 5A23880E27FB4C4E00480C20 /* SettingsView.swift */,
269 | );
270 | path = Views;
271 | sourceTree = "";
272 | };
273 | 526D97DB27F0CA6300BD46CE /* OctoKitExtensions */ = {
274 | isa = PBXGroup;
275 | children = (
276 | 526D97DC27F0CA6F00BD46CE /* GistExtensions */,
277 | );
278 | path = OctoKitExtensions;
279 | sourceTree = "";
280 | };
281 | 526D97DC27F0CA6F00BD46CE /* GistExtensions */ = {
282 | isa = PBXGroup;
283 | children = (
284 | 526D97DD27F0CA8600BD46CE /* Gist+text.swift */,
285 | 52769DED27F0CD4C00F5D4E0 /* Gist+language.swift */,
286 | 5ADC94A527F8E00200EFE347 /* Gist+Custom.swift */,
287 | );
288 | path = GistExtensions;
289 | sourceTree = "";
290 | };
291 | 52769DFE27F0EED100F5D4E0 /* Add */ = {
292 | isa = PBXGroup;
293 | children = (
294 | 52769E0227F0F06900F5D4E0 /* AddGistView.swift */,
295 | );
296 | path = Add;
297 | sourceTree = "";
298 | };
299 | 5283B2DE27EC0A1B0018F19B /* Models */ = {
300 | isa = PBXGroup;
301 | children = (
302 | 5A2387FD27FB3B0200480C20 /* Errors */,
303 | 526D97D227F0C82D00BD46CE /* Language.swift */,
304 | 52769DFF27F0EEE400F5D4E0 /* Visibility.swift */,
305 | );
306 | path = Models;
307 | sourceTree = "";
308 | };
309 | 52BABB2727E7DCF300999F01 /* Fragment-iOS-Tests */ = {
310 | isa = PBXGroup;
311 | children = (
312 | 52BABB2827E7DCF300999F01 /* Fragment_iOS_Tests.swift */,
313 | );
314 | path = "Fragment-iOS-Tests";
315 | sourceTree = "";
316 | };
317 | 52BABB3427E7DD5500999F01 /* Fragment-macOS-Tests */ = {
318 | isa = PBXGroup;
319 | children = (
320 | 52BABB3527E7DD5500999F01 /* Fragment_macOS_Tests.swift */,
321 | );
322 | path = "Fragment-macOS-Tests";
323 | sourceTree = "";
324 | };
325 | 52BABB3C27E7DD8B00999F01 /* FragmentSharedTests */ = {
326 | isa = PBXGroup;
327 | children = (
328 | 52BABB3D27E7DDB200999F01 /* TestApp.swift */,
329 | );
330 | path = FragmentSharedTests;
331 | sourceTree = "";
332 | };
333 | 52E3296E27FBBE980059F82B /* Support */ = {
334 | isa = PBXGroup;
335 | children = (
336 | 52E3296F27FBBEAD0059F82B /* SupportThisAppView.swift */,
337 | );
338 | path = Support;
339 | sourceTree = "";
340 | };
341 | 5A2387FD27FB3B0200480C20 /* Errors */ = {
342 | isa = PBXGroup;
343 | children = (
344 | 5A2387FE27FB3B1900480C20 /* FragmentError.swift */,
345 | );
346 | path = Errors;
347 | sourceTree = "";
348 | };
349 | 5A23880127FB3ED000480C20 /* Handler */ = {
350 | isa = PBXGroup;
351 | children = (
352 | 5A1C6CFA27FB7E4A00EB8D91 /* SessionHandler.swift */,
353 | );
354 | path = Handler;
355 | sourceTree = "";
356 | };
357 | 5A23881427FB673600480C20 /* Container */ = {
358 | isa = PBXGroup;
359 | children = (
360 | 525D860427E762610066B147 /* ContainerView.swift */,
361 | 5A23881527FB674C00480C20 /* ListView.swift */,
362 | );
363 | path = Container;
364 | sourceTree = "";
365 | };
366 | /* End PBXGroup section */
367 |
368 | /* Begin PBXNativeTarget section */
369 | 525D85BE27E760A60066B147 /* Fragment (iOS) */ = {
370 | isa = PBXNativeTarget;
371 | buildConfigurationList = 525D85EC27E760A60066B147 /* Build configuration list for PBXNativeTarget "Fragment (iOS)" */;
372 | buildPhases = (
373 | 525D85BB27E760A60066B147 /* Sources */,
374 | 525D85BC27E760A60066B147 /* Frameworks */,
375 | 525D85BD27E760A60066B147 /* Resources */,
376 | 52BABB1E27E7D83000999F01 /* SwiftFormat */,
377 | 52BABB2027E7D84F00999F01 /* SwiftLint */,
378 | );
379 | buildRules = (
380 | );
381 | dependencies = (
382 | );
383 | name = "Fragment (iOS)";
384 | packageProductDependencies = (
385 | 525D860727E762B40066B147 /* OctoKit */,
386 | 5254DDFE27EF8224005BAC56 /* SFSafeSymbols */,
387 | 526D97C227F0C25B00BD46CE /* KeychainAccess */,
388 | 526D97C727F0C5BE00BD46CE /* Highlightr */,
389 | 5AD4DFF227F3AC7A003C2C61 /* DHCacheKit */,
390 | 5A4C7E5727F3BE78006B3A4A /* CodeEditor */,
391 | 5A23880627FB47E300480C20 /* SwifterSwift */,
392 | );
393 | productName = "Fragment (iOS)";
394 | productReference = 525D85BF27E760A60066B147 /* Fragment.app */;
395 | productType = "com.apple.product-type.application";
396 | };
397 | 525D85C427E760A60066B147 /* Fragment (macOS) */ = {
398 | isa = PBXNativeTarget;
399 | buildConfigurationList = 525D85EF27E760A60066B147 /* Build configuration list for PBXNativeTarget "Fragment (macOS)" */;
400 | buildPhases = (
401 | 525D85C127E760A60066B147 /* Sources */,
402 | 525D85C227E760A60066B147 /* Frameworks */,
403 | 525D85C327E760A60066B147 /* Resources */,
404 | 52BABB1F27E7D84000999F01 /* SwiftFormat */,
405 | 52BABB2127E7D87A00999F01 /* SwiftLint */,
406 | );
407 | buildRules = (
408 | );
409 | dependencies = (
410 | );
411 | name = "Fragment (macOS)";
412 | packageProductDependencies = (
413 | 522915BF27E789E20087530C /* OctoKit */,
414 | 5254DE0327EF837A005BAC56 /* SFSafeSymbols */,
415 | 526D97C427F0C26600BD46CE /* KeychainAccess */,
416 | 526D97C927F0C5C500BD46CE /* Highlightr */,
417 | 5AD4DFF427F3AC87003C2C61 /* DHCacheKit */,
418 | 5A4C7E5527F3B600006B3A4A /* CodeEditor */,
419 | 5A23880827FB47F200480C20 /* SwifterSwift */,
420 | );
421 | productName = "Fragment (macOS)";
422 | productReference = 525D85C527E760A60066B147 /* Fragment.app */;
423 | productType = "com.apple.product-type.application";
424 | };
425 | 52BABB2527E7DCF300999F01 /* Fragment-iOS-Tests */ = {
426 | isa = PBXNativeTarget;
427 | buildConfigurationList = 52BABB2C27E7DCF300999F01 /* Build configuration list for PBXNativeTarget "Fragment-iOS-Tests" */;
428 | buildPhases = (
429 | 52BABB2227E7DCF300999F01 /* Sources */,
430 | 52BABB2327E7DCF300999F01 /* Frameworks */,
431 | 52BABB2427E7DCF300999F01 /* Resources */,
432 | );
433 | buildRules = (
434 | );
435 | dependencies = (
436 | 52BABB2B27E7DCF300999F01 /* PBXTargetDependency */,
437 | );
438 | name = "Fragment-iOS-Tests";
439 | productName = "Fragment-iOS-Tests";
440 | productReference = 52BABB2627E7DCF300999F01 /* Fragment-iOS-Tests.xctest */;
441 | productType = "com.apple.product-type.bundle.unit-test";
442 | };
443 | 52BABB3227E7DD5500999F01 /* Fragment-macOS-Tests */ = {
444 | isa = PBXNativeTarget;
445 | buildConfigurationList = 52BABB3927E7DD5500999F01 /* Build configuration list for PBXNativeTarget "Fragment-macOS-Tests" */;
446 | buildPhases = (
447 | 52BABB2F27E7DD5500999F01 /* Sources */,
448 | 52BABB3027E7DD5500999F01 /* Frameworks */,
449 | 52BABB3127E7DD5500999F01 /* Resources */,
450 | );
451 | buildRules = (
452 | );
453 | dependencies = (
454 | 52BABB3827E7DD5500999F01 /* PBXTargetDependency */,
455 | );
456 | name = "Fragment-macOS-Tests";
457 | productName = "Fragment-macOS-Tests";
458 | productReference = 52BABB3327E7DD5500999F01 /* Fragment-macOS-Tests.xctest */;
459 | productType = "com.apple.product-type.bundle.unit-test";
460 | };
461 | /* End PBXNativeTarget section */
462 |
463 | /* Begin PBXProject section */
464 | 525D85B027E760A40066B147 /* Project object */ = {
465 | isa = PBXProject;
466 | attributes = {
467 | BuildIndependentTargetsInParallel = 1;
468 | LastSwiftUpdateCheck = 1330;
469 | LastUpgradeCheck = 1330;
470 | TargetAttributes = {
471 | 525D85BE27E760A60066B147 = {
472 | CreatedOnToolsVersion = 13.3;
473 | };
474 | 525D85C427E760A60066B147 = {
475 | CreatedOnToolsVersion = 13.3;
476 | };
477 | 52BABB2527E7DCF300999F01 = {
478 | CreatedOnToolsVersion = 13.3;
479 | TestTargetID = 525D85BE27E760A60066B147;
480 | };
481 | 52BABB3227E7DD5500999F01 = {
482 | CreatedOnToolsVersion = 13.3;
483 | TestTargetID = 525D85C427E760A60066B147;
484 | };
485 | };
486 | };
487 | buildConfigurationList = 525D85B327E760A40066B147 /* Build configuration list for PBXProject "Fragment" */;
488 | compatibilityVersion = "Xcode 13.0";
489 | developmentRegion = en;
490 | hasScannedForEncodings = 0;
491 | knownRegions = (
492 | en,
493 | Base,
494 | );
495 | mainGroup = 525D85AF27E760A40066B147;
496 | packageReferences = (
497 | 525D860627E762B40066B147 /* XCRemoteSwiftPackageReference "octokit" */,
498 | 5254DDFD27EF8224005BAC56 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */,
499 | 526D97C127F0C25B00BD46CE /* XCRemoteSwiftPackageReference "KeychainAccess" */,
500 | 526D97C627F0C5BE00BD46CE /* XCRemoteSwiftPackageReference "Highlightr" */,
501 | 5AD4DFF127F3AC7A003C2C61 /* XCRemoteSwiftPackageReference "DHCacheKit" */,
502 | 5A4C7E5427F3B600006B3A4A /* XCRemoteSwiftPackageReference "CodeEditor" */,
503 | 5A23880527FB47E300480C20 /* XCRemoteSwiftPackageReference "SwifterSwift" */,
504 | 52E3296927FBBE840059F82B /* XCRemoteSwiftPackageReference "buymeacoffee" */,
505 | );
506 | productRefGroup = 525D85C027E760A60066B147 /* Products */;
507 | projectDirPath = "";
508 | projectRoot = "";
509 | targets = (
510 | 525D85BE27E760A60066B147 /* Fragment (iOS) */,
511 | 525D85C427E760A60066B147 /* Fragment (macOS) */,
512 | 52BABB2527E7DCF300999F01 /* Fragment-iOS-Tests */,
513 | 52BABB3227E7DD5500999F01 /* Fragment-macOS-Tests */,
514 | );
515 | };
516 | /* End PBXProject section */
517 |
518 | /* Begin PBXResourcesBuildPhase section */
519 | 525D85BD27E760A60066B147 /* Resources */ = {
520 | isa = PBXResourcesBuildPhase;
521 | buildActionMask = 2147483647;
522 | files = (
523 | 5AFEF54527F80C91003FA4EE /* Assets.xcassets in Resources */,
524 | );
525 | runOnlyForDeploymentPostprocessing = 0;
526 | };
527 | 525D85C327E760A60066B147 /* Resources */ = {
528 | isa = PBXResourcesBuildPhase;
529 | buildActionMask = 2147483647;
530 | files = (
531 | 52C9ECDA27EC1DC900021911 /* Assets.xcassets in Resources */,
532 | );
533 | runOnlyForDeploymentPostprocessing = 0;
534 | };
535 | 52BABB2427E7DCF300999F01 /* Resources */ = {
536 | isa = PBXResourcesBuildPhase;
537 | buildActionMask = 2147483647;
538 | files = (
539 | );
540 | runOnlyForDeploymentPostprocessing = 0;
541 | };
542 | 52BABB3127E7DD5500999F01 /* Resources */ = {
543 | isa = PBXResourcesBuildPhase;
544 | buildActionMask = 2147483647;
545 | files = (
546 | );
547 | runOnlyForDeploymentPostprocessing = 0;
548 | };
549 | /* End PBXResourcesBuildPhase section */
550 |
551 | /* Begin PBXShellScriptBuildPhase section */
552 | 52BABB1E27E7D83000999F01 /* SwiftFormat */ = {
553 | isa = PBXShellScriptBuildPhase;
554 | buildActionMask = 2147483647;
555 | files = (
556 | );
557 | inputFileListPaths = (
558 | );
559 | inputPaths = (
560 | );
561 | name = SwiftFormat;
562 | outputFileListPaths = (
563 | );
564 | outputPaths = (
565 | );
566 | runOnlyForDeploymentPostprocessing = 0;
567 | shellPath = /bin/sh;
568 | shellScript = "# Adds support for Apple Silicon brew directory\nexport PATH=\"$PATH:/opt/homebrew/bin\"\n\nif which swiftformat >/dev/null; then\n swiftformat .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n";
569 | };
570 | 52BABB1F27E7D84000999F01 /* SwiftFormat */ = {
571 | isa = PBXShellScriptBuildPhase;
572 | buildActionMask = 2147483647;
573 | files = (
574 | );
575 | inputFileListPaths = (
576 | );
577 | inputPaths = (
578 | );
579 | name = SwiftFormat;
580 | outputFileListPaths = (
581 | );
582 | outputPaths = (
583 | );
584 | runOnlyForDeploymentPostprocessing = 0;
585 | shellPath = /bin/sh;
586 | shellScript = "# Adds support for Apple Silicon brew directory\nexport PATH=\"$PATH:/opt/homebrew/bin\"\n\nif which swiftformat >/dev/null; then\n swiftformat .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n";
587 | };
588 | 52BABB2027E7D84F00999F01 /* SwiftLint */ = {
589 | isa = PBXShellScriptBuildPhase;
590 | buildActionMask = 2147483647;
591 | files = (
592 | );
593 | inputFileListPaths = (
594 | );
595 | inputPaths = (
596 | );
597 | name = SwiftLint;
598 | outputFileListPaths = (
599 | );
600 | outputPaths = (
601 | );
602 | runOnlyForDeploymentPostprocessing = 0;
603 | shellPath = /bin/sh;
604 | shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint > /dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
605 | };
606 | 52BABB2127E7D87A00999F01 /* SwiftLint */ = {
607 | isa = PBXShellScriptBuildPhase;
608 | buildActionMask = 2147483647;
609 | files = (
610 | );
611 | inputFileListPaths = (
612 | );
613 | inputPaths = (
614 | );
615 | name = SwiftLint;
616 | outputFileListPaths = (
617 | );
618 | outputPaths = (
619 | );
620 | runOnlyForDeploymentPostprocessing = 0;
621 | shellPath = /bin/sh;
622 | shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint > /dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
623 | };
624 | /* End PBXShellScriptBuildPhase section */
625 |
626 | /* Begin PBXSourcesBuildPhase section */
627 | 525D85BB27E760A60066B147 /* Sources */ = {
628 | isa = PBXSourcesBuildPhase;
629 | buildActionMask = 2147483647;
630 | files = (
631 | 5A23880F27FB4C4E00480C20 /* SettingsView.swift in Sources */,
632 | 526D97E127F0CBCC00BD46CE /* CodeView.swift in Sources */,
633 | 525D860527E762610066B147 /* ContainerView.swift in Sources */,
634 | 526D97DE27F0CA8600BD46CE /* Gist+text.swift in Sources */,
635 | 525D85E227E760A60066B147 /* FragmentApp.swift in Sources */,
636 | 52769DFC27F0E83300F5D4E0 /* Collections+ifExistsAt.swift in Sources */,
637 | 5283B2E327EC0C240018F19B /* AuthenticationView.swift in Sources */,
638 | 5A1C6CFB27FB7E4A00EB8D91 /* SessionHandler.swift in Sources */,
639 | 525D860327E7621D0066B147 /* MainView.swift in Sources */,
640 | 5ADC94A627F8E00200EFE347 /* Gist+Custom.swift in Sources */,
641 | 52769DEE27F0CD4C00F5D4E0 /* Gist+language.swift in Sources */,
642 | 5A2387FF27FB3B1900480C20 /* FragmentError.swift in Sources */,
643 | 52E3297027FBBEAD0059F82B /* SupportThisAppView.swift in Sources */,
644 | 52769E0027F0EEE400F5D4E0 /* Visibility.swift in Sources */,
645 | 522915B227E778300087530C /* GistRow.swift in Sources */,
646 | 52769E0627F0FE6A00F5D4E0 /* WebLauncher.swift in Sources */,
647 | 5ADC94A927F8EB0600EFE347 /* ClipboardHelper.swift in Sources */,
648 | 5A23881627FB674C00480C20 /* ListView.swift in Sources */,
649 | 52769E0327F0F06900F5D4E0 /* AddGistView.swift in Sources */,
650 | 5254DE0127EF8331005BAC56 /* Constants.swift in Sources */,
651 | 52769DF027F0D0CB00F5D4E0 /* Language.swift in Sources */,
652 | );
653 | runOnlyForDeploymentPostprocessing = 0;
654 | };
655 | 525D85C127E760A60066B147 /* Sources */ = {
656 | isa = PBXSourcesBuildPhase;
657 | buildActionMask = 2147483647;
658 | files = (
659 | 5A23881027FB4C4E00480C20 /* SettingsView.swift in Sources */,
660 | 525D860A27E763B80066B147 /* ContainerView.swift in Sources */,
661 | 526D97DF27F0CA8600BD46CE /* Gist+text.swift in Sources */,
662 | 525D85E327E760A60066B147 /* FragmentApp.swift in Sources */,
663 | 5283B2E427EC0C240018F19B /* AuthenticationView.swift in Sources */,
664 | 52769DFD27F0E83300F5D4E0 /* Collections+ifExistsAt.swift in Sources */,
665 | 525D860927E763B50066B147 /* MainView.swift in Sources */,
666 | 5A1C6CFC27FB7E4A00EB8D91 /* SessionHandler.swift in Sources */,
667 | 5ADC94A727F8E00200EFE347 /* Gist+Custom.swift in Sources */,
668 | 522915B327E778300087530C /* GistRow.swift in Sources */,
669 | 5A23880027FB3B1900480C20 /* FragmentError.swift in Sources */,
670 | 52769E0127F0EEE400F5D4E0 /* Visibility.swift in Sources */,
671 | 52E3297127FBBEAD0059F82B /* SupportThisAppView.swift in Sources */,
672 | 5254DE0227EF8331005BAC56 /* Constants.swift in Sources */,
673 | 52769E0727F0FE6A00F5D4E0 /* WebLauncher.swift in Sources */,
674 | 5ADC94AA27F8EB0600EFE347 /* ClipboardHelper.swift in Sources */,
675 | 526D97D427F0C82D00BD46CE /* Language.swift in Sources */,
676 | 5A23881727FB674C00480C20 /* ListView.swift in Sources */,
677 | 52769E0427F0F06900F5D4E0 /* AddGistView.swift in Sources */,
678 | 52769DEF27F0CD4C00F5D4E0 /* Gist+language.swift in Sources */,
679 | 526D97E227F0CBCC00BD46CE /* CodeView.swift in Sources */,
680 | );
681 | runOnlyForDeploymentPostprocessing = 0;
682 | };
683 | 52BABB2227E7DCF300999F01 /* Sources */ = {
684 | isa = PBXSourcesBuildPhase;
685 | buildActionMask = 2147483647;
686 | files = (
687 | 52BABB2927E7DCF300999F01 /* Fragment_iOS_Tests.swift in Sources */,
688 | 52BABB3E27E7DDB200999F01 /* TestApp.swift in Sources */,
689 | );
690 | runOnlyForDeploymentPostprocessing = 0;
691 | };
692 | 52BABB2F27E7DD5500999F01 /* Sources */ = {
693 | isa = PBXSourcesBuildPhase;
694 | buildActionMask = 2147483647;
695 | files = (
696 | 52BABB3627E7DD5500999F01 /* Fragment_macOS_Tests.swift in Sources */,
697 | 52BABB3F27E7DDB200999F01 /* TestApp.swift in Sources */,
698 | );
699 | runOnlyForDeploymentPostprocessing = 0;
700 | };
701 | /* End PBXSourcesBuildPhase section */
702 |
703 | /* Begin PBXTargetDependency section */
704 | 52BABB2B27E7DCF300999F01 /* PBXTargetDependency */ = {
705 | isa = PBXTargetDependency;
706 | target = 525D85BE27E760A60066B147 /* Fragment (iOS) */;
707 | targetProxy = 52BABB2A27E7DCF300999F01 /* PBXContainerItemProxy */;
708 | };
709 | 52BABB3827E7DD5500999F01 /* PBXTargetDependency */ = {
710 | isa = PBXTargetDependency;
711 | target = 525D85C427E760A60066B147 /* Fragment (macOS) */;
712 | targetProxy = 52BABB3727E7DD5500999F01 /* PBXContainerItemProxy */;
713 | };
714 | /* End PBXTargetDependency section */
715 |
716 | /* Begin XCBuildConfiguration section */
717 | 525D85EA27E760A60066B147 /* Debug */ = {
718 | isa = XCBuildConfiguration;
719 | buildSettings = {
720 | ALWAYS_SEARCH_USER_PATHS = NO;
721 | CLANG_ANALYZER_NONNULL = YES;
722 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
723 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
724 | CLANG_ENABLE_MODULES = YES;
725 | CLANG_ENABLE_OBJC_ARC = YES;
726 | CLANG_ENABLE_OBJC_WEAK = YES;
727 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
728 | CLANG_WARN_BOOL_CONVERSION = YES;
729 | CLANG_WARN_COMMA = YES;
730 | CLANG_WARN_CONSTANT_CONVERSION = YES;
731 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
732 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
733 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
734 | CLANG_WARN_EMPTY_BODY = YES;
735 | CLANG_WARN_ENUM_CONVERSION = YES;
736 | CLANG_WARN_INFINITE_RECURSION = YES;
737 | CLANG_WARN_INT_CONVERSION = YES;
738 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
739 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
740 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
741 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
742 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
743 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
744 | CLANG_WARN_STRICT_PROTOTYPES = YES;
745 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
746 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
747 | CLANG_WARN_UNREACHABLE_CODE = YES;
748 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
749 | COPY_PHASE_STRIP = NO;
750 | DEBUG_INFORMATION_FORMAT = dwarf;
751 | ENABLE_STRICT_OBJC_MSGSEND = YES;
752 | ENABLE_TESTABILITY = YES;
753 | GCC_C_LANGUAGE_STANDARD = gnu11;
754 | GCC_DYNAMIC_NO_PIC = NO;
755 | GCC_NO_COMMON_BLOCKS = YES;
756 | GCC_OPTIMIZATION_LEVEL = 0;
757 | GCC_PREPROCESSOR_DEFINITIONS = (
758 | "DEBUG=1",
759 | "$(inherited)",
760 | );
761 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
762 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
763 | GCC_WARN_UNDECLARED_SELECTOR = YES;
764 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
765 | GCC_WARN_UNUSED_FUNCTION = YES;
766 | GCC_WARN_UNUSED_VARIABLE = YES;
767 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
768 | MTL_FAST_MATH = YES;
769 | ONLY_ACTIVE_ARCH = YES;
770 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
771 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
772 | };
773 | name = Debug;
774 | };
775 | 525D85EB27E760A60066B147 /* Release */ = {
776 | isa = XCBuildConfiguration;
777 | buildSettings = {
778 | ALWAYS_SEARCH_USER_PATHS = NO;
779 | CLANG_ANALYZER_NONNULL = YES;
780 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
781 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
782 | CLANG_ENABLE_MODULES = YES;
783 | CLANG_ENABLE_OBJC_ARC = YES;
784 | CLANG_ENABLE_OBJC_WEAK = YES;
785 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
786 | CLANG_WARN_BOOL_CONVERSION = YES;
787 | CLANG_WARN_COMMA = YES;
788 | CLANG_WARN_CONSTANT_CONVERSION = YES;
789 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
790 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
791 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
792 | CLANG_WARN_EMPTY_BODY = YES;
793 | CLANG_WARN_ENUM_CONVERSION = YES;
794 | CLANG_WARN_INFINITE_RECURSION = YES;
795 | CLANG_WARN_INT_CONVERSION = YES;
796 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
797 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
798 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
799 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
800 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
801 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
802 | CLANG_WARN_STRICT_PROTOTYPES = YES;
803 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
804 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
805 | CLANG_WARN_UNREACHABLE_CODE = YES;
806 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
807 | COPY_PHASE_STRIP = NO;
808 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
809 | ENABLE_NS_ASSERTIONS = NO;
810 | ENABLE_STRICT_OBJC_MSGSEND = YES;
811 | GCC_C_LANGUAGE_STANDARD = gnu11;
812 | GCC_NO_COMMON_BLOCKS = YES;
813 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
814 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
815 | GCC_WARN_UNDECLARED_SELECTOR = YES;
816 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
817 | GCC_WARN_UNUSED_FUNCTION = YES;
818 | GCC_WARN_UNUSED_VARIABLE = YES;
819 | MTL_ENABLE_DEBUG_INFO = NO;
820 | MTL_FAST_MATH = YES;
821 | SWIFT_COMPILATION_MODE = wholemodule;
822 | SWIFT_OPTIMIZATION_LEVEL = "-O";
823 | };
824 | name = Release;
825 | };
826 | 525D85ED27E760A60066B147 /* Debug */ = {
827 | isa = XCBuildConfiguration;
828 | buildSettings = {
829 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
830 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
831 | ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
832 | CODE_SIGN_STYLE = Automatic;
833 | CURRENT_PROJECT_VERSION = 1;
834 | DEVELOPMENT_TEAM = WRJ8J85K4Q;
835 | ENABLE_PREVIEWS = YES;
836 | GENERATE_INFOPLIST_FILE = YES;
837 | INFOPLIST_FILE = "Fragment--iOS--Info.plist";
838 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
839 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
840 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
841 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
842 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
843 | IPHONEOS_DEPLOYMENT_TARGET = 15.4;
844 | LD_RUNPATH_SEARCH_PATHS = (
845 | "$(inherited)",
846 | "@executable_path/Frameworks",
847 | );
848 | MARKETING_VERSION = 0.1.2;
849 | PRODUCT_BUNDLE_IDENTIFIER = com.codedbydan.FragmentSnippetManager;
850 | PRODUCT_NAME = Fragment;
851 | SDKROOT = iphoneos;
852 | SWIFT_EMIT_LOC_STRINGS = YES;
853 | SWIFT_VERSION = 5.0;
854 | TARGETED_DEVICE_FAMILY = "1,2";
855 | };
856 | name = Debug;
857 | };
858 | 525D85EE27E760A60066B147 /* Release */ = {
859 | isa = XCBuildConfiguration;
860 | buildSettings = {
861 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
862 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
863 | ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
864 | CODE_SIGN_STYLE = Automatic;
865 | CURRENT_PROJECT_VERSION = 1;
866 | DEVELOPMENT_TEAM = WRJ8J85K4Q;
867 | ENABLE_PREVIEWS = YES;
868 | GENERATE_INFOPLIST_FILE = YES;
869 | INFOPLIST_FILE = "Fragment--iOS--Info.plist";
870 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
871 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
872 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
873 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
874 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
875 | IPHONEOS_DEPLOYMENT_TARGET = 15.4;
876 | LD_RUNPATH_SEARCH_PATHS = (
877 | "$(inherited)",
878 | "@executable_path/Frameworks",
879 | );
880 | MARKETING_VERSION = 0.1.2;
881 | PRODUCT_BUNDLE_IDENTIFIER = com.codedbydan.FragmentSnippetManager;
882 | PRODUCT_NAME = Fragment;
883 | SDKROOT = iphoneos;
884 | SWIFT_EMIT_LOC_STRINGS = YES;
885 | SWIFT_VERSION = 5.0;
886 | TARGETED_DEVICE_FAMILY = "1,2";
887 | VALIDATE_PRODUCT = YES;
888 | };
889 | name = Release;
890 | };
891 | 525D85F027E760A60066B147 /* Debug */ = {
892 | isa = XCBuildConfiguration;
893 | buildSettings = {
894 | ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-macOS";
895 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
896 | ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
897 | CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
898 | CODE_SIGN_STYLE = Automatic;
899 | COMBINE_HIDPI_IMAGES = YES;
900 | CURRENT_PROJECT_VERSION = 1;
901 | DEVELOPMENT_TEAM = WRJ8J85K4Q;
902 | ENABLE_HARDENED_RUNTIME = YES;
903 | ENABLE_PREVIEWS = YES;
904 | GENERATE_INFOPLIST_FILE = YES;
905 | INFOPLIST_FILE = "Fragment--macOS--Info.plist";
906 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
907 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
908 | LD_RUNPATH_SEARCH_PATHS = (
909 | "$(inherited)",
910 | "@executable_path/../Frameworks",
911 | );
912 | MACOSX_DEPLOYMENT_TARGET = 12.3;
913 | MARKETING_VERSION = 0.1.2;
914 | PRODUCT_BUNDLE_IDENTIFIER = com.codedbydan.FragmentSnippetManager;
915 | PRODUCT_NAME = Fragment;
916 | SDKROOT = macosx;
917 | SWIFT_EMIT_LOC_STRINGS = YES;
918 | SWIFT_VERSION = 5.0;
919 | };
920 | name = Debug;
921 | };
922 | 525D85F127E760A60066B147 /* Release */ = {
923 | isa = XCBuildConfiguration;
924 | buildSettings = {
925 | ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-macOS";
926 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
927 | ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
928 | CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
929 | CODE_SIGN_STYLE = Automatic;
930 | COMBINE_HIDPI_IMAGES = YES;
931 | CURRENT_PROJECT_VERSION = 1;
932 | DEVELOPMENT_TEAM = WRJ8J85K4Q;
933 | ENABLE_HARDENED_RUNTIME = YES;
934 | ENABLE_PREVIEWS = YES;
935 | GENERATE_INFOPLIST_FILE = YES;
936 | INFOPLIST_FILE = "Fragment--macOS--Info.plist";
937 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
938 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
939 | LD_RUNPATH_SEARCH_PATHS = (
940 | "$(inherited)",
941 | "@executable_path/../Frameworks",
942 | );
943 | MACOSX_DEPLOYMENT_TARGET = 12.3;
944 | MARKETING_VERSION = 0.1.2;
945 | PRODUCT_BUNDLE_IDENTIFIER = com.codedbydan.FragmentSnippetManager;
946 | PRODUCT_NAME = Fragment;
947 | SDKROOT = macosx;
948 | SWIFT_EMIT_LOC_STRINGS = YES;
949 | SWIFT_VERSION = 5.0;
950 | };
951 | name = Release;
952 | };
953 | 52BABB2D27E7DCF300999F01 /* Debug */ = {
954 | isa = XCBuildConfiguration;
955 | buildSettings = {
956 | BUNDLE_LOADER = "$(TEST_HOST)";
957 | CODE_SIGN_STYLE = Automatic;
958 | CURRENT_PROJECT_VERSION = 1;
959 | DEVELOPMENT_TEAM = WRJ8J85K4Q;
960 | GENERATE_INFOPLIST_FILE = YES;
961 | IPHONEOS_DEPLOYMENT_TARGET = 15.4;
962 | MARKETING_VERSION = 1.0;
963 | PRODUCT_BUNDLE_IDENTIFIER = "com.codedbydan.Fragment-iOS-Tests";
964 | PRODUCT_NAME = "$(TARGET_NAME)";
965 | SDKROOT = iphoneos;
966 | SWIFT_EMIT_LOC_STRINGS = NO;
967 | SWIFT_VERSION = 5.0;
968 | TARGETED_DEVICE_FAMILY = "1,2";
969 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Fragment.app/Fragment";
970 | };
971 | name = Debug;
972 | };
973 | 52BABB2E27E7DCF300999F01 /* Release */ = {
974 | isa = XCBuildConfiguration;
975 | buildSettings = {
976 | BUNDLE_LOADER = "$(TEST_HOST)";
977 | CODE_SIGN_STYLE = Automatic;
978 | CURRENT_PROJECT_VERSION = 1;
979 | DEVELOPMENT_TEAM = WRJ8J85K4Q;
980 | GENERATE_INFOPLIST_FILE = YES;
981 | IPHONEOS_DEPLOYMENT_TARGET = 15.4;
982 | MARKETING_VERSION = 1.0;
983 | PRODUCT_BUNDLE_IDENTIFIER = "com.codedbydan.Fragment-iOS-Tests";
984 | PRODUCT_NAME = "$(TARGET_NAME)";
985 | SDKROOT = iphoneos;
986 | SWIFT_EMIT_LOC_STRINGS = NO;
987 | SWIFT_VERSION = 5.0;
988 | TARGETED_DEVICE_FAMILY = "1,2";
989 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Fragment.app/Fragment";
990 | VALIDATE_PRODUCT = YES;
991 | };
992 | name = Release;
993 | };
994 | 52BABB3A27E7DD5500999F01 /* Debug */ = {
995 | isa = XCBuildConfiguration;
996 | buildSettings = {
997 | BUNDLE_LOADER = "$(TEST_HOST)";
998 | CODE_SIGN_STYLE = Automatic;
999 | CURRENT_PROJECT_VERSION = 1;
1000 | DEVELOPMENT_TEAM = WRJ8J85K4Q;
1001 | GENERATE_INFOPLIST_FILE = YES;
1002 | MACOSX_DEPLOYMENT_TARGET = 12.3;
1003 | MARKETING_VERSION = 1.0;
1004 | PRODUCT_BUNDLE_IDENTIFIER = "com.codedbydan.Fragment-macOS-Tests";
1005 | PRODUCT_NAME = "$(TARGET_NAME)";
1006 | SDKROOT = macosx;
1007 | SWIFT_EMIT_LOC_STRINGS = NO;
1008 | SWIFT_VERSION = 5.0;
1009 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Fragment.app/Contents/MacOS/Fragment";
1010 | };
1011 | name = Debug;
1012 | };
1013 | 52BABB3B27E7DD5500999F01 /* Release */ = {
1014 | isa = XCBuildConfiguration;
1015 | buildSettings = {
1016 | BUNDLE_LOADER = "$(TEST_HOST)";
1017 | CODE_SIGN_STYLE = Automatic;
1018 | CURRENT_PROJECT_VERSION = 1;
1019 | DEVELOPMENT_TEAM = WRJ8J85K4Q;
1020 | GENERATE_INFOPLIST_FILE = YES;
1021 | MACOSX_DEPLOYMENT_TARGET = 12.3;
1022 | MARKETING_VERSION = 1.0;
1023 | PRODUCT_BUNDLE_IDENTIFIER = "com.codedbydan.Fragment-macOS-Tests";
1024 | PRODUCT_NAME = "$(TARGET_NAME)";
1025 | SDKROOT = macosx;
1026 | SWIFT_EMIT_LOC_STRINGS = NO;
1027 | SWIFT_VERSION = 5.0;
1028 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Fragment.app/Contents/MacOS/Fragment";
1029 | };
1030 | name = Release;
1031 | };
1032 | /* End XCBuildConfiguration section */
1033 |
1034 | /* Begin XCConfigurationList section */
1035 | 525D85B327E760A40066B147 /* Build configuration list for PBXProject "Fragment" */ = {
1036 | isa = XCConfigurationList;
1037 | buildConfigurations = (
1038 | 525D85EA27E760A60066B147 /* Debug */,
1039 | 525D85EB27E760A60066B147 /* Release */,
1040 | );
1041 | defaultConfigurationIsVisible = 0;
1042 | defaultConfigurationName = Release;
1043 | };
1044 | 525D85EC27E760A60066B147 /* Build configuration list for PBXNativeTarget "Fragment (iOS)" */ = {
1045 | isa = XCConfigurationList;
1046 | buildConfigurations = (
1047 | 525D85ED27E760A60066B147 /* Debug */,
1048 | 525D85EE27E760A60066B147 /* Release */,
1049 | );
1050 | defaultConfigurationIsVisible = 0;
1051 | defaultConfigurationName = Release;
1052 | };
1053 | 525D85EF27E760A60066B147 /* Build configuration list for PBXNativeTarget "Fragment (macOS)" */ = {
1054 | isa = XCConfigurationList;
1055 | buildConfigurations = (
1056 | 525D85F027E760A60066B147 /* Debug */,
1057 | 525D85F127E760A60066B147 /* Release */,
1058 | );
1059 | defaultConfigurationIsVisible = 0;
1060 | defaultConfigurationName = Release;
1061 | };
1062 | 52BABB2C27E7DCF300999F01 /* Build configuration list for PBXNativeTarget "Fragment-iOS-Tests" */ = {
1063 | isa = XCConfigurationList;
1064 | buildConfigurations = (
1065 | 52BABB2D27E7DCF300999F01 /* Debug */,
1066 | 52BABB2E27E7DCF300999F01 /* Release */,
1067 | );
1068 | defaultConfigurationIsVisible = 0;
1069 | defaultConfigurationName = Release;
1070 | };
1071 | 52BABB3927E7DD5500999F01 /* Build configuration list for PBXNativeTarget "Fragment-macOS-Tests" */ = {
1072 | isa = XCConfigurationList;
1073 | buildConfigurations = (
1074 | 52BABB3A27E7DD5500999F01 /* Debug */,
1075 | 52BABB3B27E7DD5500999F01 /* Release */,
1076 | );
1077 | defaultConfigurationIsVisible = 0;
1078 | defaultConfigurationName = Release;
1079 | };
1080 | /* End XCConfigurationList section */
1081 |
1082 | /* Begin XCRemoteSwiftPackageReference section */
1083 | 5254DDFD27EF8224005BAC56 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */ = {
1084 | isa = XCRemoteSwiftPackageReference;
1085 | repositoryURL = "https://github.com/SFSafeSymbols/SFSafeSymbols";
1086 | requirement = {
1087 | kind = upToNextMajorVersion;
1088 | minimumVersion = 2.0.0;
1089 | };
1090 | };
1091 | 525D860627E762B40066B147 /* XCRemoteSwiftPackageReference "octokit" */ = {
1092 | isa = XCRemoteSwiftPackageReference;
1093 | repositoryURL = "https://github.com/nerdishbynature/octokit.swift";
1094 | requirement = {
1095 | kind = upToNextMajorVersion;
1096 | minimumVersion = 0.9.0;
1097 | };
1098 | };
1099 | 526D97C127F0C25B00BD46CE /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
1100 | isa = XCRemoteSwiftPackageReference;
1101 | repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess";
1102 | requirement = {
1103 | kind = upToNextMajorVersion;
1104 | minimumVersion = 4.0.0;
1105 | };
1106 | };
1107 | 526D97C627F0C5BE00BD46CE /* XCRemoteSwiftPackageReference "Highlightr" */ = {
1108 | isa = XCRemoteSwiftPackageReference;
1109 | repositoryURL = "https://github.com/raspu/Highlightr";
1110 | requirement = {
1111 | kind = upToNextMajorVersion;
1112 | minimumVersion = 2.0.0;
1113 | };
1114 | };
1115 | 52E3296927FBBE840059F82B /* XCRemoteSwiftPackageReference "buymeacoffee" */ = {
1116 | isa = XCRemoteSwiftPackageReference;
1117 | repositoryURL = "https://github.com/dan-hart/buymeacoffee";
1118 | requirement = {
1119 | branch = master;
1120 | kind = branch;
1121 | };
1122 | };
1123 | 5A23880527FB47E300480C20 /* XCRemoteSwiftPackageReference "SwifterSwift" */ = {
1124 | isa = XCRemoteSwiftPackageReference;
1125 | repositoryURL = "https://github.com/SwifterSwift/SwifterSwift";
1126 | requirement = {
1127 | kind = upToNextMajorVersion;
1128 | minimumVersion = 5.0.0;
1129 | };
1130 | };
1131 | 5A4C7E5427F3B600006B3A4A /* XCRemoteSwiftPackageReference "CodeEditor" */ = {
1132 | isa = XCRemoteSwiftPackageReference;
1133 | repositoryURL = "https://github.com/ZeeZide/CodeEditor";
1134 | requirement = {
1135 | kind = upToNextMajorVersion;
1136 | minimumVersion = 1.0.0;
1137 | };
1138 | };
1139 | 5AD4DFF127F3AC7A003C2C61 /* XCRemoteSwiftPackageReference "DHCacheKit" */ = {
1140 | isa = XCRemoteSwiftPackageReference;
1141 | repositoryURL = "https://github.com/dan-hart/DHCacheKit";
1142 | requirement = {
1143 | kind = exactVersion;
1144 | version = 0.0.3;
1145 | };
1146 | };
1147 | /* End XCRemoteSwiftPackageReference section */
1148 |
1149 | /* Begin XCSwiftPackageProductDependency section */
1150 | 522915BF27E789E20087530C /* OctoKit */ = {
1151 | isa = XCSwiftPackageProductDependency;
1152 | package = 525D860627E762B40066B147 /* XCRemoteSwiftPackageReference "octokit" */;
1153 | productName = OctoKit;
1154 | };
1155 | 5254DDFE27EF8224005BAC56 /* SFSafeSymbols */ = {
1156 | isa = XCSwiftPackageProductDependency;
1157 | package = 5254DDFD27EF8224005BAC56 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */;
1158 | productName = SFSafeSymbols;
1159 | };
1160 | 5254DE0327EF837A005BAC56 /* SFSafeSymbols */ = {
1161 | isa = XCSwiftPackageProductDependency;
1162 | package = 5254DDFD27EF8224005BAC56 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */;
1163 | productName = SFSafeSymbols;
1164 | };
1165 | 525D860727E762B40066B147 /* OctoKit */ = {
1166 | isa = XCSwiftPackageProductDependency;
1167 | package = 525D860627E762B40066B147 /* XCRemoteSwiftPackageReference "octokit" */;
1168 | productName = OctoKit;
1169 | };
1170 | 526D97C227F0C25B00BD46CE /* KeychainAccess */ = {
1171 | isa = XCSwiftPackageProductDependency;
1172 | package = 526D97C127F0C25B00BD46CE /* XCRemoteSwiftPackageReference "KeychainAccess" */;
1173 | productName = KeychainAccess;
1174 | };
1175 | 526D97C427F0C26600BD46CE /* KeychainAccess */ = {
1176 | isa = XCSwiftPackageProductDependency;
1177 | package = 526D97C127F0C25B00BD46CE /* XCRemoteSwiftPackageReference "KeychainAccess" */;
1178 | productName = KeychainAccess;
1179 | };
1180 | 526D97C727F0C5BE00BD46CE /* Highlightr */ = {
1181 | isa = XCSwiftPackageProductDependency;
1182 | package = 526D97C627F0C5BE00BD46CE /* XCRemoteSwiftPackageReference "Highlightr" */;
1183 | productName = Highlightr;
1184 | };
1185 | 526D97C927F0C5C500BD46CE /* Highlightr */ = {
1186 | isa = XCSwiftPackageProductDependency;
1187 | package = 526D97C627F0C5BE00BD46CE /* XCRemoteSwiftPackageReference "Highlightr" */;
1188 | productName = Highlightr;
1189 | };
1190 | 5A23880627FB47E300480C20 /* SwifterSwift */ = {
1191 | isa = XCSwiftPackageProductDependency;
1192 | package = 5A23880527FB47E300480C20 /* XCRemoteSwiftPackageReference "SwifterSwift" */;
1193 | productName = SwifterSwift;
1194 | };
1195 | 5A23880827FB47F200480C20 /* SwifterSwift */ = {
1196 | isa = XCSwiftPackageProductDependency;
1197 | package = 5A23880527FB47E300480C20 /* XCRemoteSwiftPackageReference "SwifterSwift" */;
1198 | productName = SwifterSwift;
1199 | };
1200 | 5A4C7E5527F3B600006B3A4A /* CodeEditor */ = {
1201 | isa = XCSwiftPackageProductDependency;
1202 | package = 5A4C7E5427F3B600006B3A4A /* XCRemoteSwiftPackageReference "CodeEditor" */;
1203 | productName = CodeEditor;
1204 | };
1205 | 5A4C7E5727F3BE78006B3A4A /* CodeEditor */ = {
1206 | isa = XCSwiftPackageProductDependency;
1207 | package = 5A4C7E5427F3B600006B3A4A /* XCRemoteSwiftPackageReference "CodeEditor" */;
1208 | productName = CodeEditor;
1209 | };
1210 | 5AD4DFF227F3AC7A003C2C61 /* DHCacheKit */ = {
1211 | isa = XCSwiftPackageProductDependency;
1212 | package = 5AD4DFF127F3AC7A003C2C61 /* XCRemoteSwiftPackageReference "DHCacheKit" */;
1213 | productName = DHCacheKit;
1214 | };
1215 | 5AD4DFF427F3AC87003C2C61 /* DHCacheKit */ = {
1216 | isa = XCSwiftPackageProductDependency;
1217 | package = 5AD4DFF127F3AC7A003C2C61 /* XCRemoteSwiftPackageReference "DHCacheKit" */;
1218 | productName = DHCacheKit;
1219 | };
1220 | /* End XCSwiftPackageProductDependency section */
1221 | };
1222 | rootObject = 525D85B027E760A40066B147 /* Project object */;
1223 | }
1224 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "buymeacoffee",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/dan-hart/buymeacoffee",
7 | "state" : {
8 | "branch" : "master",
9 | "revision" : "850b6346dcdc96fe761a5bf953294d3885ffac7d"
10 | }
11 | },
12 | {
13 | "identity" : "codeeditor",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/ZeeZide/CodeEditor",
16 | "state" : {
17 | "revision" : "5856fac22b0a2174dbdea212784567c8c9cd1129",
18 | "version" : "1.2.0"
19 | }
20 | },
21 | {
22 | "identity" : "dhcachekit",
23 | "kind" : "remoteSourceControl",
24 | "location" : "https://github.com/dan-hart/DHCacheKit",
25 | "state" : {
26 | "revision" : "4317aa6d9ae4db33325b51aa2ea2e6f6d09b3b40",
27 | "version" : "0.0.3"
28 | }
29 | },
30 | {
31 | "identity" : "filekit",
32 | "kind" : "remoteSourceControl",
33 | "location" : "https://github.com/nvzqz/FileKit",
34 | "state" : {
35 | "revision" : "826d9161b184509f80d85c28cd612d630646de98",
36 | "version" : "6.0.0"
37 | }
38 | },
39 | {
40 | "identity" : "highlightr",
41 | "kind" : "remoteSourceControl",
42 | "location" : "https://github.com/raspu/Highlightr",
43 | "state" : {
44 | "revision" : "93199b9e434f04bda956a613af8f571933f9f037",
45 | "version" : "2.1.2"
46 | }
47 | },
48 | {
49 | "identity" : "keychainaccess",
50 | "kind" : "remoteSourceControl",
51 | "location" : "https://github.com/kishikawakatsumi/KeychainAccess",
52 | "state" : {
53 | "revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
54 | "version" : "4.2.2"
55 | }
56 | },
57 | {
58 | "identity" : "octokit.swift",
59 | "kind" : "remoteSourceControl",
60 | "location" : "https://github.com/nerdishbynature/octokit.swift",
61 | "state" : {
62 | "revision" : "9521cdff919053868ab13cd08a228f7bc1bde2a9",
63 | "version" : "0.11.0"
64 | }
65 | },
66 | {
67 | "identity" : "requestkit",
68 | "kind" : "remoteSourceControl",
69 | "location" : "https://github.com/nerdishbynature/RequestKit.git",
70 | "state" : {
71 | "revision" : "fd5e9e99aada7432170366c9e95967011ce13bad",
72 | "version" : "2.4.0"
73 | }
74 | },
75 | {
76 | "identity" : "sfsafesymbols",
77 | "kind" : "remoteSourceControl",
78 | "location" : "https://github.com/SFSafeSymbols/SFSafeSymbols",
79 | "state" : {
80 | "revision" : "c61de7a6e9eb1520d32316b06d5932accd974e70",
81 | "version" : "2.1.3"
82 | }
83 | },
84 | {
85 | "identity" : "swifterswift",
86 | "kind" : "remoteSourceControl",
87 | "location" : "https://github.com/SwifterSwift/SwifterSwift",
88 | "state" : {
89 | "revision" : "dc813f3aa92f904a009fe543b99d5649dce7c134",
90 | "version" : "5.2.0"
91 | }
92 | }
93 | ],
94 | "version" : 2
95 | }
96 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/project.xcworkspace/xcuserdata/danhart.xcuserdatad/IDEFindNavigatorScopes.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/xcshareddata/xcbaselines/52BABB4327E7DE1900999F01.xcbaseline/21B39069-B75E-4407-A8FF-E868E50C66DA.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | classNames
6 |
7 | Fragment_macOS_UITests
8 |
9 | testLaunchPerformance()
10 |
11 | com.apple.dt.XCTMetric_ApplicationLaunch-ApplicationLaunch.duration
12 |
13 | baselineAverage
14 | 1.008654
15 | baselineIntegrationDisplayName
16 | Local Baseline
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/xcshareddata/xcbaselines/52BABB4327E7DE1900999F01.xcbaseline/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | runDestinationsByUUID
6 |
7 | 21B39069-B75E-4407-A8FF-E868E50C66DA
8 |
9 | localComputer
10 |
11 | busSpeedInMHz
12 | 400
13 | cpuCount
14 | 1
15 | cpuKind
16 | 6-Core Intel Core i5
17 | cpuSpeedInMHz
18 | 3000
19 | logicalCPUCoresPerPackage
20 | 6
21 | modelCode
22 | iMac19,1
23 | physicalCPUCoresPerPackage
24 | 6
25 | platformIdentifier
26 | com.apple.platform.macosx
27 |
28 | targetArchitecture
29 | x86_64
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/xcshareddata/xcbaselines/52BABB5227E7DE3300999F01.xcbaseline/AE7DEE5B-077D-4779-9906-03EA21164357.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | classNames
6 |
7 | Fragment_iOS_UITests
8 |
9 | testLaunchPerformance()
10 |
11 | com.apple.dt.XCTMetric_ApplicationLaunch-AppLaunch.duration
12 |
13 | baselineAverage
14 | 1.329440
15 | baselineIntegrationDisplayName
16 | Local Baseline
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/xcshareddata/xcbaselines/52BABB5227E7DE3300999F01.xcbaseline/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | runDestinationsByUUID
6 |
7 | AE7DEE5B-077D-4779-9906-03EA21164357
8 |
9 | localComputer
10 |
11 | busSpeedInMHz
12 | 400
13 | cpuCount
14 | 1
15 | cpuKind
16 | 6-Core Intel Core i5
17 | cpuSpeedInMHz
18 | 3000
19 | logicalCPUCoresPerPackage
20 | 6
21 | modelCode
22 | iMac19,1
23 | physicalCPUCoresPerPackage
24 | 6
25 | platformIdentifier
26 | com.apple.platform.macosx
27 |
28 | targetArchitecture
29 | x86_64
30 | targetDevice
31 |
32 | modelCode
33 | iPhone14,6
34 | platformIdentifier
35 | com.apple.platform.iphonesimulator
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
35 |
41 |
42 |
43 |
44 |
45 |
55 |
57 |
63 |
64 |
65 |
66 |
72 |
74 |
80 |
81 |
82 |
83 |
85 |
86 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/xcshareddata/xcschemes/macOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
35 |
41 |
42 |
43 |
44 |
45 |
55 |
57 |
63 |
64 |
65 |
66 |
72 |
74 |
80 |
81 |
82 |
83 |
85 |
86 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/xcuserdata/danhart.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/xcuserdata/danhart.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Build-and-Test-all.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 6
11 |
12 | Examples (Playground) 1.xcscheme
13 |
14 | isShown
15 |
16 | orderHint
17 | 6
18 |
19 | Examples (Playground) 2.xcscheme
20 |
21 | isShown
22 |
23 | orderHint
24 | 7
25 |
26 | Examples (Playground).xcscheme
27 |
28 | isShown
29 |
30 | orderHint
31 | 5
32 |
33 | FileKit (Playground) 1.xcscheme
34 |
35 | isShown
36 |
37 | orderHint
38 | 3
39 |
40 | FileKit (Playground) 2.xcscheme
41 |
42 | isShown
43 |
44 | orderHint
45 | 4
46 |
47 | FileKit (Playground) 3.xcscheme
48 |
49 | isShown
50 |
51 | orderHint
52 | 7
53 |
54 | FileKit (Playground) 4.xcscheme
55 |
56 | isShown
57 |
58 | orderHint
59 | 8
60 |
61 | FileKit (Playground) 5.xcscheme
62 |
63 | isShown
64 |
65 | orderHint
66 | 9
67 |
68 | FileKit (Playground) 6.xcscheme
69 |
70 | isShown
71 |
72 | orderHint
73 | 3
74 |
75 | FileKit (Playground) 7.xcscheme
76 |
77 | isShown
78 |
79 | orderHint
80 | 10
81 |
82 | FileKit (Playground) 8.xcscheme
83 |
84 | isShown
85 |
86 | orderHint
87 | 11
88 |
89 | FileKit (Playground).xcscheme
90 |
91 | isShown
92 |
93 | orderHint
94 | 2
95 |
96 | Playground_iOS (Playground) 1.xcscheme
97 |
98 | isShown
99 |
100 | orderHint
101 | 3
102 |
103 | Playground_iOS (Playground) 2.xcscheme
104 |
105 | isShown
106 |
107 | orderHint
108 | 4
109 |
110 | Playground_iOS (Playground).xcscheme
111 |
112 | isShown
113 |
114 | orderHint
115 | 2
116 |
117 | Playground_macOS (Playground) 1.xcscheme
118 |
119 | isShown
120 |
121 | orderHint
122 | 6
123 |
124 | Playground_macOS (Playground) 2.xcscheme
125 |
126 | isShown
127 |
128 | orderHint
129 | 7
130 |
131 | Playground_macOS (Playground).xcscheme
132 |
133 | isShown
134 |
135 | orderHint
136 | 5
137 |
138 | iOS.xcscheme_^#shared#^_
139 |
140 | orderHint
141 | 0
142 |
143 | macOS.xcscheme_^#shared#^_
144 |
145 | orderHint
146 | 1
147 |
148 |
149 | SuppressBuildableAutocreation
150 |
151 | 525D85BE27E760A60066B147
152 |
153 | primary
154 |
155 |
156 | 525D85C427E760A60066B147
157 |
158 | primary
159 |
160 |
161 | 525D85CB27E760A60066B147
162 |
163 | primary
164 |
165 |
166 | 52BABB3227E7DD5500999F01
167 |
168 | primary
169 |
170 |
171 | 52BABB4327E7DE1900999F01
172 |
173 | primary
174 |
175 |
176 |
177 |
178 |
179 |
--------------------------------------------------------------------------------
/Fragment.xcodeproj/xcuserdata/dhart.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Examples (Playground) 1.xcscheme
8 |
9 | isShown
10 |
11 | orderHint
12 | 7
13 |
14 | Examples (Playground) 2.xcscheme
15 |
16 | isShown
17 |
18 | orderHint
19 | 8
20 |
21 | Examples (Playground) 3.xcscheme
22 |
23 | isShown
24 |
25 | orderHint
26 | 11
27 |
28 | Examples (Playground) 4.xcscheme
29 |
30 | isShown
31 |
32 | orderHint
33 | 12
34 |
35 | Examples (Playground) 5.xcscheme
36 |
37 | isShown
38 |
39 | orderHint
40 | 13
41 |
42 | Examples (Playground).xcscheme
43 |
44 | isShown
45 |
46 | orderHint
47 | 2
48 |
49 | FileKit (Playground) 1.xcscheme
50 |
51 | isShown
52 |
53 | orderHint
54 | 4
55 |
56 | FileKit (Playground) 2.xcscheme
57 |
58 | isShown
59 |
60 | orderHint
61 | 5
62 |
63 | FileKit (Playground) 3.xcscheme
64 |
65 | isShown
66 |
67 | orderHint
68 | 6
69 |
70 | FileKit (Playground) 4.xcscheme
71 |
72 | isShown
73 |
74 | orderHint
75 | 9
76 |
77 | FileKit (Playground) 5.xcscheme
78 |
79 | isShown
80 |
81 | orderHint
82 | 10
83 |
84 | FileKit (Playground).xcscheme
85 |
86 | isShown
87 |
88 | orderHint
89 | 3
90 |
91 | iOS.xcscheme_^#shared#^_
92 |
93 | orderHint
94 | 0
95 |
96 | macOS.xcscheme_^#shared#^_
97 |
98 | orderHint
99 | 1
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/FragmentSharedTests/TestApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestApp.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/20/22.
6 | //
7 |
8 | @testable import Fragment
9 | import XCTest
10 |
11 | class TestApp: XCTestCase {
12 | static func appTest() {
13 | let app = FragmentApp()
14 | XCTAssertNotNil(app)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | GNU General Public License
2 | ==========================
3 |
4 | _Version 3, 29 June 2007_
5 | _Copyright © 2007 Free Software Foundation, Inc. <>_
6 |
7 | Everyone is permitted to copy and distribute verbatim copies of this license
8 | document, but changing it is not allowed.
9 |
10 | ## Preamble
11 |
12 | The GNU General Public License is a free, copyleft license for software and other
13 | kinds of works.
14 |
15 | The licenses for most software and other practical works are designed to take away
16 | your freedom to share and change the works. By contrast, the GNU General Public
17 | License is intended to guarantee your freedom to share and change all versions of a
18 | program--to make sure it remains free software for all its users. We, the Free
19 | Software Foundation, use the GNU General Public License for most of our software; it
20 | applies also to any other work released this way by its authors. You can apply it to
21 | your programs, too.
22 |
23 | When we speak of free software, we are referring to freedom, not price. Our General
24 | Public Licenses are designed to make sure that you have the freedom to distribute
25 | copies of free software (and charge for them if you wish), that you receive source
26 | code or can get it if you want it, that you can change the software or use pieces of
27 | it in new free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you these rights or
30 | asking you to surrender the rights. Therefore, you have certain responsibilities if
31 | you distribute copies of the software, or if you modify it: responsibilities to
32 | respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether gratis or for a fee,
35 | you must pass on to the recipients the same freedoms that you received. You must make
36 | sure that they, too, receive or can get the source code. And you must show them these
37 | terms so they know their rights.
38 |
39 | Developers that use the GNU GPL protect your rights with two steps: **(1)** assert
40 | copyright on the software, and **(2)** offer you this License giving you legal permission
41 | to copy, distribute and/or modify it.
42 |
43 | For the developers' and authors' protection, the GPL clearly explains that there is
44 | no warranty for this free software. For both users' and authors' sake, the GPL
45 | requires that modified versions be marked as changed, so that their problems will not
46 | be attributed erroneously to authors of previous versions.
47 |
48 | Some devices are designed to deny users access to install or run modified versions of
49 | the software inside them, although the manufacturer can do so. This is fundamentally
50 | incompatible with the aim of protecting users' freedom to change the software. The
51 | systematic pattern of such abuse occurs in the area of products for individuals to
52 | use, which is precisely where it is most unacceptable. Therefore, we have designed
53 | this version of the GPL to prohibit the practice for those products. If such problems
54 | arise substantially in other domains, we stand ready to extend this provision to
55 | those domains in future versions of the GPL, as needed to protect the freedom of
56 | users.
57 |
58 | Finally, every program is threatened constantly by software patents. States should
59 | not allow patents to restrict development and use of software on general-purpose
60 | computers, but in those that do, we wish to avoid the special danger that patents
61 | applied to a free program could make it effectively proprietary. To prevent this, the
62 | GPL assures that patents cannot be used to render the program non-free.
63 |
64 | The precise terms and conditions for copying, distribution and modification follow.
65 |
66 | ## TERMS AND CONDITIONS
67 |
68 | ### 0. Definitions
69 |
70 | “This License” refers to version 3 of the GNU General Public License.
71 |
72 | “Copyright” also means copyright-like laws that apply to other kinds of
73 | works, such as semiconductor masks.
74 |
75 | “The Program” refers to any copyrightable work licensed under this
76 | License. Each licensee is addressed as “you”. “Licensees” and
77 | “recipients” may be individuals or organizations.
78 |
79 | To “modify” a work means to copy from or adapt all or part of the work in
80 | a fashion requiring copyright permission, other than the making of an exact copy. The
81 | resulting work is called a “modified version” of the earlier work or a
82 | work “based on” the earlier work.
83 |
84 | A “covered work” means either the unmodified Program or a work based on
85 | the Program.
86 |
87 | To “propagate” a work means to do anything with it that, without
88 | permission, would make you directly or secondarily liable for infringement under
89 | applicable copyright law, except executing it on a computer or modifying a private
90 | copy. Propagation includes copying, distribution (with or without modification),
91 | making available to the public, and in some countries other activities as well.
92 |
93 | To “convey” a work means any kind of propagation that enables other
94 | parties to make or receive copies. Mere interaction with a user through a computer
95 | network, with no transfer of a copy, is not conveying.
96 |
97 | An interactive user interface displays “Appropriate Legal Notices” to the
98 | extent that it includes a convenient and prominently visible feature that **(1)**
99 | displays an appropriate copyright notice, and **(2)** tells the user that there is no
100 | warranty for the work (except to the extent that warranties are provided), that
101 | licensees may convey the work under this License, and how to view a copy of this
102 | License. If the interface presents a list of user commands or options, such as a
103 | menu, a prominent item in the list meets this criterion.
104 |
105 | ### 1. Source Code
106 |
107 | The “source code” for a work means the preferred form of the work for
108 | making modifications to it. “Object code” means any non-source form of a
109 | work.
110 |
111 | A “Standard Interface” means an interface that either is an official
112 | standard defined by a recognized standards body, or, in the case of interfaces
113 | specified for a particular programming language, one that is widely used among
114 | developers working in that language.
115 |
116 | The “System Libraries” of an executable work include anything, other than
117 | the work as a whole, that **(a)** is included in the normal form of packaging a Major
118 | Component, but which is not part of that Major Component, and **(b)** serves only to
119 | enable use of the work with that Major Component, or to implement a Standard
120 | Interface for which an implementation is available to the public in source code form.
121 | A “Major Component”, in this context, means a major essential component
122 | (kernel, window system, and so on) of the specific operating system (if any) on which
123 | the executable work runs, or a compiler used to produce the work, or an object code
124 | interpreter used to run it.
125 |
126 | The “Corresponding Source” for a work in object code form means all the
127 | source code needed to generate, install, and (for an executable work) run the object
128 | code and to modify the work, including scripts to control those activities. However,
129 | it does not include the work's System Libraries, or general-purpose tools or
130 | generally available free programs which are used unmodified in performing those
131 | activities but which are not part of the work. For example, Corresponding Source
132 | includes interface definition files associated with source files for the work, and
133 | the source code for shared libraries and dynamically linked subprograms that the work
134 | is specifically designed to require, such as by intimate data communication or
135 | control flow between those subprograms and other parts of the work.
136 |
137 | The Corresponding Source need not include anything that users can regenerate
138 | automatically from other parts of the Corresponding Source.
139 |
140 | The Corresponding Source for a work in source code form is that same work.
141 |
142 | ### 2. Basic Permissions
143 |
144 | All rights granted under this License are granted for the term of copyright on the
145 | Program, and are irrevocable provided the stated conditions are met. This License
146 | explicitly affirms your unlimited permission to run the unmodified Program. The
147 | output from running a covered work is covered by this License only if the output,
148 | given its content, constitutes a covered work. This License acknowledges your rights
149 | of fair use or other equivalent, as provided by copyright law.
150 |
151 | You may make, run and propagate covered works that you do not convey, without
152 | conditions so long as your license otherwise remains in force. You may convey covered
153 | works to others for the sole purpose of having them make modifications exclusively
154 | for you, or provide you with facilities for running those works, provided that you
155 | comply with the terms of this License in conveying all material for which you do not
156 | control copyright. Those thus making or running the covered works for you must do so
157 | exclusively on your behalf, under your direction and control, on terms that prohibit
158 | them from making any copies of your copyrighted material outside their relationship
159 | with you.
160 |
161 | Conveying under any other circumstances is permitted solely under the conditions
162 | stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
163 |
164 | ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law
165 |
166 | No covered work shall be deemed part of an effective technological measure under any
167 | applicable law fulfilling obligations under article 11 of the WIPO copyright treaty
168 | adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention
169 | of such measures.
170 |
171 | When you convey a covered work, you waive any legal power to forbid circumvention of
172 | technological measures to the extent such circumvention is effected by exercising
173 | rights under this License with respect to the covered work, and you disclaim any
174 | intention to limit operation or modification of the work as a means of enforcing,
175 | against the work's users, your or third parties' legal rights to forbid circumvention
176 | of technological measures.
177 |
178 | ### 4. Conveying Verbatim Copies
179 |
180 | You may convey verbatim copies of the Program's source code as you receive it, in any
181 | medium, provided that you conspicuously and appropriately publish on each copy an
182 | appropriate copyright notice; keep intact all notices stating that this License and
183 | any non-permissive terms added in accord with section 7 apply to the code; keep
184 | intact all notices of the absence of any warranty; and give all recipients a copy of
185 | this License along with the Program.
186 |
187 | You may charge any price or no price for each copy that you convey, and you may offer
188 | support or warranty protection for a fee.
189 |
190 | ### 5. Conveying Modified Source Versions
191 |
192 | You may convey a work based on the Program, or the modifications to produce it from
193 | the Program, in the form of source code under the terms of section 4, provided that
194 | you also meet all of these conditions:
195 |
196 | * **a)** The work must carry prominent notices stating that you modified it, and giving a
197 | relevant date.
198 | * **b)** The work must carry prominent notices stating that it is released under this
199 | License and any conditions added under section 7. This requirement modifies the
200 | requirement in section 4 to “keep intact all notices”.
201 | * **c)** You must license the entire work, as a whole, under this License to anyone who
202 | comes into possession of a copy. This License will therefore apply, along with any
203 | applicable section 7 additional terms, to the whole of the work, and all its parts,
204 | regardless of how they are packaged. This License gives no permission to license the
205 | work in any other way, but it does not invalidate such permission if you have
206 | separately received it.
207 | * **d)** If the work has interactive user interfaces, each must display Appropriate Legal
208 | Notices; however, if the Program has interactive interfaces that do not display
209 | Appropriate Legal Notices, your work need not make them do so.
210 |
211 | A compilation of a covered work with other separate and independent works, which are
212 | not by their nature extensions of the covered work, and which are not combined with
213 | it such as to form a larger program, in or on a volume of a storage or distribution
214 | medium, is called an “aggregate” if the compilation and its resulting
215 | copyright are not used to limit the access or legal rights of the compilation's users
216 | beyond what the individual works permit. Inclusion of a covered work in an aggregate
217 | does not cause this License to apply to the other parts of the aggregate.
218 |
219 | ### 6. Conveying Non-Source Forms
220 |
221 | You may convey a covered work in object code form under the terms of sections 4 and
222 | 5, provided that you also convey the machine-readable Corresponding Source under the
223 | terms of this License, in one of these ways:
224 |
225 | * **a)** Convey the object code in, or embodied in, a physical product (including a
226 | physical distribution medium), accompanied by the Corresponding Source fixed on a
227 | durable physical medium customarily used for software interchange.
228 | * **b)** Convey the object code in, or embodied in, a physical product (including a
229 | physical distribution medium), accompanied by a written offer, valid for at least
230 | three years and valid for as long as you offer spare parts or customer support for
231 | that product model, to give anyone who possesses the object code either **(1)** a copy of
232 | the Corresponding Source for all the software in the product that is covered by this
233 | License, on a durable physical medium customarily used for software interchange, for
234 | a price no more than your reasonable cost of physically performing this conveying of
235 | source, or **(2)** access to copy the Corresponding Source from a network server at no
236 | charge.
237 | * **c)** Convey individual copies of the object code with a copy of the written offer to
238 | provide the Corresponding Source. This alternative is allowed only occasionally and
239 | noncommercially, and only if you received the object code with such an offer, in
240 | accord with subsection 6b.
241 | * **d)** Convey the object code by offering access from a designated place (gratis or for
242 | a charge), and offer equivalent access to the Corresponding Source in the same way
243 | through the same place at no further charge. You need not require recipients to copy
244 | the Corresponding Source along with the object code. If the place to copy the object
245 | code is a network server, the Corresponding Source may be on a different server
246 | (operated by you or a third party) that supports equivalent copying facilities,
247 | provided you maintain clear directions next to the object code saying where to find
248 | the Corresponding Source. Regardless of what server hosts the Corresponding Source,
249 | you remain obligated to ensure that it is available for as long as needed to satisfy
250 | these requirements.
251 | * **e)** Convey the object code using peer-to-peer transmission, provided you inform
252 | other peers where the object code and Corresponding Source of the work are being
253 | offered to the general public at no charge under subsection 6d.
254 |
255 | A separable portion of the object code, whose source code is excluded from the
256 | Corresponding Source as a System Library, need not be included in conveying the
257 | object code work.
258 |
259 | A “User Product” is either **(1)** a “consumer product”, which
260 | means any tangible personal property which is normally used for personal, family, or
261 | household purposes, or **(2)** anything designed or sold for incorporation into a
262 | dwelling. In determining whether a product is a consumer product, doubtful cases
263 | shall be resolved in favor of coverage. For a particular product received by a
264 | particular user, “normally used” refers to a typical or common use of
265 | that class of product, regardless of the status of the particular user or of the way
266 | in which the particular user actually uses, or expects or is expected to use, the
267 | product. A product is a consumer product regardless of whether the product has
268 | substantial commercial, industrial or non-consumer uses, unless such uses represent
269 | the only significant mode of use of the product.
270 |
271 | “Installation Information” for a User Product means any methods,
272 | procedures, authorization keys, or other information required to install and execute
273 | modified versions of a covered work in that User Product from a modified version of
274 | its Corresponding Source. The information must suffice to ensure that the continued
275 | functioning of the modified object code is in no case prevented or interfered with
276 | solely because modification has been made.
277 |
278 | If you convey an object code work under this section in, or with, or specifically for
279 | use in, a User Product, and the conveying occurs as part of a transaction in which
280 | the right of possession and use of the User Product is transferred to the recipient
281 | in perpetuity or for a fixed term (regardless of how the transaction is
282 | characterized), the Corresponding Source conveyed under this section must be
283 | accompanied by the Installation Information. But this requirement does not apply if
284 | neither you nor any third party retains the ability to install modified object code
285 | on the User Product (for example, the work has been installed in ROM).
286 |
287 | The requirement to provide Installation Information does not include a requirement to
288 | continue to provide support service, warranty, or updates for a work that has been
289 | modified or installed by the recipient, or for the User Product in which it has been
290 | modified or installed. Access to a network may be denied when the modification itself
291 | materially and adversely affects the operation of the network or violates the rules
292 | and protocols for communication across the network.
293 |
294 | Corresponding Source conveyed, and Installation Information provided, in accord with
295 | this section must be in a format that is publicly documented (and with an
296 | implementation available to the public in source code form), and must require no
297 | special password or key for unpacking, reading or copying.
298 |
299 | ### 7. Additional Terms
300 |
301 | “Additional permissions” are terms that supplement the terms of this
302 | License by making exceptions from one or more of its conditions. Additional
303 | permissions that are applicable to the entire Program shall be treated as though they
304 | were included in this License, to the extent that they are valid under applicable
305 | law. If additional permissions apply only to part of the Program, that part may be
306 | used separately under those permissions, but the entire Program remains governed by
307 | this License without regard to the additional permissions.
308 |
309 | When you convey a copy of a covered work, you may at your option remove any
310 | additional permissions from that copy, or from any part of it. (Additional
311 | permissions may be written to require their own removal in certain cases when you
312 | modify the work.) You may place additional permissions on material, added by you to a
313 | covered work, for which you have or can give appropriate copyright permission.
314 |
315 | Notwithstanding any other provision of this License, for material you add to a
316 | covered work, you may (if authorized by the copyright holders of that material)
317 | supplement the terms of this License with terms:
318 |
319 | * **a)** Disclaiming warranty or limiting liability differently from the terms of
320 | sections 15 and 16 of this License; or
321 | * **b)** Requiring preservation of specified reasonable legal notices or author
322 | attributions in that material or in the Appropriate Legal Notices displayed by works
323 | containing it; or
324 | * **c)** Prohibiting misrepresentation of the origin of that material, or requiring that
325 | modified versions of such material be marked in reasonable ways as different from the
326 | original version; or
327 | * **d)** Limiting the use for publicity purposes of names of licensors or authors of the
328 | material; or
329 | * **e)** Declining to grant rights under trademark law for use of some trade names,
330 | trademarks, or service marks; or
331 | * **f)** Requiring indemnification of licensors and authors of that material by anyone
332 | who conveys the material (or modified versions of it) with contractual assumptions of
333 | liability to the recipient, for any liability that these contractual assumptions
334 | directly impose on those licensors and authors.
335 |
336 | All other non-permissive additional terms are considered “further
337 | restrictions” within the meaning of section 10. If the Program as you received
338 | it, or any part of it, contains a notice stating that it is governed by this License
339 | along with a term that is a further restriction, you may remove that term. If a
340 | license document contains a further restriction but permits relicensing or conveying
341 | under this License, you may add to a covered work material governed by the terms of
342 | that license document, provided that the further restriction does not survive such
343 | relicensing or conveying.
344 |
345 | If you add terms to a covered work in accord with this section, you must place, in
346 | the relevant source files, a statement of the additional terms that apply to those
347 | files, or a notice indicating where to find the applicable terms.
348 |
349 | Additional terms, permissive or non-permissive, may be stated in the form of a
350 | separately written license, or stated as exceptions; the above requirements apply
351 | either way.
352 |
353 | ### 8. Termination
354 |
355 | You may not propagate or modify a covered work except as expressly provided under
356 | this License. Any attempt otherwise to propagate or modify it is void, and will
357 | automatically terminate your rights under this License (including any patent licenses
358 | granted under the third paragraph of section 11).
359 |
360 | However, if you cease all violation of this License, then your license from a
361 | particular copyright holder is reinstated **(a)** provisionally, unless and until the
362 | copyright holder explicitly and finally terminates your license, and **(b)** permanently,
363 | if the copyright holder fails to notify you of the violation by some reasonable means
364 | prior to 60 days after the cessation.
365 |
366 | Moreover, your license from a particular copyright holder is reinstated permanently
367 | if the copyright holder notifies you of the violation by some reasonable means, this
368 | is the first time you have received notice of violation of this License (for any
369 | work) from that copyright holder, and you cure the violation prior to 30 days after
370 | your receipt of the notice.
371 |
372 | Termination of your rights under this section does not terminate the licenses of
373 | parties who have received copies or rights from you under this License. If your
374 | rights have been terminated and not permanently reinstated, you do not qualify to
375 | receive new licenses for the same material under section 10.
376 |
377 | ### 9. Acceptance Not Required for Having Copies
378 |
379 | You are not required to accept this License in order to receive or run a copy of the
380 | Program. Ancillary propagation of a covered work occurring solely as a consequence of
381 | using peer-to-peer transmission to receive a copy likewise does not require
382 | acceptance. However, nothing other than this License grants you permission to
383 | propagate or modify any covered work. These actions infringe copyright if you do not
384 | accept this License. Therefore, by modifying or propagating a covered work, you
385 | indicate your acceptance of this License to do so.
386 |
387 | ### 10. Automatic Licensing of Downstream Recipients
388 |
389 | Each time you convey a covered work, the recipient automatically receives a license
390 | from the original licensors, to run, modify and propagate that work, subject to this
391 | License. You are not responsible for enforcing compliance by third parties with this
392 | License.
393 |
394 | An “entity transaction” is a transaction transferring control of an
395 | organization, or substantially all assets of one, or subdividing an organization, or
396 | merging organizations. If propagation of a covered work results from an entity
397 | transaction, each party to that transaction who receives a copy of the work also
398 | receives whatever licenses to the work the party's predecessor in interest had or
399 | could give under the previous paragraph, plus a right to possession of the
400 | Corresponding Source of the work from the predecessor in interest, if the predecessor
401 | has it or can get it with reasonable efforts.
402 |
403 | You may not impose any further restrictions on the exercise of the rights granted or
404 | affirmed under this License. For example, you may not impose a license fee, royalty,
405 | or other charge for exercise of rights granted under this License, and you may not
406 | initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging
407 | that any patent claim is infringed by making, using, selling, offering for sale, or
408 | importing the Program or any portion of it.
409 |
410 | ### 11. Patents
411 |
412 | A “contributor” is a copyright holder who authorizes use under this
413 | License of the Program or a work on which the Program is based. The work thus
414 | licensed is called the contributor's “contributor version”.
415 |
416 | A contributor's “essential patent claims” are all patent claims owned or
417 | controlled by the contributor, whether already acquired or hereafter acquired, that
418 | would be infringed by some manner, permitted by this License, of making, using, or
419 | selling its contributor version, but do not include claims that would be infringed
420 | only as a consequence of further modification of the contributor version. For
421 | purposes of this definition, “control” includes the right to grant patent
422 | sublicenses in a manner consistent with the requirements of this License.
423 |
424 | Each contributor grants you a non-exclusive, worldwide, royalty-free patent license
425 | under the contributor's essential patent claims, to make, use, sell, offer for sale,
426 | import and otherwise run, modify and propagate the contents of its contributor
427 | version.
428 |
429 | In the following three paragraphs, a “patent license” is any express
430 | agreement or commitment, however denominated, not to enforce a patent (such as an
431 | express permission to practice a patent or covenant not to sue for patent
432 | infringement). To “grant” such a patent license to a party means to make
433 | such an agreement or commitment not to enforce a patent against the party.
434 |
435 | If you convey a covered work, knowingly relying on a patent license, and the
436 | Corresponding Source of the work is not available for anyone to copy, free of charge
437 | and under the terms of this License, through a publicly available network server or
438 | other readily accessible means, then you must either **(1)** cause the Corresponding
439 | Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the
440 | patent license for this particular work, or **(3)** arrange, in a manner consistent with
441 | the requirements of this License, to extend the patent license to downstream
442 | recipients. “Knowingly relying” means you have actual knowledge that, but
443 | for the patent license, your conveying the covered work in a country, or your
444 | recipient's use of the covered work in a country, would infringe one or more
445 | identifiable patents in that country that you have reason to believe are valid.
446 |
447 | If, pursuant to or in connection with a single transaction or arrangement, you
448 | convey, or propagate by procuring conveyance of, a covered work, and grant a patent
449 | license to some of the parties receiving the covered work authorizing them to use,
450 | propagate, modify or convey a specific copy of the covered work, then the patent
451 | license you grant is automatically extended to all recipients of the covered work and
452 | works based on it.
453 |
454 | A patent license is “discriminatory” if it does not include within the
455 | scope of its coverage, prohibits the exercise of, or is conditioned on the
456 | non-exercise of one or more of the rights that are specifically granted under this
457 | License. You may not convey a covered work if you are a party to an arrangement with
458 | a third party that is in the business of distributing software, under which you make
459 | payment to the third party based on the extent of your activity of conveying the
460 | work, and under which the third party grants, to any of the parties who would receive
461 | the covered work from you, a discriminatory patent license **(a)** in connection with
462 | copies of the covered work conveyed by you (or copies made from those copies), or **(b)**
463 | primarily for and in connection with specific products or compilations that contain
464 | the covered work, unless you entered into that arrangement, or that patent license
465 | was granted, prior to 28 March 2007.
466 |
467 | Nothing in this License shall be construed as excluding or limiting any implied
468 | license or other defenses to infringement that may otherwise be available to you
469 | under applicable patent law.
470 |
471 | ### 12. No Surrender of Others' Freedom
472 |
473 | If conditions are imposed on you (whether by court order, agreement or otherwise)
474 | that contradict the conditions of this License, they do not excuse you from the
475 | conditions of this License. If you cannot convey a covered work so as to satisfy
476 | simultaneously your obligations under this License and any other pertinent
477 | obligations, then as a consequence you may not convey it at all. For example, if you
478 | agree to terms that obligate you to collect a royalty for further conveying from
479 | those to whom you convey the Program, the only way you could satisfy both those terms
480 | and this License would be to refrain entirely from conveying the Program.
481 |
482 | ### 13. Use with the GNU Affero General Public License
483 |
484 | Notwithstanding any other provision of this License, you have permission to link or
485 | combine any covered work with a work licensed under version 3 of the GNU Affero
486 | General Public License into a single combined work, and to convey the resulting work.
487 | The terms of this License will continue to apply to the part which is the covered
488 | work, but the special requirements of the GNU Affero General Public License, section
489 | 13, concerning interaction through a network will apply to the combination as such.
490 |
491 | ### 14. Revised Versions of this License
492 |
493 | The Free Software Foundation may publish revised and/or new versions of the GNU
494 | General Public License from time to time. Such new versions will be similar in spirit
495 | to the present version, but may differ in detail to address new problems or concerns.
496 |
497 | Each version is given a distinguishing version number. If the Program specifies that
498 | a certain numbered version of the GNU General Public License “or any later
499 | version” applies to it, you have the option of following the terms and
500 | conditions either of that numbered version or of any later version published by the
501 | Free Software Foundation. If the Program does not specify a version number of the GNU
502 | General Public License, you may choose any version ever published by the Free
503 | Software Foundation.
504 |
505 | If the Program specifies that a proxy can decide which future versions of the GNU
506 | General Public License can be used, that proxy's public statement of acceptance of a
507 | version permanently authorizes you to choose that version for the Program.
508 |
509 | Later license versions may give you additional or different permissions. However, no
510 | additional obligations are imposed on any author or copyright holder as a result of
511 | your choosing to follow a later version.
512 |
513 | ### 15. Disclaimer of Warranty
514 |
515 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
516 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
517 | PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER
518 | EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
519 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
520 | QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
521 | DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
522 |
523 | ### 16. Limitation of Liability
524 |
525 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
526 | COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
527 | PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
528 | INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
529 | PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE
530 | OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
531 | WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
532 | POSSIBILITY OF SUCH DAMAGES.
533 |
534 | ### 17. Interpretation of Sections 15 and 16
535 |
536 | If the disclaimer of warranty and limitation of liability provided above cannot be
537 | given local legal effect according to their terms, reviewing courts shall apply local
538 | law that most closely approximates an absolute waiver of all civil liability in
539 | connection with the Program, unless a warranty or assumption of liability accompanies
540 | a copy of the Program in return for a fee.
541 |
542 | _END OF TERMS AND CONDITIONS_
543 |
544 | ## How to Apply These Terms to Your New Programs
545 |
546 | If you develop a new program, and you want it to be of the greatest possible use to
547 | the public, the best way to achieve this is to make it free software which everyone
548 | can redistribute and change under these terms.
549 |
550 | To do so, attach the following notices to the program. It is safest to attach them
551 | to the start of each source file to most effectively state the exclusion of warranty;
552 | and each file should have at least the “copyright” line and a pointer to
553 | where the full notice is found.
554 |
555 |
556 | Copyright (C)
557 |
558 | This program is free software: you can redistribute it and/or modify
559 | it under the terms of the GNU General Public License as published by
560 | the Free Software Foundation, either version 3 of the License, or
561 | (at your option) any later version.
562 |
563 | This program is distributed in the hope that it will be useful,
564 | but WITHOUT ANY WARRANTY; without even the implied warranty of
565 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
566 | GNU General Public License for more details.
567 |
568 | You should have received a copy of the GNU General Public License
569 | along with this program. If not, see .
570 |
571 | Also add information on how to contact you by electronic and paper mail.
572 |
573 | If the program does terminal interaction, make it output a short notice like this
574 | when it starts in an interactive mode:
575 |
576 | Copyright (C)
577 | This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
578 | This is free software, and you are welcome to redistribute it
579 | under certain conditions; type 'show c' for details.
580 |
581 | The hypothetical commands `show w` and `show c` should show the appropriate parts of
582 | the General Public License. Of course, your program's commands might be different;
583 | for a GUI interface, you would use an “about box”.
584 |
585 | You should also get your employer (if you work as a programmer) or school, if any, to
586 | sign a “copyright disclaimer” for the program, if necessary. For more
587 | information on this, and how to apply and follow the GNU GPL, see
588 | <>.
589 |
590 | The GNU General Public License does not permit incorporating your program into
591 | proprietary programs. If your program is a subroutine library, you may consider it
592 | more useful to permit linking proprietary applications with the library. If this is
593 | what you want to do, use the GNU Lesser General Public License instead of this
594 | License. But first, please read
595 | <>.
596 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fragment
2 | [](https://github.com/dan-hart/Fragment/issues)
3 | [](https://github.com/dan-hart/Fragment/network)
4 | [](https://github.com/dan-hart/Fragment/stargazers)
5 | [](https://github.com/dan-hart/Fragment)
6 | [](https://codeclimate.com/github/dan-hart/Fragment/maintainability)
7 | [](https://github.com/dan-hart/Fragment/actions/workflows/iOS-test.yml)
8 | [](https://github.com/dan-hart/Fragment/actions/workflows/macOS-test.yml)
9 | ### iOS & macOS Gist manager using Github
10 |
11 | 
12 |
13 | [](https://www.buymeacoffee.com/codedbydan)
14 |
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "extended-srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.412",
9 | "green" : "0.271",
10 | "red" : "0.769"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "extended-srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.412",
27 | "green" : "0.271",
28 | "red" : "0.958"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "filename" : "Icon-33.png",
10 | "idiom" : "mac",
11 | "scale" : "2x",
12 | "size" : "16x16"
13 | },
14 | {
15 | "filename" : "Icon-32.png",
16 | "idiom" : "mac",
17 | "scale" : "1x",
18 | "size" : "32x32"
19 | },
20 | {
21 | "filename" : "Icon-65.png",
22 | "idiom" : "mac",
23 | "scale" : "2x",
24 | "size" : "32x32"
25 | },
26 | {
27 | "filename" : "Icon-128.png",
28 | "idiom" : "mac",
29 | "scale" : "1x",
30 | "size" : "128x128"
31 | },
32 | {
33 | "filename" : "Icon-257.png",
34 | "idiom" : "mac",
35 | "scale" : "2x",
36 | "size" : "128x128"
37 | },
38 | {
39 | "filename" : "Icon-256.png",
40 | "idiom" : "mac",
41 | "scale" : "1x",
42 | "size" : "256x256"
43 | },
44 | {
45 | "filename" : "Icon-513.png",
46 | "idiom" : "mac",
47 | "scale" : "2x",
48 | "size" : "256x256"
49 | },
50 | {
51 | "filename" : "Icon-512.png",
52 | "idiom" : "mac",
53 | "scale" : "1x",
54 | "size" : "512x512"
55 | },
56 | {
57 | "filename" : "Icon-1025.png",
58 | "idiom" : "mac",
59 | "scale" : "2x",
60 | "size" : "512x512"
61 | }
62 | ],
63 | "info" : {
64 | "author" : "xcode",
65 | "version" : 1
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-1025.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-1025.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-128.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-256.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-257.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-257.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-32.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-33.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-33.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-512.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-513.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-513.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-65.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon-macOS.appiconset/Icon-65.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "switchify_icon_v01-20@2x.png",
5 | "idiom" : "iphone",
6 | "scale" : "2x",
7 | "size" : "20x20"
8 | },
9 | {
10 | "filename" : "switchify_icon_v01-20@3x.png",
11 | "idiom" : "iphone",
12 | "scale" : "3x",
13 | "size" : "20x20"
14 | },
15 | {
16 | "filename" : "switchify_icon_v01-29@2x.png",
17 | "idiom" : "iphone",
18 | "scale" : "2x",
19 | "size" : "29x29"
20 | },
21 | {
22 | "filename" : "switchify_icon_v01-29@3x.png",
23 | "idiom" : "iphone",
24 | "scale" : "3x",
25 | "size" : "29x29"
26 | },
27 | {
28 | "filename" : "switchify_icon_v01-40@2x.png",
29 | "idiom" : "iphone",
30 | "scale" : "2x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "filename" : "switchify_icon_v01-40@3x.png",
35 | "idiom" : "iphone",
36 | "scale" : "3x",
37 | "size" : "40x40"
38 | },
39 | {
40 | "filename" : "switchify_icon_v01-60@2x.png",
41 | "idiom" : "iphone",
42 | "scale" : "2x",
43 | "size" : "60x60"
44 | },
45 | {
46 | "filename" : "switchify_icon_v01-60@3x.png",
47 | "idiom" : "iphone",
48 | "scale" : "3x",
49 | "size" : "60x60"
50 | },
51 | {
52 | "filename" : "switchify_icon_v01-20.png",
53 | "idiom" : "ipad",
54 | "scale" : "1x",
55 | "size" : "20x20"
56 | },
57 | {
58 | "filename" : "switchify_icon_v01-20@2x.png",
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "20x20"
62 | },
63 | {
64 | "filename" : "switchify_icon_v01-29.png",
65 | "idiom" : "ipad",
66 | "scale" : "1x",
67 | "size" : "29x29"
68 | },
69 | {
70 | "filename" : "switchify_icon_v01-29@2x.png",
71 | "idiom" : "ipad",
72 | "scale" : "2x",
73 | "size" : "29x29"
74 | },
75 | {
76 | "filename" : "switchify_icon_v01-40.png",
77 | "idiom" : "ipad",
78 | "scale" : "1x",
79 | "size" : "40x40"
80 | },
81 | {
82 | "filename" : "switchify_icon_v01-40@2x.png",
83 | "idiom" : "ipad",
84 | "scale" : "2x",
85 | "size" : "40x40"
86 | },
87 | {
88 | "filename" : "switchify_icon_v01-76.png",
89 | "idiom" : "ipad",
90 | "scale" : "1x",
91 | "size" : "76x76"
92 | },
93 | {
94 | "filename" : "switchify_icon_v01-76@2x.png",
95 | "idiom" : "ipad",
96 | "scale" : "2x",
97 | "size" : "76x76"
98 | },
99 | {
100 | "filename" : "switchify_icon_v01-83.5@2x.png",
101 | "idiom" : "ipad",
102 | "scale" : "2x",
103 | "size" : "83.5x83.5"
104 | },
105 | {
106 | "filename" : "switchify_icon_v01-1024.png",
107 | "idiom" : "ios-marketing",
108 | "scale" : "1x",
109 | "size" : "1024x1024"
110 | }
111 | ],
112 | "info" : {
113 | "author" : "xcode",
114 | "version" : 1
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-1024.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-20.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-20@2x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-20@3x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-29.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-29@2x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-29@3x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-40.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-40@2x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-40@3x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-60@2x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-60@3x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-76.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-76@2x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dan-hart/Fragment/31ccd2626d1b2abf94e282f0a50940e83afb6d16/Shared/Assets.xcassets/AppIcon.appiconset/switchify_icon_v01-83.5@2x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Shared/ClipboardHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClipboardHelper.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 4/2/22.
6 | //
7 |
8 | import Foundation
9 | #if canImport(UIKit)
10 | import UIKit
11 | #endif
12 | #if canImport(AppKit)
13 | import AppKit
14 | #endif
15 |
16 | enum ClipboardHelper {
17 | static func getText() -> String? {
18 | #if canImport(UIKit)
19 | return UIPasteboard.general.string
20 | #else
21 | var clipboardItems: [String] = []
22 | for element in NSPasteboard.general.pasteboardItems! {
23 | if let str = element.string(forType: NSPasteboard.PasteboardType(rawValue: "public.utf8-plain-text")) {
24 | clipboardItems.append(str)
25 | }
26 | }
27 |
28 | // Access the item in the clipboard
29 | return clipboardItems[ifExistsAt: 0]
30 | #endif
31 | }
32 |
33 | static func set(text: String) {
34 | #if canImport(UIKit)
35 | UIPasteboard.general.string = text
36 | #else
37 | let pasteBoard = NSPasteboard.general
38 | pasteBoard.clearContents()
39 | pasteBoard.writeObjects([text as NSString])
40 | #endif
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Shared/Constants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constants.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/26/22.
6 | //
7 |
8 | import Foundation
9 | #if canImport(UIKit)
10 | import UIKit
11 | #endif
12 |
13 | // swiftlint:disable line_length
14 | enum Constants {
15 | static let appName = "Fragment"
16 | static let buyMeACoffeeUsername = "codedbydan"
17 |
18 | enum URL: String {
19 | case repositoryOnGitHub = "https://github.com/dan-hart/Fragment"
20 | case buyMeACoffee = "https://www.buymeacoffee.com/codedbydan"
21 | case githubHowToPersonalAccessToken = "https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token"
22 | }
23 |
24 | enum Feature {
25 | // False
26 | static var ifNoGistsEnableCreateButton = false
27 | static var ifNoGistsEnablePullButton = false
28 |
29 | // True
30 | static var settingsEnabled = true
31 | }
32 |
33 | /// Is the current device running macOS or is it an iPad
34 | static func isMacOrPad() -> Bool {
35 | #if os(macOS)
36 | return true
37 | #endif
38 |
39 | #if canImport(UIKit)
40 | if UIDevice.current.userInterfaceIdiom == .pad {
41 | return true
42 | } else {
43 | return false
44 | }
45 | #endif
46 | }
47 | }
48 |
49 | // swiftlint:enable line_length
50 |
--------------------------------------------------------------------------------
/Shared/Extensions/Collections+ifExistsAt.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Collections+ifExistsAt.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/27/22.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Collection where Indices.Iterator.Element == Index {
11 | /// Returns the element at the specified index if it is within bounds, otherwise nil.
12 | subscript(ifExistsAt index: Index) -> Iterator.Element? {
13 | return indices.contains(index) ? self[index] : nil
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Shared/Extensions/OctoKitExtensions/GistExtensions/Gist+Custom.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Gist+Custom.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 4/2/22.
6 | //
7 |
8 | import Foundation
9 | import OctoKit
10 |
11 | public extension Gist {
12 | /// Non-optional identifier for a gist, uses a UUID as fallback
13 | var identifier: String {
14 | id ?? UUID().uuidString
15 | }
16 |
17 | /// The first filename
18 | var filename: String {
19 | "\(files.first?.key ?? "")"
20 | }
21 |
22 | /// The extension with no leading `.` of the filename of the first gist
23 | var fileExtension: String? {
24 | let filename = "\(files.first?.key ?? "")"
25 | if filename.isEmpty { return nil }
26 | return filename.pathExtension
27 | }
28 |
29 | // MARK: - Functions
30 |
31 | /// Does this gist meet the search criteria based on the search text
32 | func meetsSearchCriteria(text: String) -> Bool {
33 | let lowercasedSearchText = text.lowercased()
34 | let descriptionContainsText = description?.lowercased().contains(lowercasedSearchText) ?? false
35 | let fileExtensionContainsText = fileExtension?.lowercased().contains(lowercasedSearchText) ?? false
36 | let filenameContainsText = files.first?.value.filename?.lowercased().contains(lowercasedSearchText) ?? false
37 |
38 | if descriptionContainsText ||
39 | filenameContainsText ||
40 | fileExtensionContainsText
41 | {
42 | return true
43 | } else {
44 | return false
45 | }
46 | }
47 | }
48 |
49 | extension Gist: Equatable {
50 | public static func == (lhs: Gist, rhs: Gist) -> Bool {
51 | lhs.identifier == rhs.identifier
52 | }
53 | }
54 |
55 | extension Gist: Hashable {
56 | public func hash(into hasher: inout Hasher) {
57 | hasher.combine(identifier)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Shared/Extensions/OctoKitExtensions/GistExtensions/Gist+language.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Gist+language.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/27/22.
6 | //
7 |
8 | import Foundation
9 | import OctoKit
10 |
11 | extension Gist {
12 | var language: Language? {
13 | if let fileURL = url {
14 | return Language(url: fileURL)
15 | } else {
16 | return nil
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Shared/Extensions/OctoKitExtensions/GistExtensions/Gist+text.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Gist+text.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/27/22.
6 | //
7 |
8 | import Foundation
9 | import Highlightr
10 | import OctoKit
11 |
12 | extension Gist {
13 | var text: String {
14 | if let file = files.first, let url = file.value.rawURL, let text = try? String(contentsOf: url) {
15 | return text
16 | } else {
17 | return ""
18 | }
19 | }
20 |
21 | var lines: [String] {
22 | text.components(separatedBy: "\n")
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Shared/Fragment.xcdatamodeld/.xccurrentversion:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Shared/Fragment.xcdatamodeld/Shared.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Shared/FragmentApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FragmentApp.swift
3 | // Shared
4 | //
5 | // Created by Dan Hart on 3/20/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct FragmentApp: App {
12 | @StateObject var sessionHandler = SessionHandler()
13 |
14 | @State var isLoading = false
15 | @State var isSettingsLoading = false
16 |
17 | var body: some Scene {
18 | WindowGroup {
19 | MainView(isLoading: $isLoading)
20 | .environmentObject(sessionHandler)
21 | .alert(isPresented: $sessionHandler.isShowingAlert) {
22 | sessionHandler.alert ?? Alert(title: Text(""))
23 | }
24 | .onAppear {
25 | sessionHandler.callTask {
26 | isLoading = true
27 | try await sessionHandler.startSession(with: sessionHandler.token)
28 | isLoading = false
29 | }
30 | }
31 | }
32 |
33 | #if os(macOS)
34 | Settings {
35 | if Constants.Feature.settingsEnabled {
36 | SettingsView(isLoading: $isSettingsLoading)
37 | .environmentObject(sessionHandler)
38 | .frame(width: 400, height: 400)
39 | .redacted(reason: isSettingsLoading ? .placeholder : [])
40 | }
41 | }
42 | #endif
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Shared/Handler/SessionHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionHandler.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 4/4/22.
6 | //
7 |
8 | import Foundation
9 | import KeychainAccess
10 | import OctoKit
11 | import SwiftUI
12 |
13 | class SessionHandler: ObservableObject {
14 | var keychainKeyIdentifier = "FRAGMENT_GITHUB_API_TOKEN"
15 |
16 | // MARK: - Publishable data
17 |
18 | @Published public var isAuthenticated = false
19 | @Published public var gists: [Gist] = []
20 |
21 | private var configuration = TokenConfiguration()
22 |
23 | // MARK: - Computed
24 |
25 | var bundleID: String {
26 | Bundle.main.bundleIdentifier ?? ""
27 | }
28 |
29 | private var keychain: Keychain {
30 | Keychain(service: bundleID)
31 | }
32 |
33 | var token: String? {
34 | keychain[keychainKeyIdentifier]
35 | }
36 |
37 | // MARK: - Alerts
38 |
39 | @Published var alert: Alert? {
40 | didSet { isShowingAlert = alert != nil }
41 | }
42 |
43 | @Published var isShowingAlert = false
44 |
45 | // MARK: - Settings
46 |
47 | @AppStorage("fontSize") var fontSize: Int = 12 {
48 | didSet {
49 | cgFloatFontSize = CGFloat(fontSize)
50 | }
51 | }
52 |
53 | @Published var cgFloatFontSize: CGFloat = 12
54 |
55 | // MARK: - Initialization
56 |
57 | init() {
58 | cgFloatFontSize = CGFloat(fontSize)
59 | }
60 |
61 | // MARK: - Methods
62 |
63 | func invalidateSession() {
64 | keychain[keychainKeyIdentifier] = nil
65 | isAuthenticated = false
66 | }
67 |
68 | func startSession(with optionalToken: String? = nil) async throws {
69 | if let token = optionalToken {
70 | // Continue existing session
71 | configuration = try await authenticate(using: token)
72 | keychain[keychainKeyIdentifier] = token
73 | } else {
74 | // No token. Start a new session by providing one.
75 | }
76 | }
77 |
78 | // MARK: - Authentication
79 |
80 | private func getToken() throws -> String {
81 | guard let token = keychain[keychainKeyIdentifier] else {
82 | throw FragmentError.nilToken
83 | }
84 |
85 | return token
86 | }
87 |
88 | @MainActor
89 | private func authenticate(using token: String?) async throws -> TokenConfiguration {
90 | guard let token = token, !token.isEmpty else {
91 | throw FragmentError.nilToken
92 | }
93 |
94 | let configuration = TokenConfiguration(token)
95 | let response = await withCheckedContinuation { continuation in
96 | Octokit(configuration).me { response in
97 | continuation.resume(returning: response)
98 | }
99 | }
100 |
101 | switch response {
102 | case .success:
103 | isAuthenticated = true
104 | return configuration
105 | case let .failure(error):
106 | throw error
107 | }
108 | }
109 |
110 | // MARK: - Gist CRU
111 |
112 | func update(
113 | _ identifier: String,
114 | _ description: String,
115 | _ filename: String,
116 | _ content: String
117 | ) async throws -> Gist {
118 | try await validate()
119 |
120 | let response = await withCheckedContinuation { continuation in
121 | Octokit(configuration).patchGistFile(id: identifier,
122 | description: description,
123 | filename: filename,
124 | fileContent: content)
125 | { response in
126 | continuation.resume(returning: response)
127 | }
128 | }
129 |
130 | switch response {
131 | case let .success(gist):
132 | return gist
133 | case let .failure(error):
134 | throw error
135 | }
136 | }
137 |
138 | func create(
139 | gist filename: String,
140 | _ description: String,
141 | _ content: String,
142 | _ visibility: Visibility
143 | ) async throws -> Gist {
144 | try await validate()
145 |
146 | let response = await withCheckedContinuation { continuation in
147 | Octokit(configuration).postGistFile(
148 | description: description,
149 | filename: filename,
150 | fileContent: content,
151 | publicAccess: visibility == .public ? true : false
152 | ) { response in
153 | continuation.resume(returning: response)
154 | }
155 | }
156 |
157 | switch response {
158 | case let .success(gist):
159 | return gist
160 | case let .failure(error):
161 | throw error
162 | }
163 | }
164 |
165 | @MainActor
166 | func refreshGists() async throws {
167 | try await validate()
168 | gists = try await myGists()
169 | }
170 |
171 | func myGists() async throws -> [Gist] {
172 | try await validate()
173 |
174 | let response = await withCheckedContinuation { continuation in
175 | Octokit(configuration).myGists { response in
176 | continuation.resume(returning: response)
177 | }
178 | }
179 |
180 | switch response {
181 | case let .success(gists):
182 | return gists
183 | case .failure:
184 | throw FragmentError.couldNotFetchData
185 | }
186 | }
187 |
188 | // MARK: - Profile
189 |
190 | func me() async throws -> User {
191 | try await validate()
192 |
193 | let response = await withCheckedContinuation { continuation in
194 | Octokit(configuration).me { response in
195 | continuation.resume(returning: response)
196 | }
197 | }
198 |
199 | switch response {
200 | case let .success(user):
201 | return user
202 | case let .failure(error):
203 | throw error
204 | }
205 | }
206 |
207 | // MARK: - Helpers
208 |
209 | func validate() async throws {
210 | if !isAuthenticated { throw FragmentError.notAuthenticated }
211 | }
212 |
213 | func call(thisAsyncThrowingCode: @escaping () async throws -> Void) async {
214 | do {
215 | try await thisAsyncThrowingCode()
216 | } catch {
217 | var errorMessage = ""
218 | if error is FragmentError {
219 | errorMessage = (error as? FragmentError)?.rawValue ?? "Error"
220 | } else {
221 | errorMessage = error.localizedDescription
222 | }
223 |
224 | alert = Alert(title: Text("Oops!").font(.system(.body, design: .monospaced)), message: Text(errorMessage).font(.system(.caption, design: .monospaced)))
225 | }
226 | }
227 |
228 | func callTask(thisAsyncThrowingCode: @escaping () async throws -> Void) {
229 | Task {
230 | await call(thisAsyncThrowingCode: thisAsyncThrowingCode)
231 | }
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/Shared/Models/Errors/FragmentError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FragmentError.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 4/4/22.
6 | //
7 |
8 | import Foundation
9 |
10 | enum FragmentError: String, Error {
11 | case nilToken = "Empty Token"
12 | case nilConfiguratioin = "Invalid Configuration"
13 | case invalidToken = "Provided Token is invalid"
14 | case notAuthenticated = "Not Authenticated"
15 |
16 | // MARK: - Data
17 |
18 | case couldNotFetchData = "Could not get data. Check your network connection."
19 | }
20 |
--------------------------------------------------------------------------------
/Shared/Models/Language.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | // swiftlint:disable identifier_name
4 | enum Language: String, CaseIterable {
5 | case abnf
6 | case accesslog
7 | case actionscript
8 | case ada
9 | case angelscript
10 | case apache
11 | case applescript
12 | case arcade
13 | case cpp
14 | case arduino
15 | case armasm
16 | case xml
17 | case asciidoc
18 | case aspectj
19 | case autohotkey
20 | case autoit
21 | case avrasm
22 | case awk
23 | case axapta
24 | case bash
25 | case basic
26 | case bnf
27 | case brainfuck
28 | case cal
29 | case capnproto
30 | case ceylon
31 | case clean
32 | case clojure
33 | case cmake
34 | case coffeescript
35 | case coq
36 | case cos
37 | case crmsh
38 | case crystal
39 | case cs
40 | case csp
41 | case css
42 | case d
43 | case markdown
44 | case dart
45 | case delphi
46 | case diff
47 | case django
48 | case dns
49 | case dockerfile
50 | case dos
51 | case dsconfig
52 | case dts
53 | case dust
54 | case ebnf
55 | case elixir
56 | case elm
57 | case ruby
58 | case erb
59 | case erlang
60 | case excel
61 | case fix
62 | case flix
63 | case fortran
64 | case fsharp
65 | case gams
66 | case gauss
67 | case gcode
68 | case gherkin
69 | case glsl
70 | case gml
71 | case go
72 | case golo
73 | case gradle
74 | case groovy
75 | case haml
76 | case handlebars
77 | case haskell
78 | case haxe
79 | case hsp
80 | case htmlbars
81 | case html
82 | case http
83 | case hy
84 | case inform7
85 | case ini
86 | case irpf90
87 | case isbl
88 | case java
89 | case javascript
90 | case json
91 | case julia
92 | case kotlin
93 | case lasso
94 | case ldif
95 | case leaf
96 | case less
97 | case lisp
98 | case livecodeserver
99 | case livescript
100 | case llvm
101 | case lsl
102 | case lua
103 | case makefile
104 | case mathematica
105 | case matlab
106 | case maxima
107 | case mel
108 | case mercury
109 | case mipsasm
110 | case mizar
111 | case perl
112 | case mojolicious
113 | case monkey
114 | case moonscript
115 | case n1ql
116 | case nginx
117 | case nimrod
118 | case nix
119 | case nsis
120 | case objectivec
121 | case ocaml
122 | case openscad
123 | case oxygene
124 | case parser3
125 | case pf
126 | case pgsql
127 | case php
128 | case plaintext = "txt"
129 | case pony
130 | case powershell
131 | case processing
132 | case profile
133 | case prolog
134 | case properties
135 | case protobuf
136 | case puppet
137 | case purebasic
138 | case python
139 | case q
140 | case qml
141 | case r
142 | case reasonml
143 | case rib
144 | case roboconf
145 | case routeros
146 | case rsl
147 | case ruleslanguage
148 | case rust
149 | case sas
150 | case scala
151 | case scheme
152 | case scilab
153 | case scss
154 | case shell = "sh"
155 | case smali
156 | case smalltalk
157 | case sml
158 | case sqf
159 | case sql
160 | case stan
161 | case stata
162 | case step21
163 | case stylus
164 | case subunit
165 | case swift
166 | case taggerscript
167 | case yaml
168 | case tap
169 | case tcl
170 | case tex
171 | case thrift
172 | case tp
173 | case twig
174 | case typescript
175 | case vala
176 | case vbnet
177 | case vbscript
178 | case verilog
179 | case vhdl
180 | case vim
181 | case x86asm
182 | case xl
183 | case xquery
184 | case zephir
185 | case clojureRepl = "clojure-repl"
186 | case vbscriptHtml = "vbscript-html"
187 | case juliaRepl = "julia-repl"
188 | case jbossCli = "jboss-cli"
189 | case erlangRepl = "erlang-repl"
190 | case oneC = "1c"
191 |
192 | init?(url: URL) {
193 | let fileExtension = url.pathExtension.lowercased()
194 | switch fileExtension {
195 | case "js": self = .javascript
196 | case "tf": self = .typescript
197 | case "md": self = .markdown
198 | case "py": self = .python
199 | case "bat": self = .dos
200 | case "cxx", "h", "hpp", "hxx": self = .cpp
201 | case "scpt", "scptd", "applescript": self = .applescript
202 | case "pl": self = .perl
203 | case "txt": self = .plaintext
204 | default: self.init(rawValue: fileExtension)
205 | }
206 | }
207 | }
208 |
209 | // swiftlint:enable identifier_name
210 |
--------------------------------------------------------------------------------
/Shared/Models/Visibility.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Visibility.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/27/22.
6 | //
7 |
8 | import Foundation
9 | import SFSafeSymbols
10 | import SwiftUI
11 |
12 | enum Visibility: String, CaseIterable {
13 | case `public`
14 | case secret
15 |
16 | init(isPublic: Bool?) {
17 | if isPublic ?? false {
18 | self = .public
19 | } else {
20 | self = .secret
21 | }
22 | }
23 |
24 | var body: some View {
25 | var symbol: SFSymbol = .star
26 | switch self {
27 | case .public:
28 | symbol = .network
29 | case .secret:
30 | symbol = .lock
31 | }
32 |
33 | return HStack {
34 | Image(systemSymbol: symbol)
35 | Text(rawValue)
36 | .font(.system(.footnote, design: .monospaced))
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Shared/Views/Add/AddGistView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddGistView.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/27/22.
6 | //
7 |
8 | import CodeEditor
9 | import OctoKit
10 | import SwifterSwift
11 | import SwiftUI
12 |
13 | struct AddGistView: View {
14 | @EnvironmentObject var sessionHandler: SessionHandler
15 |
16 | @Environment(\.presentationMode) private var presentationMode: Binding
17 |
18 | @State var language: Language = .swift
19 | @State var filename: String
20 | @State var description: String
21 | @AppStorage("addingGistDefaultVisibility") var visibility: Visibility = .public
22 | @State var content: String
23 |
24 | @State var isAddingData = true
25 | @State var error: String?
26 |
27 | var didAdd: (Gist) -> Void
28 |
29 | let clipboard = ClipboardHelper.getText()
30 |
31 | @ViewBuilder
32 | func getSaveButton() -> some View {
33 | Button {
34 | let ext = ".\(language.rawValue)"
35 | if !$filename.wrappedValue.ends(with: ext) {
36 | filename.append(ext)
37 | }
38 | Task {
39 | do {
40 | let gist = try await sessionHandler.create(gist: filename, description, content, visibility)
41 | didAdd(gist)
42 | presentationMode.wrappedValue.dismiss()
43 |
44 | } catch {
45 | self.error = error.localizedDescription
46 | }
47 | }
48 | } label: {
49 | HStack {
50 | Text("Save")
51 | .font(.system(.body, design: .monospaced))
52 | }
53 | }
54 | }
55 |
56 | var body: some View {
57 | Form {
58 | Section(header: Text("Details").font(.system(.caption, design: .monospaced))) {
59 | TextField("File Name", text: $filename)
60 | .onChange(of: filename) { newValue in
61 | let filename: NSString = newValue as NSString
62 | let pathExtention = filename.pathExtension
63 | if let lang = Language(rawValue: pathExtention) {
64 | language = lang
65 | }
66 | }
67 | Picker("Language", selection: $language) {
68 | ForEach(Language.allCases, id: \.self) { language in
69 | Text(language.rawValue)
70 | .font(.system(.footnote, design: .monospaced))
71 | .tag(language.rawValue)
72 | }
73 | }
74 | TextField("Description", text: $description)
75 | Picker("Visibility", selection: $visibility) {
76 | ForEach(Visibility.allCases, id: \.self) { access in
77 | access.body.tag(access)
78 | }
79 | }
80 | }
81 | .font(.system(.caption, design: .monospaced))
82 |
83 | if clipboard != nil, !content.isEmpty {
84 | Button {
85 | content = ""
86 | } label: {
87 | Text("Clear Code")
88 | .font(.system(.body, design: .monospaced))
89 | }
90 | }
91 |
92 | Section(header: Text("Code").font(.system(.caption, design: .monospaced))) {
93 | CodeEditor(source: $content, language: CodeEditor.Language(rawValue: language.rawValue), fontSize: $sessionHandler.cgFloatFontSize)
94 | .font(.system(.caption, design: .monospaced))
95 | .frame(minHeight: 200)
96 | }
97 | .onAppear {
98 | if let clipboardText = clipboard {
99 | content = clipboardText
100 | }
101 | }
102 |
103 | getSaveButton()
104 |
105 | if error != nil {
106 | Section(header: Text("Error").font(.system(.body, design: .monospaced))) {
107 | Text(error ?? "")
108 | .font(.system(.body, design: .monospaced))
109 | }
110 | }
111 | }
112 | .toolbar {
113 | ToolbarItem(placement: .cancellationAction) {
114 | Button {
115 | presentationMode.wrappedValue.dismiss()
116 | } label: {
117 | Text("Cancel")
118 | .font(.system(.body, design: .monospaced))
119 | }
120 | }
121 |
122 | ToolbarItem(placement: .primaryAction) {
123 | getSaveButton()
124 | }
125 | }
126 |
127 | .navigationTitle(isAddingData ? "Add Gist" : "Edit Gist")
128 | }
129 | }
130 |
131 | struct EditGistView_Previews: PreviewProvider {
132 | static var previews: some View {
133 | AddGistView(
134 | filename: "",
135 | description: "",
136 | visibility: .public,
137 | content: ""
138 | ) { _ in
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/Shared/Views/AuthenticationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthenticationView.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/23/22.
6 | //
7 |
8 | import SFSafeSymbols
9 | import SwiftUI
10 |
11 | struct AuthenticationView: View {
12 | @EnvironmentObject var sessionHandler: SessionHandler
13 |
14 | @Binding var isLoading: Bool
15 |
16 | @State var token: String = ""
17 | @State var isShowingSupportThisAppView = false
18 |
19 | var body: some View {
20 | VStack(alignment: .center) {
21 | Spacer()
22 | Text("Github Personal Access Token")
23 | .font(.system(.body, design: .monospaced))
24 | SecureField(text: $token,
25 | prompt: Text("uZnVflqpqr2U1M9x984h3985a48dn74n").font(.system(.body, design: .monospaced))) {
26 | Text("Token")
27 | .font(.system(.body, design: .monospaced))
28 | }
29 | .onSubmit {
30 | go()
31 | }
32 |
33 | Button {
34 | go()
35 | } label: {
36 | HStack {
37 | Image(systemSymbol: SFSymbol.lock)
38 | Text("Validate")
39 | .font(.system(.body, design: .monospaced))
40 | }
41 | }
42 | Spacer()
43 | if let url = URL(string: Constants.URL.githubHowToPersonalAccessToken.rawValue) {
44 | Button {
45 | WebLauncher.go(to: url)
46 | } label: {
47 | HStack {
48 | Image(systemSymbol: SFSymbol.questionmarkCircle)
49 | Text("How To")
50 | .font(.system(.body, design: .monospaced))
51 | }
52 | }
53 | .padding(.bottom)
54 | }
55 | Divider()
56 | Button {
57 | isShowingSupportThisAppView = true
58 | } label: {
59 | HStack {
60 | Image(systemSymbol: SFSymbol.person3)
61 | Text("Support this app")
62 | .font(.system(.body, design: .monospaced))
63 | }
64 | }
65 | .padding(.top)
66 | Spacer()
67 | }
68 | .sheet(isPresented: $isShowingSupportThisAppView) {
69 | NavigationView {
70 | SupportThisAppView(showCancelButton: true)
71 | }
72 | }
73 | .redacted(reason: isLoading ? .placeholder : [])
74 | .padding()
75 |
76 | .navigationTitle("Authentication")
77 | }
78 |
79 | func go() {
80 | sessionHandler.callTask {
81 | try await sessionHandler.startSession(with: token)
82 | }
83 | }
84 | }
85 |
86 | struct AuthenticationView_Previews: PreviewProvider {
87 | static var previews: some View {
88 | AuthenticationView(isLoading: .constant(false))
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Shared/Views/CodeViews/CodeView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeView.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/20/22.
6 | //
7 |
8 | import CodeEditor
9 | import Foundation
10 | import Highlightr
11 | import OctoKit
12 | import SFSafeSymbols
13 | import SwiftUI
14 |
15 | struct CodeView: View {
16 | @EnvironmentObject var sessionHandler: SessionHandler
17 |
18 | @Environment(\.colorScheme) var colorScheme
19 |
20 | var theme: CodeEditor.ThemeName {
21 | colorScheme == .dark ? .atelierSavannaDark : .atelierSavannaLight
22 | }
23 |
24 | @Binding var gist: Gist
25 | @Binding var isLoadingParent: Bool
26 |
27 | @State var loadedSourceCode = ""
28 | @State var sourceCode = ""
29 |
30 | var body: some View {
31 | ScrollView([.horizontal, .vertical]) {
32 | ScrollViewReader { reader in
33 | CodeEditor(source: $sourceCode, language: CodeEditor.Language(rawValue: gist.fileExtension ?? ""), theme: theme, fontSize: $sessionHandler.cgFloatFontSize, flags: .defaultEditorFlags, indentStyle: .system, autoPairs: nil, inset: nil)
34 | .onAppear {
35 | reader.scrollTo(0, anchor: .topLeading)
36 | }
37 | }
38 | }
39 | .content.offset(x: 0, y: 0)
40 | .onAppear {
41 | loadedSourceCode = gist.text
42 | sourceCode = gist.text
43 | }
44 | .toolbar {
45 | ToolbarItem {
46 | HStack {
47 | if loadedSourceCode != sourceCode {
48 | Button {
49 | guard let identifier = gist.id,
50 | let description = gist.description,
51 | let filename = gist.files.first?.key
52 | else {
53 | return
54 | }
55 |
56 | isLoadingParent = true
57 | sessionHandler.callTask {
58 | let updatedGist = try await sessionHandler.update(identifier, description, filename, sourceCode)
59 | self.isLoadingParent = false
60 | await MainActor.run {
61 | self.sourceCode = updatedGist.text
62 | self.loadedSourceCode = updatedGist.text
63 | }
64 | }
65 | } label: {
66 | Text("Save")
67 | .font(.system(.body, design: .monospaced))
68 | }
69 | }
70 |
71 | Menu {
72 | // Menu Content
73 | Button {
74 | ClipboardHelper.set(text: gist.text)
75 | } label: {
76 | HStack {
77 | Image(systemSymbol: SFSymbol.docOnDoc)
78 | Text("Copy File Contents")
79 | .font(.system(.body, design: .monospaced))
80 | }
81 | }
82 |
83 | if let url = gist.htmlURL {
84 | Button {
85 | WebLauncher.go(to: url)
86 | } label: {
87 | HStack {
88 | Image(systemSymbol: SFSymbol.docRichtext)
89 | Text("Open on Web")
90 | .font(.system(.body, design: .monospaced))
91 | }
92 | }
93 | }
94 | // End Menu Content
95 | } label: {
96 | Image(systemSymbol: .ellipsisCircle)
97 | }
98 | }
99 | }
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Shared/Views/Container/ContainerView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContainerView.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/20/22.
6 | //
7 |
8 | import OctoKit
9 | import SFSafeSymbols
10 | import SwiftUI
11 |
12 | struct ContainerView: View {
13 | @EnvironmentObject var sessionHandler: SessionHandler
14 |
15 | @Binding var isLoading: Bool
16 |
17 | @State var isShowingAddModal = false
18 | @State var searchText = ""
19 |
20 | var body: some View {
21 | NavigationView {
22 | ListView(isLoading: $isLoading, searchText: $searchText, isShowingAddModal: $isShowingAddModal)
23 | SupportThisAppView(showCancelButton: false)
24 | }
25 | .sheet(isPresented: $isShowingAddModal) {
26 | #if os(iOS)
27 | NavigationView {
28 | AddGistView(filename: "", description: "", visibility: .public, content: "") { newGist in
29 | sessionHandler.gists.insert(newGist, at: 0)
30 | }
31 | }
32 | #endif
33 | #if os(macOS)
34 | AddGistView(filename: "", description: "", content: "") { newGist in
35 | sessionHandler.gists.insert(newGist, at: 0)
36 | }
37 | .frame(minWidth: 800, minHeight: 800)
38 | .padding()
39 | #endif
40 | }
41 | .toolbar {
42 | ToolbarItem(placement: .navigation) {
43 | Button {
44 | toggleSidebar()
45 | } label: {
46 | Image(systemSymbol: .sidebarLeading)
47 | }
48 | }
49 | }
50 | }
51 |
52 | private func toggleSidebar() {
53 | #if os(iOS)
54 | #else
55 | NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
56 | #endif
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Shared/Views/Container/ListView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListView.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 4/4/22.
6 | //
7 |
8 | import OctoKit
9 | import SFSafeSymbols
10 | import SwiftUI
11 |
12 | struct ListView: View {
13 | @EnvironmentObject var sessionHandler: SessionHandler
14 |
15 | @Binding var isLoading: Bool
16 | @Binding var searchText: String
17 | @Binding var isShowingAddModal: Bool
18 |
19 | @AppStorage("visibility") var visibility: Visibility = .public
20 |
21 | @State var isShowingSupportThisAppView = false
22 | @State var isShowingPreferencesView = false
23 |
24 | var filteredGists: [Gist] {
25 | let withVisibility = sessionHandler.gists.filter { gist in
26 | let gistVisibility = Visibility(isPublic: gist.publicGist)
27 | return gistVisibility == visibility
28 | }
29 |
30 | if searchText.isEmpty {
31 | return withVisibility
32 | } else {
33 | return withVisibility.filter { gist in
34 | gist.meetsSearchCriteria(text: searchText)
35 | }
36 | }
37 | }
38 |
39 | var body: some View {
40 | List {
41 | Picker("Visibility", selection: $visibility) {
42 | ForEach(Visibility.allCases, id: \.self) { access in
43 | Text(access.rawValue)
44 | .font(.system(.body, design: .monospaced))
45 | }
46 | }
47 | .pickerStyle(.segmented)
48 | .labelsHidden()
49 |
50 | .navigationTitle("Gists")
51 |
52 | if sessionHandler.gists.isEmpty {
53 | VStack {
54 | Text(isLoading ? "Loading..." : "No Gists")
55 | .font(.system(.body, design: .monospaced))
56 |
57 | if Constants.Feature.ifNoGistsEnableCreateButton {
58 | HStack {
59 | if sessionHandler.isAuthenticated {
60 | Button {
61 | isShowingAddModal.toggle()
62 | } label: {
63 | HStack {
64 | #if !os(macOS)
65 | Image(systemSymbol: .plusCircle)
66 | #endif
67 | Text("Create Gist")
68 | .font(.system(.body, design: .monospaced))
69 | }
70 | }
71 | }
72 | }
73 | }
74 |
75 | if Constants.Feature.ifNoGistsEnablePullButton, sessionHandler.isAuthenticated {
76 | VStack {
77 | Text("If this is unexpected, try pulling.")
78 | .padding()
79 | Button {
80 | sessionHandler.callTask {
81 | try await sessionHandler.refreshGists()
82 | }
83 | } label: {
84 | HStack {
85 | #if os(iOS)
86 | Image(systemSymbol: .arrowDownCircle)
87 | #endif
88 | Text("Pull")
89 | .font(.system(.body, design: .monospaced))
90 | #if os(macOS)
91 | .padding()
92 | #endif
93 | }
94 | }
95 | .padding()
96 | }
97 | .font(.system(.footnote, design: .monospaced))
98 | .padding(5)
99 | .overlay(
100 | RoundedRectangle(cornerRadius: 8)
101 | .strokeBorder()
102 | .foregroundColor(.gray)
103 | )
104 | }
105 | }
106 | } else {
107 | if !searchText.isEmpty {
108 | Text("Results")
109 | .font(.system(.body, design: .monospaced))
110 | }
111 | ForEach(filteredGists, id: \.self) { gist in
112 | NavigationLink {
113 | CodeView(gist: .constant(gist), isLoadingParent: $isLoading)
114 | .navigationTitle(gist.filename)
115 | } label: {
116 | GistRow(data: .constant(gist))
117 | .padding()
118 | }
119 | }
120 | }
121 | }
122 | .sheet(isPresented: $isShowingSupportThisAppView) {
123 | NavigationView {
124 | SupportThisAppView(showCancelButton: true)
125 | }
126 | }
127 | .sheet(isPresented: $isShowingPreferencesView) {
128 | NavigationView {
129 | SettingsView(isLoading: $isLoading)
130 |
131 | .navigationTitle("Settings")
132 | }
133 | }
134 | .onAppear {
135 | if !isLoading {
136 | sessionHandler.callTask {
137 | try await sessionHandler.refreshGists()
138 | }
139 | }
140 | }
141 | .refreshable {
142 | sessionHandler.callTask {
143 | try await sessionHandler.refreshGists()
144 | }
145 | }
146 | .searchable(text: $searchText)
147 | .redacted(reason: isLoading ? .placeholder : [])
148 | .toolbar {
149 | #if os(iOS)
150 | ToolbarItem(placement: .navigationBarLeading) {
151 | Menu {
152 | Button {
153 | isShowingSupportThisAppView = true
154 | } label: {
155 | HStack {
156 | Image(systemSymbol: SFSymbol.person2Circle)
157 | Text("Support this app")
158 | .font(.system(.body, design: .monospaced))
159 | }
160 | }
161 |
162 | Button {
163 | WebLauncher.go(to: URL(string: Constants.URL.buyMeACoffee.rawValue))
164 | } label: {
165 | HStack {
166 | Image(systemSymbol: .dollarsignCircle)
167 | Text("Donate")
168 | .font(.system(.body, design: .monospaced))
169 | }
170 | }
171 |
172 | if Constants.Feature.settingsEnabled {
173 | Button {
174 | isShowingPreferencesView = true
175 | } label: {
176 | HStack {
177 | Image(systemSymbol: .gearshape)
178 | Text("Preferences")
179 | .font(.system(.body, design: .monospaced))
180 | }
181 | }
182 | }
183 | } label: {
184 | Image(systemSymbol: .gearshape)
185 | }
186 | }
187 | #endif
188 |
189 | ToolbarItem(placement: .primaryAction) {
190 | HStack {
191 | if sessionHandler.isAuthenticated {
192 | Button {
193 | isShowingAddModal.toggle()
194 | } label: {
195 | HStack {
196 | Image(systemSymbol: .plusCircle)
197 | Text("Create")
198 | .font(.system(.body, design: .monospaced))
199 | }
200 | }
201 | #if os(macOS)
202 | .frame(minWidth: 100)
203 | #endif
204 | }
205 | }
206 | }
207 | }
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/Shared/Views/GistViews/GistRow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GistRow.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/20/22.
6 | //
7 |
8 | import OctoKit
9 | import SFSafeSymbols
10 | import SwiftUI
11 |
12 | struct GistRow: View {
13 | @Binding var data: Gist
14 |
15 | var filenameNoExtension: String? {
16 | ((data.files.first?.key ?? "") as NSString).deletingPathExtension
17 | }
18 |
19 | var body: some View {
20 | VStack(alignment: .leading) {
21 | Text(filenameNoExtension ?? (data.files.first?.key ?? "Unknown"))
22 | .font(.system(.headline, design: .monospaced))
23 | .lineLimit(1)
24 | .truncationMode(.middle)
25 | Spacer()
26 | if let description = data.description, !description.isEmpty {
27 | Text("\(description)")
28 | .font(.system(.caption, design: .monospaced))
29 | .lineLimit(2)
30 | .truncationMode(.middle)
31 | }
32 | Spacer()
33 | if let updated = data.updatedAt {
34 | Text("Updated \(updated.formatted(date: .abbreviated, time: .standard))")
35 | .font(.system(.caption2, design: .monospaced))
36 | .lineLimit(2)
37 | .truncationMode(.middle)
38 | }
39 | Spacer()
40 | HStack {
41 | Visibility(isPublic: data.publicGist).body
42 | Spacer()
43 | if let `extension` = data.fileExtension, !`extension`.isEmpty {
44 | Text(`extension`)
45 | .font(.system(.footnote, design: .monospaced))
46 | .lineLimit(1)
47 | .truncationMode(.middle)
48 | .padding(5)
49 | .overlay(
50 | RoundedRectangle(cornerRadius: 8)
51 | .strokeBorder()
52 | .foregroundColor(.gray)
53 | )
54 | }
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Shared/Views/MainView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainView.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/20/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MainView: View {
11 | @EnvironmentObject var sessionHandler: SessionHandler
12 |
13 | @Binding var isLoading: Bool
14 |
15 | var body: some View {
16 | if sessionHandler.isAuthenticated {
17 | ContainerView(isLoading: $isLoading)
18 | } else {
19 | if isLoading {
20 | ContainerView(isLoading: $isLoading) // Permanet loading
21 | .redacted(reason: .placeholder) //
22 | } else {
23 | NavigationView {
24 | #if os(macOS)
25 | EmptyView()
26 | AuthenticationView(isLoading: $isLoading)
27 | .padding()
28 | #endif
29 | #if os(iOS)
30 | AuthenticationView(isLoading: $isLoading)
31 | .padding()
32 | Text("Enter Github personal access token on the lefthand sidebar")
33 | .font(.system(.body, design: .monospaced))
34 | #endif
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
41 | struct MainView_Previews: PreviewProvider {
42 | static var previews: some View {
43 | MainView(isLoading: .constant(false))
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Shared/Views/SettingsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsView.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 4/4/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SettingsView: View {
11 | @EnvironmentObject var sessionHandler: SessionHandler
12 | @Environment(\.presentationMode) private var presentationMode: Binding
13 |
14 | @Binding var isLoading: Bool
15 |
16 | @State var name: String?
17 |
18 | var body: some View {
19 | TabView {
20 | if sessionHandler.isAuthenticated {
21 | VStack {
22 | Form {
23 | Section("General") {
24 | EmptyView()
25 | }
26 |
27 | Section {
28 | Button {
29 | sessionHandler.callTask {
30 | try await sessionHandler.refreshGists()
31 | }
32 | } label: {
33 | HStack {
34 | Image(systemSymbol: .arrowDownCircle)
35 | Text("Refresh")
36 | .font(.system(.body, design: .monospaced))
37 | }
38 | }
39 | } footer: {
40 | Text("Get new Gists from Github or use pull-to-refresh on the list")
41 | }
42 | .padding(.bottom)
43 |
44 | Section {
45 | Button {
46 | WebLauncher.go(to: URL(string: Constants.URL.buyMeACoffee.rawValue))
47 | } label: {
48 | HStack {
49 | Image(systemSymbol: .dollarsignCircle)
50 | Text("Donate")
51 | .font(.system(.body, design: .monospaced))
52 | }
53 | }
54 | .padding(.vertical)
55 | } footer: {
56 | Text("Support this app")
57 | }
58 | }
59 | }
60 | .tabItem {
61 | Label("General", systemImage: "gearshape")
62 | }
63 | }
64 |
65 | VStack {
66 | Form {
67 | Section {
68 | Stepper("Code Font Size: \(sessionHandler.fontSize)", value: $sessionHandler.fontSize, in: 8 ... 72)
69 | .padding()
70 | } footer: {
71 | Text("Affects areas where code is being used. Go to Settings to adjust other text size.")
72 | }
73 | }
74 | }
75 | .tabItem {
76 | Label("Appearance", systemImage: "paintpalette")
77 | }
78 |
79 | if sessionHandler.isAuthenticated {
80 | VStack {
81 | Form {
82 | Section("You") {
83 | Text(name ?? "Loading...")
84 | }
85 |
86 | Section {
87 | Button {
88 | sessionHandler.invalidateSession()
89 | } label: {
90 | HStack {
91 | Image(systemSymbol: .xmarkCircle)
92 | Text("Clear Token")
93 | .font(.system(.body, design: .monospaced))
94 | }
95 | }
96 | } footer: {
97 | Text("Clears all gists and discards your personal access token")
98 | }
99 | }
100 | }
101 | .task {
102 | isLoading = true
103 |
104 | sessionHandler.callTask {
105 | let user = try await sessionHandler.me()
106 | self.name = user.name
107 | }
108 |
109 | isLoading = false
110 | }
111 | .tabItem {
112 | Label("Profile", systemImage: "person.crop.circle")
113 | }
114 |
115 | VStack {
116 | Form {
117 | Section {
118 | Toggle("Disable Local Caching", isOn: .constant(false))
119 | .disabled(true)
120 | } footer: {
121 | Text("Not implemented yet")
122 | }
123 | }
124 | }
125 | .tabItem {
126 | Label("Privacy", systemImage: "hand.raised")
127 | }
128 | }
129 |
130 | SupportThisAppView(showCancelButton: false)
131 | .tabItem {
132 | Label("Support", systemImage: "person.3")
133 | }
134 | }
135 | #if os(iOS)
136 | .toolbar {
137 | ToolbarItem(placement: .primaryAction) {
138 | Button {
139 | presentationMode.wrappedValue.dismiss()
140 | } label: {
141 | Text("Done")
142 | .font(.system(.body, design: .monospaced))
143 | }
144 | }
145 | }
146 | #endif
147 | }
148 | }
149 |
150 | struct SettingsView_Previews: PreviewProvider {
151 | static var previews: some View {
152 | SettingsView(isLoading: .constant(false))
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/Shared/Views/Support/SupportThisAppView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SupportThisAppView.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 4/4/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SupportThisAppView: View {
11 | @Environment(\.presentationMode) private var presentationMode: Binding
12 |
13 | var showCancelButton: Bool
14 |
15 | var body: some View {
16 | VStack(alignment: .leading) {
17 | Spacer()
18 | Text("Support this project")
19 | .font(.system(.title, design: .monospaced))
20 | .padding(.bottom)
21 | Text("\(Constants.appName) will always be free and open-source.")
22 | .font(.system(.headline, design: .monospaced))
23 | .padding(.bottom)
24 | Text("If you have found this app useful, please consider:")
25 | .font(.system(.subheadline, design: .monospaced))
26 | Button {
27 | WebLauncher.go(to: URL(string: Constants.URL.repositoryOnGitHub.rawValue))
28 | } label: {
29 | Text("Contributing on Github ")
30 | }
31 | .padding()
32 | Text("or")
33 | .font(.system(.subheadline, design: .monospaced))
34 | .padding(.horizontal)
35 | Button {
36 | WebLauncher.go(to: URL(string: Constants.URL.buyMeACoffee.rawValue))
37 | } label: {
38 | Text("☕️ Buying Me A Coffee")
39 | }
40 | .padding()
41 | Text("Thank you,\nDan")
42 | .font(.system(.subheadline, design: .monospaced))
43 | Spacer()
44 | }
45 | .padding()
46 | .toolbar {
47 | ToolbarItem(placement: .cancellationAction) {
48 | if showCancelButton {
49 | Button {
50 | presentationMode.wrappedValue.dismiss()
51 | } label: {
52 | Text("Cancel")
53 | .font(.system(.body, design: .monospaced))
54 | }
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
61 | struct SupportView_Previews: PreviewProvider {
62 | static var previews: some View {
63 | SupportThisAppView(showCancelButton: false)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Shared/WebLauncher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebLauncher.swift
3 | // Fragment
4 | //
5 | // Created by Dan Hart on 3/27/22.
6 | //
7 |
8 | import Foundation
9 | #if canImport(UIKit)
10 | import UIKit
11 | #else
12 | import AppKit
13 | #endif
14 |
15 | enum WebLauncher {
16 | static func go(to url: URL?) {
17 | guard let url = url else {
18 | return
19 | }
20 |
21 | #if canImport(UIKit)
22 | Task {
23 | if await UIApplication.shared.open(url) {
24 | print("default browser was successfully opened")
25 | }
26 | }
27 | #else
28 | if NSWorkspace.shared.open(url) {
29 | print("default browser was successfully opened")
30 | }
31 | #endif
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sourceless/2011B4FA-8E52-4464-A976-76AA07DBDDE7.json:
--------------------------------------------------------------------------------
1 | {"iv":"b520b4eae4c5eb548d3425d94ac4cc0d","cryptKey":"705349475148785a737a373033686c77","cryptValue":"73f121ecb73229ba3c0a8453e9b3f16f0360d7d92a710550278b19d6ab1787033317fe40a40d7dcb0e4cba4a20521f0df3ebd3b28796867531c2045438c4ebef"}
--------------------------------------------------------------------------------
/autocommit.sh:
--------------------------------------------------------------------------------
1 | # LANG=C.UTF-8 or any UTF-8 English locale supported by your OS may be used
2 | rm -f .git/index.lock
3 | git add .
4 | LANG=C git -c color.status=false status \
5 | | sed -n -r -e '1,/Changes to be committed:/ d' \
6 | -e '1,1 d' \
7 | -e '/^Untracked files:/,$ d' \
8 | -e 's/^\s*//' \
9 | -e '/./p' \
10 | | git commit -F -
--------------------------------------------------------------------------------
/macOS/macOS.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 | com.apple.security.network.client
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------