├── Screenshots
└── sample.png
├── Examples
├── unicode
│ ├── images
│ │ ├── unicode-checker.png
│ │ ├── unicode-explained.jpg
│ │ └── the-unicode-standard.png
│ └── slides.swift
└── attr-string-in-swift
│ └── slides.swift
├── Truffaut
├── Truffaut-Bridging-Header.h
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ ├── Slice 1128w.png
│ │ ├── Slice 116w.png
│ │ ├── Slice 1256w.png
│ │ ├── Slice 132w.png
│ │ ├── Slice 1512w.png
│ │ ├── Slice 164w.png
│ │ ├── Slice 11024w.png
│ │ ├── Slice 1256w-1.png
│ │ ├── Slice 132w-1.png
│ │ ├── Slice 1512w-1.png
│ │ └── Contents.json
├── PreferenceWindowController.swift
├── NSWindow+Style.swift
├── syntax-hl.rb
├── MainWindowController.swift
├── SlidesWindowController.swift
├── MainViewController.swift
├── AppDelegate.swift
├── StoryboardLoading.swift
├── PreferenceViewController.swift
├── UserPreference.swift
├── Info.plist
├── Shell.swift
├── ExportController.swift
├── Font.swift
├── ReadController.swift
├── SyntaxHighlighter.swift
├── PresentationDocument.swift
├── SlidesViewController.swift
├── PageViewController.xib
├── PageViewController.swift
└── Base.lproj
│ └── Main.storyboard
├── Truffaut.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcshareddata
│ └── xcschemes
│ │ ├── Truffaut.xcscheme
│ │ └── TruffautSupport.xcscheme
└── project.pbxproj
├── TruffautSupport
├── Page.swift
├── TruffautSupport.h
├── Info.plist
├── Presentation.swift
└── Content.swift
├── TruffautSupportTests
├── Info.plist
└── TruffautSupportTests.swift
├── LICENSE
├── README.md
├── .gitignore
└── Documentations
└── TruffautSupport-API-Reference.md
/Screenshots/sample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Screenshots/sample.png
--------------------------------------------------------------------------------
/Examples/unicode/images/unicode-checker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Examples/unicode/images/unicode-checker.png
--------------------------------------------------------------------------------
/Examples/unicode/images/unicode-explained.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Examples/unicode/images/unicode-explained.jpg
--------------------------------------------------------------------------------
/Examples/unicode/images/the-unicode-standard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Examples/unicode/images/the-unicode-standard.png
--------------------------------------------------------------------------------
/Truffaut/Truffaut-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
--------------------------------------------------------------------------------
/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1128w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1128w.png
--------------------------------------------------------------------------------
/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 116w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 116w.png
--------------------------------------------------------------------------------
/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1256w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1256w.png
--------------------------------------------------------------------------------
/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 132w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 132w.png
--------------------------------------------------------------------------------
/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1512w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1512w.png
--------------------------------------------------------------------------------
/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 164w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 164w.png
--------------------------------------------------------------------------------
/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 11024w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 11024w.png
--------------------------------------------------------------------------------
/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1256w-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1256w-1.png
--------------------------------------------------------------------------------
/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 132w-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 132w-1.png
--------------------------------------------------------------------------------
/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1512w-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1512w-1.png
--------------------------------------------------------------------------------
/Truffaut.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Truffaut/PreferenceWindowController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PreferenceWindowController.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 29/12/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class PreferenceWindowController: NSWindowController {
12 |
13 | // ...
14 | }
15 |
16 | extension PreferenceWindowController: StoryboardInstantiatable {
17 |
18 | typealias ControllerType = PreferenceWindowController
19 | }
20 |
--------------------------------------------------------------------------------
/Truffaut/NSWindow+Style.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSWindow+Style.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 29/12/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import AppKit
10 |
11 | extension NSWindow {
12 |
13 | func applyTransparentTitlebarStyle() {
14 | styleMask.insert(NSWindow.StyleMask.fullSizeContentView)
15 | titlebarAppearsTransparent = true
16 | titleVisibility = .hidden
17 | isMovable = true
18 | isMovableByWindowBackground = true
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TruffautSupport/Page.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Page.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/3/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct Page: Codable {
12 |
13 | public let title: String?
14 | public let subtitle: String?
15 | public let contents: [Content]?
16 |
17 | public init(title: String? = nil, subtitle: String? = nil, contents: [Content]? = nil) {
18 | self.title = title
19 | self.subtitle = subtitle
20 | self.contents = contents
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/TruffautSupport/TruffautSupport.h:
--------------------------------------------------------------------------------
1 | //
2 | // TruffautSupport.h
3 | // TruffautSupport
4 | //
5 | // Created by Yan Li on 4/3/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for TruffautSupport.
12 | FOUNDATION_EXPORT double TruffautSupportVersionNumber;
13 |
14 | //! Project version string for TruffautSupport.
15 | FOUNDATION_EXPORT const unsigned char TruffautSupportVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Truffaut/syntax-hl.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'rouge'
4 |
5 | source = ARGV.shift
6 | sourceFileType = ARGV.shift
7 |
8 | case sourceFileType
9 | when 'c'
10 | lexer = Rouge::Lexers::C.new
11 | when 'cpp'
12 | lexer = Rouge::Lexers::Cpp.new
13 | when 'javaScript'
14 | lexer = Rouge::Lexers::Javascript.new
15 | when 'objc'
16 | lexer = Rouge::Lexers::ObjectiveC.new
17 | when 'rust'
18 | lexer = Rouge::Lexers::Rust.new
19 | when 'shell'
20 | lexer = Rouge::Lexers::Shell.new
21 | when 'swift'
22 | lexer = Rouge::Lexers::Swift.new
23 | end
24 |
25 | formatter = Rouge::Formatters::HTMLInline.new Rouge::Themes::MonokaiSublime.new
26 |
27 | result = formatter.format(lexer.lex(source))
28 | puts result
29 |
--------------------------------------------------------------------------------
/Truffaut/MainWindowController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainWindowController.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/15/16.
6 | // Copyright © 2016 Codezerker. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class MainWindowController: NSWindowController {
12 |
13 | override func awakeFromNib() {
14 | super.awakeFromNib()
15 |
16 | window?.applyTransparentTitlebarStyle()
17 |
18 | if let contentViewController = contentViewController as? MainViewController {
19 | contentViewController.windowController = self
20 | }
21 | }
22 | }
23 |
24 | extension MainWindowController: StoryboardInstantiatable {
25 |
26 | typealias ControllerType = MainWindowController
27 | }
28 |
--------------------------------------------------------------------------------
/Truffaut/SlidesWindowController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SlidesWindowController.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/15/16.
6 | // Copyright © 2016 Codezerker. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class SlidesWindowController: NSWindowController {
12 |
13 | override func awakeFromNib() {
14 | super.awakeFromNib()
15 |
16 | window?.applyTransparentTitlebarStyle()
17 |
18 | if let contentViewController = contentViewController as? SlidesViewController {
19 | contentViewController.windowController = self
20 | }
21 | }
22 | }
23 |
24 | extension SlidesWindowController: StoryboardInstantiatable {
25 |
26 | typealias ControllerType = SlidesWindowController
27 | }
28 |
--------------------------------------------------------------------------------
/TruffautSupportTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 204
21 |
22 |
23 |
--------------------------------------------------------------------------------
/TruffautSupport/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 204
21 | NSHumanReadableCopyright
22 | Copyright © 2017 Codezerker. All rights reserved.
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/TruffautSupport/Presentation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Presentation.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/3/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct Presentation: Codable {
12 |
13 | public let title: String
14 | public let authors: [String]
15 | public let pages: [Page]
16 |
17 | public init(title: String = "", authors: [String] = [], pages: [Page]) {
18 | self.title = title
19 | self.authors = authors
20 | self.pages = pages
21 |
22 | // dump self as a JSON String to STDOUT
23 | dump()
24 | }
25 | }
26 |
27 | private extension Presentation {
28 |
29 | private func dump() {
30 | let encoder = JSONEncoder()
31 | encoder.outputFormatting = .prettyPrinted
32 | guard let jsonData = try? encoder.encode(self),
33 | let jsonString = String(data: jsonData, encoding: .utf8) else {
34 | return
35 | }
36 | print(jsonString)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Truffaut/MainViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/14/16.
6 | // Copyright © 2016 Codezerker. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class MainViewController: NSViewController {
12 |
13 | @IBOutlet weak var pathControl: NSPathControl!
14 |
15 | weak var windowController: MainWindowController?
16 |
17 | private lazy var slidesWindowController: SlidesWindowController = {
18 | return SlidesWindowController.loadFromStoryboard()
19 | }()
20 |
21 | override func viewWillAppear() {
22 | super.viewWillAppear()
23 |
24 | if let fileURL = (windowController?.document as? PresentationDocument)?.fileURL {
25 | pathControl.url = fileURL
26 | }
27 | }
28 |
29 | @IBAction func showSlides(_ sender: AnyObject?) {
30 | windowController?.document?.addWindowController(slidesWindowController)
31 | slidesWindowController.showWindow(nil)
32 |
33 | windowController?.window?.orderOut(nil)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Truffaut/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/14/16.
6 | // Copyright © 2016 Codezerker. All rights reserved.
7 | //
8 |
9 | // MARK: - App Delegate
10 |
11 | import Cocoa
12 |
13 | @NSApplicationMain
14 | class AppDelegate: NSObject, NSApplicationDelegate {
15 |
16 | private var preferenceWindowController: PreferenceWindowController?
17 |
18 | func applicationDidFinishLaunching(_ notification: Notification) {
19 | openDocumentIfNeeded()
20 | }
21 |
22 | func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
23 | if flag {
24 | return true
25 | } else {
26 | openDocumentIfNeeded()
27 | return false
28 | }
29 | }
30 | }
31 |
32 | fileprivate extension AppDelegate {
33 |
34 | func openDocumentIfNeeded() {
35 | NSDocumentController.shared.openDocument(nil)
36 | }
37 | }
38 |
39 | fileprivate extension AppDelegate {
40 |
41 | @IBAction func showPreferenceWindow(_ sender: Any?) {
42 | preferenceWindowController = PreferenceWindowController.loadFromStoryboard()
43 | preferenceWindowController?.window?.makeKeyAndOrderFront(sender)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Truffaut/StoryboardLoading.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Definitions.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/15/16.
6 | // Copyright © 2016 Codezerker. All rights reserved.
7 | //
8 |
9 |
10 | // MARK: - Storyboard
11 |
12 | struct Storyboard {
13 |
14 | struct Names {
15 | static let main = "Main"
16 | }
17 |
18 | struct Identifiers {
19 | static let mainWindowController = "MainWindowController"
20 | static let slidesWindowController = "SlideWindowController"
21 | }
22 | }
23 |
24 | protocol StoryboardInstantiatable {
25 | associatedtype ControllerType
26 | static var storyboardName: String { get }
27 | static var storyboardIdentifier: String { get }
28 | static func loadFromStoryboard() -> ControllerType
29 | }
30 |
31 | import AppKit
32 |
33 | extension StoryboardInstantiatable {
34 |
35 | static var storyboardName: String {
36 | return Storyboard.Names.main
37 | }
38 |
39 | static var storyboardIdentifier: String {
40 | return "\(Self.self)"
41 | }
42 |
43 | static func loadFromStoryboard() -> ControllerType {
44 | let controller = NSStoryboard(name: storyboardName, bundle: nil).instantiateController(withIdentifier: storyboardIdentifier) as! ControllerType
45 | return controller
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2017 Codezerker Limited
2 |
3 | Redistribution and use in source and binary forms, with or without modification,
4 | are permitted provided that the following conditions are met:
5 |
6 | 1. Redistributions of source code must retain the above copyright notice, this
7 | list of conditions and the following disclaimer.
8 |
9 | 2. Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation and/or
11 | other materials provided with the distribution.
12 |
13 | 3. Neither the name of the copyright holder nor the names of its contributors
14 | may be used to endorse or promote products derived from this software without
15 | specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
21 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
--------------------------------------------------------------------------------
/Truffaut/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "Slice 116w.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "Slice 132w.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "Slice 132w-1.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "Slice 164w.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "Slice 1128w.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "Slice 1256w.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "Slice 1256w-1.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "Slice 1512w.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "Slice 1512w-1.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "Slice 11024w.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/Truffaut/PreferenceViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PreferenceViewController.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 29/12/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class PreferenceViewController: NSViewController {
12 |
13 | @IBOutlet private weak var swiftcPathTextField: NSTextField!
14 | @IBOutlet private weak var activeSwiftcPathLabel: NSTextField!
15 | @IBOutlet private weak var saveSwiftcPathButton: NSButton!
16 |
17 | @IBOutlet private weak var rubyPathTextField: NSTextField!
18 | @IBOutlet private weak var activeRubyPathLabel: NSTextField!
19 | @IBOutlet private weak var saveRubyPathButton: NSButton!
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 | updateViews()
24 | }
25 |
26 | private func updateViews() {
27 | activeSwiftcPathLabel.stringValue = Shell.Environment.swiftc
28 | activeRubyPathLabel.stringValue = Shell.Environment.ruby
29 |
30 | if let customSwiftcPath = UserPreference.customSwiftcPath {
31 | swiftcPathTextField.stringValue = customSwiftcPath
32 | }
33 | if let customRubyPath = UserPreference.customRubyPath {
34 | rubyPathTextField.stringValue = customRubyPath
35 | }
36 | }
37 |
38 | @IBAction private func saveCustomSwiftcPath(_ sender: Any?) {
39 | UserPreference.customSwiftcPath = swiftcPathTextField.stringValue
40 | updateViews()
41 | }
42 |
43 | @IBAction private func saveCustomRubyPath(_ sender: Any?) {
44 | UserPreference.customRubyPath = rubyPathTextField.stringValue
45 | updateViews()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Truffaut/UserPreference.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserPreference.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 29/12/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct UserPreference {
12 |
13 | struct UserDefaultKeys {
14 | static let customSwiftcPath = "com.codezerker.paths.swiftc"
15 | static let customRubyPath = "com.codezerker.paths.ruby"
16 | }
17 |
18 | static var customSwiftcPath: String? {
19 | set {
20 | guard let newValue = newValue, !newValue.isEmpty else {
21 | UserDefaults.standard.removeObject(forKey: UserDefaultKeys.customSwiftcPath)
22 | return
23 | }
24 | UserDefaults.standard.setValue(newValue, forKey: UserDefaultKeys.customSwiftcPath)
25 | }
26 | get {
27 | guard let customPath = UserDefaults.standard.string(forKey: UserDefaultKeys.customSwiftcPath),
28 | !customPath.isEmpty else {
29 | return nil
30 | }
31 | return customPath
32 | }
33 | }
34 |
35 | static var customRubyPath: String? {
36 | set {
37 | guard let newValue = newValue, !newValue.isEmpty else {
38 | UserDefaults.standard.removeObject(forKey: UserDefaultKeys.customRubyPath)
39 | return
40 | }
41 | UserDefaults.standard.setValue(newValue, forKey: UserDefaultKeys.customRubyPath)
42 | }
43 | get {
44 | guard let customPath = UserDefaults.standard.string(forKey: UserDefaultKeys.customRubyPath),
45 | !customPath.isEmpty else {
46 | return nil
47 | }
48 | return customPath
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Truffaut/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDocumentTypes
8 |
9 |
10 | CFBundleTypeExtensions
11 |
12 | swift
13 |
14 | CFBundleTypeName
15 | Swift
16 | CFBundleTypeRole
17 | Viewer
18 | LSTypeIsPackage
19 | 0
20 | NSDocumentClass
21 | $(PRODUCT_MODULE_NAME).PresentationDocument
22 |
23 |
24 | CFBundleExecutable
25 | $(EXECUTABLE_NAME)
26 | CFBundleIconFile
27 |
28 | CFBundleIdentifier
29 | $(PRODUCT_BUNDLE_IDENTIFIER)
30 | CFBundleInfoDictionaryVersion
31 | 6.0
32 | CFBundleName
33 | $(PRODUCT_NAME)
34 | CFBundlePackageType
35 | APPL
36 | CFBundleShortVersionString
37 | 0.7.1
38 | CFBundleSignature
39 | ????
40 | CFBundleVersion
41 | 204
42 | LSApplicationCategoryType
43 | public.app-category.developer-tools
44 | LSMinimumSystemVersion
45 | $(MACOSX_DEPLOYMENT_TARGET)
46 | NSHumanReadableCopyright
47 | Copyright © 2017 Codezerker. All rights reserved.
48 | NSMainStoryboardFile
49 | Main
50 | NSPrincipalClass
51 | NSApplication
52 | SUEnableAutomaticChecks
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/Truffaut/Shell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Reader.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/3/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public enum ShellRuntimeError: Error {
12 | case brokenPipe
13 | case processError(stderr: String?)
14 | }
15 |
16 | class Shell: NSObject {
17 |
18 | struct Environment {
19 |
20 | static var swiftc: String {
21 | return UserPreference.customSwiftcPath ?? "/usr/bin/swiftc"
22 | }
23 |
24 | static var ruby: String {
25 | return UserPreference.customRubyPath ?? "/usr/local/bin/ruby"
26 | }
27 | }
28 |
29 | public static func call(command: String, arguments: [String] = [], currentDirectoryPath: String? = nil) throws -> String {
30 | let process = Process()
31 | process.launchPath = command
32 | process.arguments = arguments
33 | if let currentDirectoryPath = currentDirectoryPath {
34 | process.currentDirectoryPath = currentDirectoryPath
35 | }
36 |
37 | let stdout = Pipe()
38 | let stderr = Pipe()
39 | process.standardOutput = stdout
40 | process.standardError = stderr
41 |
42 | try process.run()
43 | process.waitUntilExit()
44 |
45 | let stderrData = stderr.fileHandleForReading.readDataToEndOfFile()
46 | guard stderrData.count == 0 else {
47 | throw ShellRuntimeError.processError(stderr: String(data: stderrData, encoding: .utf8))
48 | }
49 |
50 | let stdoutData = stdout.fileHandleForReading.readDataToEndOfFile()
51 | guard let output = String(data: stdoutData, encoding: .utf8) else {
52 | throw ShellRuntimeError.brokenPipe
53 | }
54 |
55 | return output
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Truffaut
2 |
3 | A humble tool to help you presenting ideas by writing Swift
4 |
5 | 
6 |
7 | ## Requirements
8 |
9 | - **Xcode 10.2.1**, the embedded `swiftc` will be used to interpret the slides manifest file
10 | - Ruby gem [**rouge**](https://github.com/jneen/rouge), this gem will be used to provide syntax highlighting for source code blocks
11 |
12 | ## Usage
13 |
14 | ### Get Truffaut.app
15 |
16 | Clone this repo and build the target `Truffaut`.
17 |
18 | ### Create slides manifest
19 |
20 | Create a Swift file:
21 |
22 | ```sh
23 | $ touch slides.swift
24 | ```
25 |
26 | Import the supporting module:
27 |
28 | ```swift
29 | import TruffautSupport
30 | ```
31 |
32 | Initialize a presentation with pages:
33 |
34 | ```swift
35 | let presentation = Presentation(pages: [
36 | Page(title: "Hello World", subtitle: "A Swift Slide"),
37 | ])
38 | ```
39 |
40 | ## Manifest file reference
41 |
42 | For a full reference of the `TruffautSupport` supporting module, please check [here](Documentations/TruffautSupport-API-Reference.md).
43 |
44 | ## Examples
45 |
46 | For real-life Truffaut slides, please check the [Examples](Examples/).
47 |
48 | ## Caveats and Known Issues
49 |
50 | - The Xcode version selected by `xcode-select` must be the same as the one used to build the application.
51 | - Otherwise opening slides may fail with errors like this: `slides.swift:1:8: error: cannot load underlying module for 'TruffautSupport'`
52 | - In Swift 5.1, this issue is likely to be fixed by [Library Evolution](https://github.com/apple/swift-evolution/blob/master/proposals/0260-library-evolution.md).
53 | - Currently the syntax highlighter is assuming ruby is installed at path `/usr/local/bin/ruby` which is usually true if the ruby is intalled with [homebrew](https://brew.sh/).
54 | - If this is not true for you, you can modify the ruby path in the preference (`⌘ + ,`).
55 | - Export to PDF is not implemented.
56 |
--------------------------------------------------------------------------------
/Truffaut/ExportController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExportController.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 23/07/2016.
6 | // Copyright © 2016 Codezerker. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Quartz
11 |
12 | protocol ExportControllerDataSource {
13 |
14 | func numberOfPagesToExport() -> Int
15 | func viewForPageToExport(atIndex index: Int) -> NSView?
16 | }
17 |
18 | struct ExportController {
19 |
20 | enum ExportingType {
21 | case pdf
22 |
23 | var fileExtension: String {
24 | switch self {
25 | case .pdf:
26 | return "pdf"
27 | }
28 | }
29 | }
30 |
31 | func exportToPDF(withDataSource dataSource: ExportControllerDataSource) -> PDFDocument {
32 | let pdfDocument = PDFDocument()
33 | let numberOfPages = dataSource.numberOfPagesToExport()
34 | for i in (0.. PDFPage? {
53 | view.frame = NSRect(origin: .zero, size: Layout.defaultExportContentSize)
54 |
55 | // FIXME: in some cases the layout is incorrect,
56 | // we don't know the reason yet
57 | view.updateConstraintsForSubtreeIfNeeded()
58 | view.layoutSubtreeIfNeeded()
59 |
60 | let pdfData = view.dataWithPDF(inside: view.bounds)
61 | guard let pdfRep = NSPDFImageRep(data: pdfData) else {
62 | return nil
63 | }
64 |
65 | let pdfImage = NSImage(size: view.bounds.size)
66 | pdfImage.addRepresentation(pdfRep)
67 | return PDFPage(image: pdfImage)
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Truffaut/Font.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Font.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 17/08/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import AppKit
10 |
11 | struct DynamicLayout {
12 |
13 | private static let defaultScreenSize = NSSize(width: 800, height: 600)
14 | static var currentScreenSize = DynamicLayout.defaultScreenSize
15 |
16 | static func sizeFittingCurrentScreenSize(originalSize: CGFloat) -> CGFloat {
17 | return min(originalSize / DynamicLayout.defaultScreenSize.width * DynamicLayout.currentScreenSize.width,
18 | originalSize / DynamicLayout.defaultScreenSize.height * DynamicLayout.currentScreenSize.height)
19 | }
20 | }
21 |
22 | struct Font {
23 |
24 | struct Cover {
25 |
26 | static var title: NSFont {
27 | return NSFont.boldSystemFont(ofSize: DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 42))
28 | }
29 |
30 | static var subtitle: NSFont {
31 | return NSFont.systemFont(ofSize: DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 24))
32 | }
33 | }
34 |
35 | struct Page {
36 |
37 | static var title: NSFont {
38 | return NSFont.boldSystemFont(ofSize: DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 38))
39 | }
40 |
41 | static var text: NSFont {
42 | return NSFont.systemFont(ofSize: DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 24))
43 | }
44 |
45 | static var source: NSFont {
46 | guard let sfMono = NSFont(name: "SF Mono", size: DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 18)) else {
47 | return text
48 | }
49 | return sfMono
50 | }
51 | }
52 | }
53 |
54 | struct TextColor {
55 |
56 | struct Display {
57 | static let title = NSColor.white
58 | static let subtitle = NSColor.white
59 | static let text = NSColor.white
60 | static let source = NSColor.white
61 | }
62 |
63 | struct Export {
64 | static let title = NSColor.black
65 | static let subtitle = NSColor.black
66 | static let text = NSColor.black
67 | static let source = NSColor.black
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Truffaut/ReadController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReadController.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/14/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import TruffautSupport
10 |
11 | struct ReadController {
12 |
13 | enum ReadingError: Error {
14 | case unableToLoadSupportModule
15 | case malformedManifestData(output: String)
16 | }
17 |
18 | static func read(from url: URL, completion: @escaping (Presentation?, Error?) -> Void) {
19 | ManifestReading.queue.async {
20 | do {
21 | let cmd = Shell.Environment.swiftc
22 | let args = try ManifestReading.commandArguments(with: url)
23 | let output = try Shell.call(command: cmd, arguments: args)
24 | guard let outputData = output.data(using: .utf8) else {
25 | DispatchQueue.main.async {
26 | completion(nil, ReadingError.malformedManifestData(output: output))
27 | }
28 | return
29 | }
30 | let presentation = try JSONDecoder().decode(Presentation.self, from: outputData)
31 | DispatchQueue.main.async {
32 | completion(presentation, nil)
33 | }
34 | } catch {
35 | DispatchQueue.main.async {
36 | completion(nil, error)
37 | }
38 | }
39 | }
40 | }
41 | }
42 |
43 | import Foundation
44 |
45 | fileprivate extension ReadController {
46 |
47 | struct ManifestReading {
48 |
49 | static let queue = DispatchQueue(label: "com.codezerker.truffaut.manifestReading")
50 |
51 | private static let supportModuleName = "TruffautSupport"
52 | private static var searchPath: String? {
53 | return Bundle.main.privateFrameworksURL?.appendingPathComponent("\(supportModuleName).framework/Library", isDirectory: true).path
54 | }
55 |
56 | static func commandArguments(with url: URL) throws -> [String] {
57 | guard let searchPath = searchPath else {
58 | throw ReadingError.unableToLoadSupportModule
59 | }
60 | return [
61 | /* swiftc settings */
62 | "--driver-mode=swift",
63 | "-target", "x86_64-apple-macosx10.14.4",
64 |
65 | /* link options */
66 | "-I", searchPath,
67 | "-L", searchPath,
68 | "-l\(supportModuleName)",
69 |
70 | /* manifest file path */
71 | url.path
72 | ]
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/TruffautSupport/Content.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Content.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/3/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public enum Content {
12 |
13 | public enum FileType: String, Codable {
14 | case plainText
15 |
16 | case c
17 | case cpp
18 | case javaScript
19 | case objc
20 | case rust
21 | case shell
22 | case swift
23 | }
24 |
25 | case title(String)
26 | case text(String)
27 | case image(String)
28 | case sourceCode(FileType, String)
29 |
30 | indirect case indent([Content])
31 | }
32 |
33 | extension Content: Codable {
34 |
35 | private enum CodingKeys: String, CodingKey {
36 | case title
37 | case image
38 | case text
39 |
40 | case sourceCode
41 | case fileType
42 |
43 | case indent
44 | }
45 |
46 | public init(from decoder: Decoder) throws {
47 | let values = try decoder.container(keyedBy: CodingKeys.self)
48 | if let title = try values.decodeIfPresent(String.self, forKey: .title) {
49 | self = .title(title)
50 | } else if let imageFilePath = try values.decodeIfPresent(String.self, forKey: .image) {
51 | self = .image(imageFilePath)
52 | } else if let text = try values.decodeIfPresent(String.self, forKey: .text) {
53 | self = .text(text)
54 | } else if let sourceCode = try values.decodeIfPresent(String.self, forKey: .sourceCode),
55 | let fileType = try values.decodeIfPresent(FileType.self, forKey: .fileType) {
56 | self = .sourceCode(fileType, sourceCode)
57 | } else if let contents = try values.decodeIfPresent([Content].self, forKey: .indent) {
58 | self = .indent(contents)
59 | } else {
60 | let context = DecodingError.Context(codingPath: values.codingPath,
61 | debugDescription: "Unexpected content")
62 | throw DecodingError.dataCorrupted(context)
63 | }
64 | }
65 |
66 | public func encode(to encoder: Encoder) throws {
67 | var container = encoder.container(keyedBy: CodingKeys.self)
68 | switch self {
69 | case .title(let title):
70 | try container.encode(title, forKey: .title)
71 | case .image(let imageFilePath):
72 | try container.encode(imageFilePath, forKey: .image)
73 | case .text(let text):
74 | try container.encode(text, forKey: .text)
75 | case .sourceCode(let fileType, let sourceCode):
76 | try container.encode(fileType, forKey: .fileType)
77 | try container.encode(sourceCode, forKey: .sourceCode)
78 | case .indent(let contents):
79 | try container.encode(contents, forKey: .indent)
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/osx,swift,vim,xcode
3 |
4 | ### OSX ###
5 | *.DS_Store
6 | .AppleDouble
7 | .LSOverride
8 |
9 | # Icon must end with two \r
10 | Icon
11 |
12 |
13 | # Thumbnails
14 | ._*
15 |
16 | # Files that might appear in the root of a volume
17 | .DocumentRevisions-V100
18 | .fseventsd
19 | .Spotlight-V100
20 | .TemporaryItems
21 | .Trashes
22 | .VolumeIcon.icns
23 | .com.apple.timemachine.donotpresent
24 |
25 | # Directories potentially created on remote AFP share
26 | .AppleDB
27 | .AppleDesktop
28 | Network Trash Folder
29 | Temporary Items
30 | .apdisk
31 |
32 | ### Swift ###
33 | # Xcode
34 | #
35 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
36 |
37 | ## Build generated
38 | build/
39 | DerivedData/
40 |
41 | ## Various settings
42 | *.pbxuser
43 | !default.pbxuser
44 | *.mode1v3
45 | !default.mode1v3
46 | *.mode2v3
47 | !default.mode2v3
48 | *.perspectivev3
49 | !default.perspectivev3
50 | xcuserdata/
51 |
52 | ## Other
53 | *.moved-aside
54 | *.xccheckout
55 | *.xcscmblueprint
56 |
57 | ## Obj-C/Swift specific
58 | *.hmap
59 | *.ipa
60 | *.dSYM.zip
61 | *.dSYM
62 |
63 | ## Playgrounds
64 | timeline.xctimeline
65 | playground.xcworkspace
66 |
67 | # Swift Package Manager
68 | #
69 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
70 | # Packages/
71 | # Package.pins
72 | .build/
73 |
74 | # CocoaPods
75 | #
76 | # We recommend against adding the Pods directory to your .gitignore. However
77 | # you should judge for yourself, the pros and cons are mentioned at:
78 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
79 | #
80 | # Pods/
81 |
82 | # Carthage
83 | #
84 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
85 | Carthage/Checkouts
86 | Carthage/Build
87 |
88 | # fastlane
89 | #
90 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
91 | # screenshots whenever they are needed.
92 | # For more information about the recommended setup visit:
93 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
94 |
95 | fastlane/report.xml
96 | fastlane/Preview.html
97 | fastlane/screenshots
98 | fastlane/test_output
99 |
100 | ### Vim ###
101 | # swap
102 | [._]*.s[a-v][a-z]
103 | [._]*.sw[a-p]
104 | [._]s[a-v][a-z]
105 | [._]sw[a-p]
106 | # session
107 | Session.vim
108 | # temporary
109 | .netrwhist
110 | *~
111 | # auto-generated tag files
112 | tags
113 |
114 | ### Xcode ###
115 | # Xcode
116 | #
117 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
118 |
119 | ## Build generated
120 |
121 | ## Various settings
122 |
123 | ## Other
124 |
125 | ### Xcode Patch ###
126 | *.xcodeproj/*
127 | !*.xcodeproj/project.pbxproj
128 | !*.xcodeproj/xcshareddata/
129 | !*.xcworkspace/contents.xcworkspacedata
130 | /*.gcno
131 |
132 | # End of https://www.gitignore.io/api/osx,swift,vim,xcode
133 |
--------------------------------------------------------------------------------
/TruffautSupportTests/TruffautSupportTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TruffautSupportTests.swift
3 | // TruffautSupportTests
4 | //
5 | // Created by Yan Li on 4/3/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import TruffautSupport
11 |
12 | class TruffautSupportTests: XCTestCase {
13 |
14 | func testJSONRepresentation() {
15 | let sourceCode =
16 | """
17 | extension NSAttributedString: _CFBridgeable {
18 | var _cfObject: CFAttributedString {
19 | return unsafeBitCast(self, to: CFAttributedString.self)
20 | }
21 | }
22 | """
23 |
24 | let presentation = Presentation(
25 | title: "presentation title",
26 | authors: ["Yan Li ", "@eyeplum"],
27 | pages: [
28 | Page(title: "hello",
29 | subtitle: "say hello",
30 | contents: [
31 | .indent([
32 | .image("./image/url.png"),
33 | .text("hello"),
34 | .sourceCode(.swift, sourceCode),
35 | .sourceCode(.javaScript, sourceCode),
36 | ]),
37 | ]),
38 | ])
39 |
40 | guard let data = try? JSONEncoder().encode(presentation),
41 | let newPresentation = try? JSONDecoder().decode(Presentation.self, from: data) else {
42 | XCTAssert(false)
43 | return
44 | }
45 |
46 | XCTAssertEqual(presentation.title, newPresentation.title)
47 | XCTAssertEqual(presentation.authors, newPresentation.authors)
48 | XCTAssertEqual(presentation.pages.count, newPresentation.pages.count)
49 | XCTAssertEqual(presentation.pages[0].title, newPresentation.pages[0].title)
50 | XCTAssertEqual(presentation.pages[0].subtitle, newPresentation.pages[0].subtitle)
51 | XCTAssertEqual(presentation.pages[0].contents?.count, newPresentation.pages[0].contents?.count)
52 |
53 | guard let indent = newPresentation.pages[0].contents?.first,
54 | case let .indent(contents) = indent else {
55 | XCTAssert(false)
56 | return
57 | }
58 | XCTAssertEqual(contents.count, 4)
59 |
60 | guard case let .image(path) = contents[0] else {
61 | XCTAssert(false)
62 | return
63 | }
64 | XCTAssertEqual(path, "./image/url.png")
65 |
66 | guard case let .text(string) = contents[1] else {
67 | XCTAssert(false)
68 | return
69 | }
70 | XCTAssertEqual(string, "hello")
71 |
72 | guard case let .sourceCode(fileType, source) = contents[2] else {
73 | XCTAssert(false)
74 | return
75 | }
76 | XCTAssertEqual(fileType.rawValue, "swift")
77 | XCTAssertEqual(source, sourceCode)
78 |
79 | guard case let .sourceCode(jsFileType, _) = contents[3] else {
80 | XCTAssert(false)
81 | return
82 | }
83 | XCTAssertEqual(jsFileType.rawValue, "javaScript")
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Truffaut/SyntaxHighlighter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SyntaxHighlighter.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 29/08/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import AppKit
10 | import TruffautSupport
11 |
12 | struct SyntaxHighlighter {
13 |
14 | private static let syntaxHighlightingQueue = DispatchQueue(label: "com.codezerker.truffaut.syntaxHighlighting",
15 | attributes: .concurrent)
16 |
17 | static func highlight(sourceCode: String,
18 | ofType type: Content.FileType,
19 | withFont font: NSFont,
20 | completion: @escaping (NSAttributedString?) -> Void) {
21 | syntaxHighlightingQueue.async {
22 | guard let scriptPath = Bundle.main.path(forResource: "syntax-hl", ofType: "rb") else {
23 | DispatchQueue.main.async {
24 | completion(nil)
25 | }
26 | return
27 | }
28 | do {
29 | var output = try Shell.call(command: Shell.Environment.ruby, arguments: [
30 | scriptPath,
31 | sourceCode,
32 | type.rawValue,
33 | ])
34 |
35 | guard !output.isEmpty else {
36 | DispatchQueue.main.async {
37 | completion(nil)
38 | }
39 | return
40 | }
41 | output.removeLast() // remove '\n'
42 |
43 | let result = self.attributedString(from: output, font: font)
44 | DispatchQueue.main.async {
45 | completion(result)
46 | }
47 | } catch {
48 | DispatchQueue.main.async {
49 | completion(nil)
50 | }
51 | }
52 | }
53 | }
54 |
55 | private static func attributedString(from htmlString: String, font: NSFont) -> NSAttributedString? {
56 | let renderableString = htmlString.replacingOccurrences(of: "\n", with: "
")
57 | .replacingOccurrences(of: " ", with: " ")
58 | guard let htmlData = renderableString.data(using: .utf8),
59 | let attrString = NSMutableAttributedString(html: htmlData, options: [.characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else {
60 | return nil
61 | }
62 | let fullStringRange = NSRange(location: 0, length: attrString.length)
63 | attrString.addAttribute(.font, value: font, range: fullStringRange)
64 | attrString.enumerateAttribute(.foregroundColor, in: fullStringRange, options: []) { attr, range, _ in
65 | guard let foregroundColor = attr as? NSColor,
66 | foregroundColor.redComponent == 0,
67 | foregroundColor.greenComponent == 0,
68 | foregroundColor.blueComponent == 0,
69 | foregroundColor.alphaComponent == 1 else {
70 | return
71 | }
72 | attrString.addAttribute(.foregroundColor, value: TextColor.Display.source, range: range)
73 | }
74 | return attrString
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Truffaut/PresentationDocument.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Document.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/14/16.
6 | // Copyright © 2016 Codezerker. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import TruffautSupport
11 |
12 | class PresentationDocument: NSDocument {
13 |
14 | enum ParsingError: Error {
15 | case InvalidData
16 | }
17 |
18 | var fileNameWithoutExtension: String? {
19 | return fileURL?.deletingPathExtension().lastPathComponent
20 | }
21 |
22 | var presentation: Presentation?
23 |
24 | override class var autosavesInPlace: Bool {
25 | return true
26 | }
27 |
28 | override func makeWindowControllers() {
29 | let windowController = MainWindowController.loadFromStoryboard()
30 | addWindowController(windowController)
31 | }
32 |
33 | override func data(ofType typeName: String) throws -> Data {
34 | throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
35 | }
36 |
37 | override func read(from url: URL, ofType typeName: String) throws {
38 | reload {}
39 | }
40 |
41 | func reload(completion: @escaping () -> Void) {
42 | guard let url = fileURL else {
43 | completion()
44 | return
45 | }
46 |
47 | ReadController.read(from: url) { presentation, error in
48 | guard let presentation = presentation else {
49 | let alert: NSAlert
50 |
51 | if let error = error as? ShellRuntimeError {
52 | alert = NSAlert()
53 | alert.messageText = "Failed to load presentation."
54 | switch error {
55 | case .brokenPipe:
56 | alert.informativeText = "Broken pipe."
57 | case .processError(let stderr):
58 | alert.informativeText = stderr ?? "Unkown shell error happened when parsing the manifest file."
59 | }
60 | } else if let error = error {
61 | alert = NSAlert(error: error)
62 | } else {
63 | alert = NSAlert()
64 | alert.messageText = "Failed to load presentation (unexpected error)."
65 | }
66 |
67 | alert.alertStyle = .critical
68 | alert.showsHelp = true
69 | alert.delegate = self
70 |
71 | if let lastWindow = self.windowControllers.last?.window {
72 | alert.beginSheetModal(for: lastWindow, completionHandler: nil)
73 | } else {
74 | alert.runModal()
75 | }
76 |
77 | return
78 | }
79 |
80 | self.presentation = presentation
81 | completion()
82 | }
83 | }
84 | }
85 |
86 | extension PresentationDocument: NSAlertDelegate {
87 |
88 | static let documentationURLString = "https://github.com/Codezerker/Truffaut/blob/master/Documentations/TruffautSupport-API-Reference.md"
89 |
90 | public func alertShowHelp(_ alert: NSAlert) -> Bool {
91 | if let helpURL = URL(string: PresentationDocument.documentationURLString) {
92 | NSWorkspace.shared.open(helpURL)
93 | }
94 | return true
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Truffaut.xcodeproj/xcshareddata/xcschemes/Truffaut.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/Truffaut.xcodeproj/xcshareddata/xcschemes/TruffautSupport.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
54 |
55 |
65 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
84 |
90 |
91 |
92 |
93 |
95 |
96 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/Examples/attr-string-in-swift/slides.swift:
--------------------------------------------------------------------------------
1 | let cfAttrStrDef = """
2 | struct __CFAttributedString {
3 | CFRuntimeBase base;
4 | CFStringRef string;
5 | CFRunArrayRef attributeArray;
6 | };
7 | """
8 |
9 | let cfAttrStrClassDef = """
10 | static const CFRuntimeClass __CFAttributedStringClass = {
11 | 0,
12 | "CFAttributedString",
13 | NULL, // init
14 | NULL, // copy
15 | __CFAttributedStringDeallocate,
16 | __CFAttributedStringEqual, // <----- THIS!!
17 | __CFAttributedStringHash,
18 | NULL,
19 | __CFAttributedStringCopyDescription
20 | };
21 | """
22 |
23 | let copyIsRetain = """
24 | // NSAttributedString
25 |
26 | open override func copy() -> Any {
27 | return copy(with: nil)
28 | }
29 |
30 | open func copy(with zone: NSZone? = nil) -> Any {
31 | return self
32 | }
33 |
34 | // NSMutableAttributedString
35 |
36 | open override func copy(with zone: NSZone? = nil) -> Any {
37 | return NSAttributedString(attributedString: self)
38 | }
39 | """
40 |
41 | import TruffautSupport
42 |
43 | let presentation = Presentation(pages: [
44 |
45 | Page(title: "NSAttributedString.swift",
46 | subtitle: "A implementation story"),
47 |
48 | Page(title: "swift-corelibs-foundation", contents: [
49 | .text("A Swift re-implementation of Foundation.framework"),
50 | .text("For platforms without Objective-C runtime"),
51 | .text("Interface matching DarwinFoundation"),
52 | .text("Implementation unrelated"),
53 | ]),
54 |
55 | Page(title: "CoreFoundation", contents: [
56 | .text("C library, Foundation's \"core\""),
57 | .text("Provides common data types for all frameworks (Darwin)"),
58 | .text("Toll-free bridging"),
59 | .indent([
60 | .text("Bridgable: NSString is-a CFStringRef"),
61 | .text("Non-bridgable: NSRunLoop has-a CFRunLoop"),
62 | ]),
63 | ]),
64 |
65 | Page(title: "CFAttributedString.c", contents: [
66 | .text("CFAttributedString and CFMutableAttributedString are the same"),
67 | .sourceCode(.c, cfAttrStrDef),
68 | ]),
69 |
70 | Page(title: "_CFInfo", contents: [
71 | .text("Essentially 64 bit of in-memory data"),
72 | .text("Type meta data of all CFTypes"),
73 | .indent([
74 | .text("Is this CFTypeRef a __CFAttributedString?"),
75 | .text("Is this __CFAttributedString mutable?"),
76 | ]),
77 | ]),
78 |
79 | Page(title: "class NSAttributedString {}", contents: [
80 | .text("It's a string"),
81 | .indent([
82 | .sourceCode(.swift, "var _string: NSString"),
83 | ]),
84 | .text("It has attributes"),
85 | .indent([
86 | .sourceCode(.swift, "var _attributes: CFRunArray"),
87 | .text("The \"Run\" is the same as in \"CTRunRef\""),
88 | ]),
89 | .text("It is-a CFAttributedStringRef"),
90 | .indent([
91 | .sourceCode(.swift, "let _cfinfo: _CFInfo"),
92 | ]),
93 | ]),
94 |
95 | Page(title: "init(...)", contents: [
96 | .sourceCode(.swift, "self = CFAttributedStringCreate(...)"),
97 | .sourceCode(.plainText, "~~~~ ^ error: cannot assign to value: 'self' is immutable"),
98 | .text("We need to re-implement CFAttributedStringCreate(...) in Swift!"),
99 | ]),
100 |
101 | Page(title: "func attribute(...)", contents: [
102 | .sourceCode(.swift, "typealias NSRangePointer = UnsafeMutablePointer"),
103 | .text("Time for a pointer dance!"),
104 | ]),
105 |
106 | Page(title: "func enumerateAttributes(...)", contents: [
107 | .text("A peek of DarwinFoundation"),
108 | .text("So excited!!"),
109 | .text("Remember the implementation!"),
110 | .text("Annnnnnd, the code is messy :troll:"),
111 | ]),
112 |
113 | Page(title: "class NSMutableAttributedString: NSAttributedString {}", contents: [
114 | .sourceCode(.swift, "var mutableString: NSMutableString"),
115 | .text("It is-a CFMutableAttributedString"),
116 | ]),
117 |
118 | Page(title: "func isEqual(to:)", contents: [
119 | .sourceCode(.c, cfAttrStrClassDef),
120 | ]),
121 |
122 | Page(title: "NSCopying", contents: [
123 | .text("You say copy, I say retain."),
124 | .sourceCode(.swift, copyIsRetain),
125 | ]),
126 |
127 | Page(title: "What's NeXT", contents: [
128 | .text("Make some swift packages to use NS[Mutable]AttributedString"),
129 | .sourceCode(.swift, "let string = \"hello\" as NSString"),
130 | .text("5 non-trivial pull requests -> commit access!")
131 | ]),
132 |
133 | Page(title: "NSAttributedString(string: \"Thanks!\")"),
134 | ])
135 |
--------------------------------------------------------------------------------
/Truffaut/SlidesViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SlidesViewController.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 4/15/16.
6 | // Copyright © 2016 Codezerker. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import TruffautSupport
11 |
12 | class SlidesViewController: NSViewController {
13 |
14 | weak var windowController: NSWindowController?
15 |
16 | fileprivate var currentPage = 0
17 | fileprivate weak var currentPageViewController: PageViewController?
18 |
19 | fileprivate var presentation: Presentation? {
20 | return (windowController?.document as? PresentationDocument)?.presentation
21 | }
22 |
23 | fileprivate var fileName: String? {
24 | return (windowController?.document as? PresentationDocument)?.fileNameWithoutExtension
25 | }
26 |
27 | fileprivate var documentFolderURL: URL? {
28 | guard let document = windowController?.document,
29 | let documentURL = document.fileURL,
30 | let folderURL = documentURL?.deletingLastPathComponent() else {
31 | return nil
32 | }
33 | return folderURL
34 | }
35 |
36 | override func viewWillAppear() {
37 | super.viewWillAppear()
38 | reload(nil)
39 | }
40 | }
41 |
42 | extension SlidesViewController {
43 |
44 | @IBAction func showPreviousPage(_ sender: Any?) {
45 | show(pageAtIndex: currentPage - 1)
46 | }
47 |
48 | @IBAction func showNextPage(_ sender: Any?) {
49 | show(pageAtIndex: currentPage + 1)
50 | }
51 |
52 | @IBAction func reload(_ sender: Any?) {
53 | (windowController?.document as? PresentationDocument)?.reload { [weak self] in
54 | guard let validSelf = self else {
55 | return
56 | }
57 | validSelf.show(pageAtIndex: validSelf.currentPage)
58 | }
59 | }
60 |
61 | // FIXME:
62 | // because the document view for the page view is flipped, the PDF image is messed up.
63 | // Disable export for now.
64 | /*
65 | @IBAction func exportPresentationToPDF(_ sender: Any?) {
66 | guard let fileName = self.fileName else {
67 | return
68 | }
69 | let openPanel = NSSavePanel()
70 | openPanel.allowedFileTypes = [ExportController.ExportingType.pdf.fileExtension]
71 | openPanel.allowsOtherFileTypes = false
72 | openPanel.nameFieldStringValue = fileName
73 | openPanel.begin { result in
74 | guard result == NSApplication.ModalResponse.OK,
75 | let exportURL = openPanel.url else {
76 | return
77 | }
78 | let pdf = ExportController().exportToPDF(withDataSource: self)
79 | pdf.write(to: exportURL)
80 | }
81 | }
82 | */
83 | }
84 |
85 | extension SlidesViewController: ExportControllerDataSource {
86 |
87 | func numberOfPagesToExport() -> Int {
88 | guard let pages = presentation?.pages else {
89 | return 0
90 | }
91 | return pages.count
92 | }
93 |
94 | private struct ExportLayout {
95 | static let width: CGFloat = 1280
96 | static let height: CGFloat = 720
97 | }
98 |
99 | func viewForPageToExport(atIndex index: Int) -> NSView? {
100 | guard let page = presentation?.pages[index],
101 | let documentFolderURL = documentFolderURL else {
102 | return nil
103 | }
104 | let pageViewController = PageViewController(page: page, imageBaseURL: documentFolderURL, isExporting: true)
105 | return pageViewController.view
106 | }
107 | }
108 |
109 | extension SlidesViewController {
110 |
111 | fileprivate func show(pageAtIndex index: Int) {
112 | guard let pages = presentation?.pages,
113 | let documentFolderURL = documentFolderURL,
114 | index >= 0 && index < pages.count else {
115 | return
116 | }
117 |
118 | currentPage = index
119 |
120 | let pageViewController = PageViewController(page: pages[index], imageBaseURL: documentFolderURL)
121 | pageViewController.view.translatesAutoresizingMaskIntoConstraints = false
122 | view.addSubview(pageViewController.view)
123 | addChild(pageViewController)
124 |
125 | NSLayoutConstraint.activate([
126 | pageViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
127 | pageViewController.view.leftAnchor.constraint(equalTo: view.leftAnchor),
128 | pageViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
129 | pageViewController.view.rightAnchor.constraint(equalTo: view.rightAnchor),
130 | ])
131 |
132 | self.currentPageViewController?.removeFromParent()
133 | self.currentPageViewController?.view.removeFromSuperview()
134 |
135 | self.currentPage = index
136 | self.currentPageViewController = pageViewController
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/Truffaut/PageViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/Documentations/TruffautSupport-API-Reference.md:
--------------------------------------------------------------------------------
1 | # TruffautSupport
2 |
3 | Describe your presentation slides with Swift.
4 |
5 | ## Overview
6 |
7 | `TruffautSupport` contains data types for describing presentation slides.
8 |
9 | It provides the vocabulary for the manifest file.
10 |
11 | ## Data Types
12 |
13 | ### Presentation
14 |
15 | [View source](../TruffautSupport/Presentation.swift)
16 |
17 | The top-level data type that holds all necessary information about the slides.
18 |
19 | It is also a crucial part of the manifest file interpreting process, as its designated initializer is responsible for printing out its JSON representation so that the manifest can be passed from the `swiftc` interpreter to `Truffaut.app`.
20 |
21 | #### title
22 |
23 | - Reserved for future use.
24 |
25 | #### authors
26 |
27 | - Reserved for future use.
28 |
29 | #### pages
30 |
31 | - An array of `Page`s that needs to be presented in the slides.
32 |
33 | #### init(title: String = "", authors: [String] = [], pages: [Page])
34 |
35 | - Designated initializer for `Presentation`.
36 | - `title`: optional, reserved for future use, default value is an empty string.
37 | - `authors`: optional, reserved for future use, default value is an empty array of strings.
38 | - `pages`: required, an array of `Page`s that needs to be presented in the slides.
39 |
40 | ### Page
41 |
42 | [View source](../TruffautSupport/Page.swift)
43 |
44 | A `Page` describes what should be displayed in a single slide.
45 |
46 | Currently there are two types of pages: **cover** and **normal**.
47 |
48 | - A **cover** page is a page that only displayes its `title` and `subtitle`, and all the contents are vertically and horizontally centered.
49 | - A **normal** page is a page that can display a `title` and a tree of `contents`. The title is aligned to the top-left corner.
50 |
51 | The detection of page types is implicit: **if `contents` is `nil`** the page will be recognized as a **cover**, otherwise it will be recognized as a `normal` page.
52 |
53 | #### title
54 |
55 | - The title of the page.
56 |
57 | #### subtitle
58 |
59 | - Subtitle of the page.
60 | - This value is ignored if the page is a **normal** page.
61 |
62 | #### contents
63 |
64 | - An array of `Content` to be displayed in the page.
65 | - If this value is `nil`, the page will be recognized as a **cover**, otherwise it will be recognized as a **normal** page.
66 |
67 | #### init(title: String? = nil, subtitle: String? = nil, contents: [Content]? = nil)
68 |
69 | - Designated initializer for `Page`.
70 | - `title`: optional, the title of the page, default value is `nil`.
71 | - `subtitle`: optional, the subtitle of the page, default value is `nil`.
72 | - `contents`: optional, an array of `Content`, default value is `nil`.
73 |
74 | ### Content
75 |
76 | [View source](../TruffautSupport/Content.swift)
77 |
78 | A `Content` describes an entry in the slide.
79 |
80 | It can be a text (`.text`), a monospaced text with optional syntax highlighting (`.sourceCode`), or an image (`.image`).
81 |
82 | To make it more flexible to layout the entries, it is also possible to wrap contents with indentations (`.indent`). All contents can be wrapped in a `.indent`, including the `.indent` itself.
83 |
84 | #### Content.title(String)
85 |
86 | - Reserved for future use.
87 |
88 | #### Content.text(String)
89 |
90 | - A text entry rendered with system font.
91 | - The associated `String` will be used to render the text entry.
92 |
93 | #### Content.image(String)
94 |
95 | - A image entry.
96 | - The associated `String` will be used as the image's relative file path based on the manifest file's path.
97 |
98 | #### Content.sourceCode(FileType, String)
99 |
100 | - A text entry rendered with monospaced font.
101 | - The first associated `FileType` value will be used to determine the syntax highlighting type.
102 | - The second associated `String` will be used to render the text entry.
103 |
104 | #### Content.indent([Content])
105 |
106 | - An indent entry to make its content visually indented by one level.
107 | - The associated `Content` array will be indented.
108 |
109 | #### Content.FileType
110 |
111 | A `Content.FileType` will be mapped to [**rouge**](https://github.com/jneen/rouge)'s syntax highlighting lexers.
112 |
113 | ##### FileType.plainText
114 |
115 | - The content is plain text, no lexer will be applied (no syntax highlighting).
116 |
117 | ##### FileType.c
118 |
119 | - The content is C source code, `Rouge::Lexers::C` will be applied.
120 |
121 | ##### FileType.cpp
122 |
123 | - The content is C++ source code, `Rouge::Lexers::Cpp` will be applied.
124 |
125 | ##### FileType.javaScript
126 |
127 | - The content is JavaScript source code, `Rouge::Lexers::Javascript` will be applied.
128 |
129 | ##### FileType.objc
130 |
131 | - The content is Objective-C source code, `Rouge::Lexers::ObjectiveC` will be applied.
132 |
133 | ##### FileType.rust
134 |
135 | - The content is Rust source code, `Rouge::Lexers::Rust` will be applied.
136 |
137 | ##### FileType.shell
138 |
139 | - The content is shell script, `Rouge::Lexers::Shell` will be applied.
140 |
141 | ##### FileType.swift
142 |
143 | - The content is Swift source code, `Rouge::Lexers::Swift` will be applied.
144 |
--------------------------------------------------------------------------------
/Truffaut/PageViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PageViewController.swift
3 | // Truffaut
4 | //
5 | // Created by Yan Li on 17/08/17.
6 | // Copyright © 2017 Codezerker. All rights reserved.
7 | //
8 |
9 | import AppKit
10 | import TruffautSupport
11 |
12 | class PageViewController: NSViewController {
13 |
14 | @IBOutlet private weak var visualEffectView: NSVisualEffectView!
15 | @IBOutlet fileprivate weak var scrollView: NSScrollView!
16 |
17 | let page: Page
18 | let imageBaseURL: URL
19 | let isExporting: Bool
20 |
21 | init(page: Page, imageBaseURL: URL, isExporting: Bool = false) {
22 | self.page = page
23 | self.imageBaseURL = imageBaseURL
24 | self.isExporting = isExporting
25 | super.init(nibName: nil, bundle: nil)
26 | }
27 |
28 | required init?(coder: NSCoder) {
29 | fatalError("init(coder:) has not been implemented")
30 | }
31 |
32 | override func viewWillLayout() {
33 | super.viewWillLayout()
34 | DynamicLayout.currentScreenSize = view.bounds.size
35 | setUpViews()
36 | }
37 | }
38 |
39 | fileprivate class FlippedView: NSView {
40 |
41 | override var isFlipped: Bool {
42 | return true
43 | }
44 | }
45 |
46 | fileprivate extension PageViewController {
47 |
48 | private struct LayoutConstants {
49 |
50 | static var pageMargin: CGFloat {
51 | return DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 42)
52 | }
53 |
54 | static var spacing: CGFloat {
55 | return DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 16)
56 | }
57 |
58 | static var indentOffset: CGFloat {
59 | return DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 32)
60 | }
61 | }
62 |
63 | private func setUpViews() {
64 | scrollView.documentView = nil
65 |
66 | let contentStackView = NSStackView(frame: .zero)
67 | contentStackView.translatesAutoresizingMaskIntoConstraints = false
68 | contentStackView.orientation = .vertical
69 | contentStackView.spacing = LayoutConstants.spacing
70 | contentStackView.edgeInsets = NSEdgeInsets(top: LayoutConstants.pageMargin,
71 | left: LayoutConstants.pageMargin,
72 | bottom: LayoutConstants.pageMargin,
73 | right: LayoutConstants.pageMargin)
74 |
75 | if page.contents != nil {
76 | layoutPage(in: contentStackView)
77 | } else {
78 | layoutPageAsCover(in: contentStackView)
79 | }
80 |
81 | let documentView = FlippedView(frame: view.bounds)
82 | documentView.translatesAutoresizingMaskIntoConstraints = false
83 | documentView.addSubview(contentStackView)
84 | NSLayoutConstraint.activate([
85 | documentView.widthAnchor.constraint(greaterThanOrEqualToConstant: view.bounds.width),
86 | documentView.heightAnchor.constraint(greaterThanOrEqualToConstant: view.bounds.height),
87 |
88 | contentStackView.topAnchor.constraint(equalTo: documentView.topAnchor),
89 | contentStackView.leftAnchor.constraint(equalTo: documentView.leftAnchor),
90 | contentStackView.bottomAnchor.constraint(equalTo: documentView.bottomAnchor),
91 | contentStackView.rightAnchor.constraint(equalTo: documentView.rightAnchor),
92 | ])
93 |
94 | scrollView.documentView = documentView
95 | }
96 |
97 | private func layoutPageAsCover(in contentStackView: NSStackView) {
98 | contentStackView.alignment = .centerX
99 | contentStackView.distribution = .gravityAreas
100 |
101 | let titleLabel = NSTextField(wrappingLabelWithString: page.title ?? "")
102 | titleLabel.translatesAutoresizingMaskIntoConstraints = false
103 | titleLabel.font = Font.Cover.title
104 | titleLabel.textColor = isExporting ? TextColor.Export.title : TextColor.Display.title
105 | contentStackView.addView(titleLabel, in: .center)
106 |
107 | let subtitleLabel = NSTextField(wrappingLabelWithString: page.subtitle ?? "")
108 | subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
109 | subtitleLabel.font = Font.Cover.subtitle
110 | subtitleLabel.textColor = isExporting ? TextColor.Export.title : TextColor.Display.subtitle
111 | contentStackView.addView(subtitleLabel, in: .center)
112 | }
113 |
114 | private func layoutPage(in contentStackView: NSStackView) {
115 | guard let contents = page.contents else {
116 | return
117 | }
118 |
119 | let pageGravity: NSStackView.Gravity
120 | if let title = page.title {
121 | contentStackView.alignment = .leading
122 | contentStackView.distribution = .gravityAreas
123 |
124 | pageGravity = .top
125 |
126 | let titleLabel = NSTextField(wrappingLabelWithString: page.title ?? "")
127 | titleLabel.translatesAutoresizingMaskIntoConstraints = false
128 | titleLabel.font = Font.Page.title
129 | titleLabel.textColor = isExporting ? TextColor.Export.title : TextColor.Display.title
130 | contentStackView.addView(titleLabel, in: pageGravity)
131 | } else {
132 | contentStackView.alignment = .centerX
133 | contentStackView.distribution = .gravityAreas
134 |
135 | pageGravity = .center
136 | }
137 |
138 | // FIXME: layout subtitle
139 |
140 | func addContent(content: Content, to stackView: NSStackView, isLast: Bool) {
141 | switch content {
142 | case .indent(let nestedContents):
143 | let indentStackView = NSStackView(views: [])
144 | indentStackView.translatesAutoresizingMaskIntoConstraints = false
145 | indentStackView.orientation = .vertical
146 | indentStackView.alignment = .leading
147 | indentStackView.distribution = .gravityAreas
148 | indentStackView.edgeInsets = NSEdgeInsets(top: 0, left: LayoutConstants.indentOffset, bottom: 0, right: 0)
149 | for nestedContent in nestedContents {
150 | addContent(content: nestedContent, to: indentStackView, isLast: false)
151 | }
152 | stackView.addView(indentStackView, in: pageGravity)
153 | case .text(let text):
154 | let displayText = text.replacingOccurrences(of: "->", with: " ➞ ")
155 | let label = NSTextField(wrappingLabelWithString: displayText)
156 | label.translatesAutoresizingMaskIntoConstraints = false
157 | label.font = Font.Page.text
158 | label.textColor = isExporting ? TextColor.Export.text : TextColor.Display.text
159 | stackView.addView(label, in: pageGravity)
160 | case .sourceCode(let fileType, let source):
161 | let label: NSTextField
162 | switch fileType {
163 | case .plainText:
164 | label = NSTextField(labelWithString: source)
165 | default:
166 | label = NSTextField(labelWithAttributedString: NSAttributedString(string: source))
167 | SyntaxHighlighter.highlight(sourceCode: source,
168 | ofType: fileType,
169 | withFont: Font.Page.source) { result in
170 | guard let result = result else {
171 | return
172 | }
173 | label.attributedStringValue = result
174 | }
175 | }
176 | label.translatesAutoresizingMaskIntoConstraints = false
177 | label.font = Font.Page.source
178 | label.textColor = isExporting ? TextColor.Export.source : TextColor.Display.source
179 | stackView.addView(label, in: pageGravity)
180 | case .image(let relativePath):
181 | let imageURL = imageBaseURL.appendingPathComponent(relativePath, isDirectory: false)
182 | guard let image = NSImage(contentsOf: imageURL) else {
183 | break
184 | }
185 | let imageView = NSImageView(image: image)
186 | imageView.translatesAutoresizingMaskIntoConstraints = false
187 | imageView.imageScaling = .scaleProportionallyUpOrDown
188 | stackView.addView(imageView, in: pageGravity)
189 | default:
190 | break
191 | }
192 |
193 | if isLast {
194 | // workaround: insert a vertically growable dummy view
195 | // this will make sure there is no ambiguity in vertical layout
196 | let bottomView = NSView()
197 | bottomView.translatesAutoresizingMaskIntoConstraints = false
198 | bottomView.setContentHuggingPriority(.defaultLow, for: .vertical)
199 | stackView.addView(bottomView, in: .bottom)
200 | }
201 | }
202 |
203 | for (index, content) in contents.enumerated() {
204 | addContent(content: content, to: contentStackView, isLast: index + 1 == contents.count)
205 | }
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/Examples/unicode/slides.swift:
--------------------------------------------------------------------------------
1 | import TruffautSupport
2 |
3 | let presentation = Presentation(pages: [
4 |
5 | // intro
6 |
7 | Page(title: "Unicode and You", subtitle: "try? explain(Unicode.self)"),
8 |
9 | Page(title: "Why does this function throw?", contents: [
10 | .image("images/the-unicode-standard.png"),
11 | .text("1,044 pages"),
12 | ]),
13 |
14 | Page(title: "Topics", contents: [
15 | .text("The Unicode Standard"),
16 | .text("Cocoa and Unicode"),
17 | .text("Books and Apps"),
18 | ]),
19 |
20 | // Unicode Standard
21 |
22 | Page(title: "The Unicode Standard", subtitle: "A Whirlwind Tour"),
23 |
24 | Page(title: "What is Unicode?", contents: [
25 | .text("A way to represent characters"),
26 | .text("Multilingual"),
27 | .text("Character composition"),
28 | .indent([
29 | .sourceCode(.plainText, "e(U+0065) + ´(U+00B4) = é(U+00E9)"),
30 | ]),
31 | .text("A super set of ASCII"),
32 | ]),
33 |
34 | Page(title: "Character number and Character name", contents: [
35 | .text("Every character in Unicode (called a code point) has two unique identifiers:"),
36 | .indent([
37 | .text("Character number"),
38 | .text("Character name"),
39 | ]),
40 | .text("Example:"),
41 | .indent([
42 | .sourceCode(.plainText, " Character : é"),
43 | .sourceCode(.plainText, "Character number : U+00E9 (233 in decimal)"),
44 | .sourceCode(.plainText, " Character name : LATIN SMALL LETTER E WITH ACUTE"),
45 | ]),
46 | ]),
47 |
48 | Page(title: "Ranges of Character number", contents: [
49 | .text("Coding space"),
50 | .indent([
51 | .sourceCode(.plainText, "U+0000..U+10FFFF"),
52 | .text("1,114,111 in total"),
53 | ]),
54 | .text("Coding space is divided into 17 planes, each has 0xFFFF(65535) of space"),
55 | .indent([
56 | .sourceCode(.plainText, "U+0000..U+FFFF"),
57 | .text("Basic Multilingual Plane (BMP): most used characters"),
58 | .text(""),
59 | .sourceCode(.plainText, "U+10000..U+1FFFF"),
60 | .text("Supplementary Multilingual Plane (SMP): special symbols"),
61 | .text(""),
62 | .sourceCode(.plainText, "U+20000..U+2FFFF"),
63 | .text("Supplementary Ideographic Plane (SIP): less used CJK characters"),
64 | .text("CJK: Chinese-Japanese-Korean"),
65 | .text(""),
66 | .sourceCode(.plainText, "U+30000..U+DFFFF"),
67 | .text("Unassigned, reserved for future use"),
68 | .text(""),
69 | .sourceCode(.plainText, "U+E0000..U+EFFFF"),
70 | .text("Supplementary Special-Purpose Plane (SSPP): reserved for internal use"),
71 | .text(""),
72 | .sourceCode(.plainText, "U+F0000..U+10FFFF"),
73 | .text("Designed for custom private use"),
74 | .text(""),
75 | ]),
76 | ]),
77 |
78 | Page(title: "UnicodeData.txt", contents: [
79 | .text("The Unicode database basic file"),
80 | .text("Example:"),
81 | .indent([
82 | .sourceCode(.plainText, "00F6;LATIN SMALL LETTER O WITH DIAERESIS;Ll;0;L;006F 0308;...;00D6;;00D6"),
83 | .sourceCode(.plainText, " 00F6 - Character number"),
84 | .sourceCode(.plainText, "LATIN...DIAERESIS - Character name"),
85 | .sourceCode(.plainText, " Ll - Letter, lowercase"),
86 | .sourceCode(.plainText, " 006F 0308 - Canonical decomposition to o(U+006F) + ̈(U+0308)"),
87 | .sourceCode(.plainText, " 00D6 - Simple case mapping, uppercase and title case both map to Ö(U+00D6)"),
88 | ]),
89 | ]),
90 |
91 | Page(title: "Word boundaries and Line-breaks", contents: [
92 | .text("Also defined in the Unicode database, e.g.:"),
93 | .indent([
94 | .sourceCode(.plainText, "GraphemeBreakProperty.txt"),
95 | .sourceCode(.plainText, "WordBreakProperty.txt"),
96 | .sourceCode(.plainText, "SentenceBreakProperty.txt"),
97 | .sourceCode(.plainText, "LineBreak.txt"),
98 | ]),
99 | ]),
100 |
101 | Page(title: "Grapheme cluster", contents: [
102 | .text("Multiple Unicode code points act as one character"),
103 | .text("Example:"),
104 | .indent([
105 | .sourceCode(.plainText, "é = e´"),
106 | .sourceCode(.plainText, "Uppercase ß => SS"),
107 | .sourceCode(.plainText, "🇳🇿 => 🇳 🇿"),
108 | .indent([
109 | .sourceCode(.plainText, "U+1F1F3 REGIONAL INDICATOR SYMBOL LETTER N"),
110 | .sourceCode(.plainText, "U+1F1FF REGIONAL INDICATOR SYMBOL LETTER Z"),
111 | ]),
112 | .sourceCode(.plainText, "👨👩👧👦 => 👨U+200D👩U+200D👧U+200D👦"),
113 | .indent([
114 | .sourceCode(.plainText, "U+200D ZERO WIDTH JOINER"),
115 | ]),
116 | ]),
117 | ]),
118 |
119 | Page(title: "Unicode encodings: UTF-32", contents: [
120 | .text("32-bit -> 4 bytes"),
121 | .text("Unicode number is 21-bit"),
122 | .indent([
123 | .text("First 7-bit is plane number"),
124 | .text("The following 16-bit is the location in the plane"),
125 | ]),
126 | .text("32-bit fits"),
127 | .indent([
128 | .text("+ Fast encoding and decoding (direct mapping)"),
129 | .text("+ Random access is O(1)"),
130 | .text("+ Robust"),
131 | .text("- Waste of space"),
132 | ]),
133 | ]),
134 |
135 | Page(title: "Unicode encodings: UTF-16", contents: [
136 | .text("BMP (U+0000..U+FFFF): 16-bit -> 2 bytes"),
137 | .text("Outside BMP (U+1FFFF..U+10FFFF): a pair of 16-bit -> 2 * 2 bytes"),
138 | .text("Surrogate pair:"),
139 | .indent([
140 | .sourceCode(.plainText, "𝐅 U+1D405 MATHEMATICAL BOLD CAPITAL F"),
141 | .sourceCode(.plainText, "000011101010000000101 // U+1D405 in binary"),
142 | .sourceCode(.plainText, "u1 = 00001 | u2 = 110101 | u3 = 0000000101"),
143 | .sourceCode(.plainText, "u1 -= 1"),
144 | .sourceCode(.plainText, "110110u1u2 = 1101100000110101 // U+D835, high surrogate"),
145 | .sourceCode(.plainText, " 110111u3 = 1101110000000101 // U+DC05, low surrogate"),
146 | ]),
147 | .text("+ BMP encoding and decoding is fast (direct mapping)"),
148 | .text("+ Robust, high surrogate must always be followed by low surrogate"),
149 | .text("- Random access is O(n)"),
150 | ]),
151 |
152 | Page(title: "Unicode encodings: UTF-8", contents: [
153 | .text("Basic Latin (ASCII) (U+0000..U+007F): 8-bit -> 1 byte"),
154 | .text("Outside ASCII: up to 32-bit -> up to 4 bytes"),
155 | .indent([
156 | .sourceCode(.plainText, " Code number in binary | Octec 1 Octec 2 Octec 3 Octec 4"),
157 | .sourceCode(.plainText, " 00000000 0xxxxxxx | 0xxxxxxx"),
158 | .sourceCode(.plainText, " 00000yyy yyxxxxxx | 110yyyyy 10xxxxxx"),
159 | .sourceCode(.plainText, " zzzzyyyy yyxxxxxx | 1110zzzz 10yyyyyy 10xxxxxx"),
160 | .sourceCode(.plainText, "uuuww zzzzyyyy yyxxxxxx | 11110uuu 10wwzzzz 10yyyyyy 10xxxxxx"),
161 | ]),
162 | .text("+ ASCII encoding and decoding is fast (direct mapping) and efficient (1 byte)"),
163 | .text("+ Robust, bit pattern enforces completeness"),
164 | .text("- Random access is O(n)"),
165 | ]),
166 |
167 | Page(title: "URL Encoding, Base64 and Punycode", contents: [
168 | .text("Emerge because of the World Wide Web"),
169 | .text("Punycode"),
170 | .indent([
171 | .sourceCode(.plainText, "http://👴🏻👀.ws/ -> http://xn--mn8hkeyf.ws/"),
172 | ]),
173 | .text("RFCs"),
174 | .indent([
175 | .text("URI: RFC 3986"),
176 | .text("Base64: RFC 3548"),
177 | .text("Punycode: RFC 3492"),
178 | ]),
179 | ]),
180 |
181 | // Cocoa and Unicode
182 |
183 | Page(title: "Cocoa and Unicode", subtitle: "CFStringRef -> NSString -> Swift.String"),
184 |
185 | Page(title: "CFStringRef and NSString", contents: [
186 | .text("String manipulation APIs are based on UTF-16"),
187 | .text("Not fully compliant to Unicode"),
188 | .text("Use with care!!"),
189 | .indent([
190 | .sourceCode(.plainText, "import Foundation"),
191 | .sourceCode(.plainText, "let string = \"👴🏻\" as NSString"),
192 | .sourceCode(.plainText, "string.length // => 4"),
193 | ]),
194 | .text("Cocoa was initially implemented in the late 1990s, UTF-16 was popular by then!"),
195 | ]),
196 |
197 | Page(title: "Swift.String", contents: [
198 | .text("Fully compliant to Unicode"),
199 | .text("Provide APIs to manipulate Unicode scalars"),
200 | .indent([
201 | .sourceCode(.plainText, "let string = \"👴🏻\""),
202 | .sourceCode(.plainText, "string.count // => 1"),
203 | .sourceCode(.plainText, "string.unicodeScalars // => \u{0001F474} \u{0001F3FB}"),
204 | .sourceCode(.plainText, "// 👴 U+1F474 OLDER MAN"),
205 | .sourceCode(.plainText, "// 🏻 U+1F3FB EMOJI MODIFIER FITZPATRICK TYPE-1-2"),
206 | ]),
207 | ]),
208 |
209 | // Books and Apps
210 |
211 | Page(title: "Books and Apps", subtitle: "📖 🔨"),
212 |
213 | Page(contents: [
214 | .image("images/unicode-explained.jpg"),
215 | .text("Chapter 4, 5, 6"),
216 | ]),
217 |
218 | Page(contents: [
219 | .image("images/unicode-checker.png"),
220 | .text("UnicodeChecker by earthlingsoft"),
221 | ]),
222 |
223 | Page(title: "Thank you!", subtitle: "@eyeplum"),
224 | ])
225 |
--------------------------------------------------------------------------------
/Truffaut.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0B0712891F557C290074EC7A /* SyntaxHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B0712881F557C290074EC7A /* SyntaxHighlighter.swift */; };
11 | 0B07128B1F557D730074EC7A /* syntax-hl.rb in Resources */ = {isa = PBXBuildFile; fileRef = 0B07128A1F557D730074EC7A /* syntax-hl.rb */; };
12 | 0B14BCDE1E92360700EB0C6A /* TruffautSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B14BCDC1E92360700EB0C6A /* TruffautSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
13 | 0B14BCE11E92360700EB0C6A /* TruffautSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */; };
14 | 0B14BCE21E92360700EB0C6A /* TruffautSupport.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
15 | 0B14BCE81E923A1200EB0C6A /* Presentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B14BCE71E923A1200EB0C6A /* Presentation.swift */; };
16 | 0B14BCEA1E923A2100EB0C6A /* Page.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B14BCE91E923A2100EB0C6A /* Page.swift */; };
17 | 0B14BCEC1E923A6F00EB0C6A /* Content.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B14BCEB1E923A6F00EB0C6A /* Content.swift */; };
18 | 0B14BD041E92499300EB0C6A /* TruffautSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B14BD031E92499300EB0C6A /* TruffautSupportTests.swift */; };
19 | 0B14BD061E92499300EB0C6A /* TruffautSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */; };
20 | 0B14BD131E9398FE00EB0C6A /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B14BD121E9398FE00EB0C6A /* Shell.swift */; };
21 | 0B62141A1F45978A00B66C9B /* PageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B6214181F45978A00B66C9B /* PageViewController.swift */; };
22 | 0B62141B1F45978A00B66C9B /* PageViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0B6214191F45978A00B66C9B /* PageViewController.xib */; };
23 | 0B62141D1F45A39000B66C9B /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B62141C1F45A39000B66C9B /* Font.swift */; };
24 | 0B7C8BDE1CBFD166000FE312 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BDD1CBFD166000FE312 /* AppDelegate.swift */; };
25 | 0B7C8BE01CBFD166000FE312 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BDF1CBFD166000FE312 /* MainViewController.swift */; };
26 | 0B7C8BE21CBFD166000FE312 /* PresentationDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BE11CBFD166000FE312 /* PresentationDocument.swift */; };
27 | 0B7C8BE41CBFD166000FE312 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0B7C8BE31CBFD166000FE312 /* Assets.xcassets */; };
28 | 0B7C8BE71CBFD166000FE312 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0B7C8BE51CBFD166000FE312 /* Main.storyboard */; };
29 | 0B7C8BF91CC13E14000FE312 /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BF81CC13E14000FE312 /* MainWindowController.swift */; };
30 | 0B7C8BFE1CC14006000FE312 /* SlidesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BFD1CC14006000FE312 /* SlidesWindowController.swift */; };
31 | 0B7C8C001CC14016000FE312 /* SlidesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BFF1CC14016000FE312 /* SlidesViewController.swift */; };
32 | 0B7C8C021CC1422B000FE312 /* StoryboardLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8C011CC1422B000FE312 /* StoryboardLoading.swift */; };
33 | 0B7FADEB1D43472300000884 /* ExportController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7FADEA1D43472300000884 /* ExportController.swift */; };
34 | 0BAAD2341FF5DF270099C191 /* PreferenceWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BAAD2321FF5DF270099C191 /* PreferenceWindowController.swift */; };
35 | 0BAAD2371FF5E49F0099C191 /* NSWindow+Style.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BAAD2361FF5E49F0099C191 /* NSWindow+Style.swift */; };
36 | 0BAAD2391FF5E62E0099C191 /* PreferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BAAD2381FF5E62E0099C191 /* PreferenceViewController.swift */; };
37 | 0BAAD23B1FF5F34B0099C191 /* UserPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BAAD23A1FF5F34B0099C191 /* UserPreference.swift */; };
38 | 0BAB64AF1EA08D7C001CE96A /* ReadController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BAB64AE1EA08D7C001CE96A /* ReadController.swift */; };
39 | /* End PBXBuildFile section */
40 |
41 | /* Begin PBXContainerItemProxy section */
42 | 0B14BCDF1E92360700EB0C6A /* PBXContainerItemProxy */ = {
43 | isa = PBXContainerItemProxy;
44 | containerPortal = 0B7C8BD21CBFD166000FE312 /* Project object */;
45 | proxyType = 1;
46 | remoteGlobalIDString = 0B14BCD91E92360700EB0C6A;
47 | remoteInfo = TruffautSupport;
48 | };
49 | 0B14BD071E92499300EB0C6A /* PBXContainerItemProxy */ = {
50 | isa = PBXContainerItemProxy;
51 | containerPortal = 0B7C8BD21CBFD166000FE312 /* Project object */;
52 | proxyType = 1;
53 | remoteGlobalIDString = 0B14BCD91E92360700EB0C6A;
54 | remoteInfo = TruffautSupport;
55 | };
56 | /* End PBXContainerItemProxy section */
57 |
58 | /* Begin PBXCopyFilesBuildPhase section */
59 | 0B7C8BF71CC139FD000FE312 /* Copy Frameworks */ = {
60 | isa = PBXCopyFilesBuildPhase;
61 | buildActionMask = 2147483647;
62 | dstPath = "";
63 | dstSubfolderSpec = 10;
64 | files = (
65 | 0B14BCE21E92360700EB0C6A /* TruffautSupport.framework in Copy Frameworks */,
66 | );
67 | name = "Copy Frameworks";
68 | runOnlyForDeploymentPostprocessing = 0;
69 | };
70 | /* End PBXCopyFilesBuildPhase section */
71 |
72 | /* Begin PBXFileReference section */
73 | 0B0712881F557C290074EC7A /* SyntaxHighlighter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyntaxHighlighter.swift; sourceTree = ""; };
74 | 0B07128A1F557D730074EC7A /* syntax-hl.rb */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = text.script.ruby; path = "syntax-hl.rb"; sourceTree = ""; tabWidth = 2; };
75 | 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TruffautSupport.framework; sourceTree = BUILT_PRODUCTS_DIR; };
76 | 0B14BCDC1E92360700EB0C6A /* TruffautSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TruffautSupport.h; sourceTree = ""; };
77 | 0B14BCDD1E92360700EB0C6A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
78 | 0B14BCE71E923A1200EB0C6A /* Presentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Presentation.swift; sourceTree = ""; };
79 | 0B14BCE91E923A2100EB0C6A /* Page.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Page.swift; sourceTree = ""; };
80 | 0B14BCEB1E923A6F00EB0C6A /* Content.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Content.swift; sourceTree = ""; };
81 | 0B14BD011E92499300EB0C6A /* TruffautSupportTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TruffautSupportTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
82 | 0B14BD031E92499300EB0C6A /* TruffautSupportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TruffautSupportTests.swift; sourceTree = ""; };
83 | 0B14BD051E92499300EB0C6A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
84 | 0B14BD121E9398FE00EB0C6A /* Shell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = ""; };
85 | 0B6214181F45978A00B66C9B /* PageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageViewController.swift; sourceTree = ""; };
86 | 0B6214191F45978A00B66C9B /* PageViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PageViewController.xib; sourceTree = ""; };
87 | 0B62141C1F45A39000B66C9B /* Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = ""; };
88 | 0B7C8BDA1CBFD166000FE312 /* Truffaut.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Truffaut.app; sourceTree = BUILT_PRODUCTS_DIR; };
89 | 0B7C8BDD1CBFD166000FE312 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
90 | 0B7C8BDF1CBFD166000FE312 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; };
91 | 0B7C8BE11CBFD166000FE312 /* PresentationDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationDocument.swift; sourceTree = ""; };
92 | 0B7C8BE31CBFD166000FE312 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
93 | 0B7C8BE61CBFD166000FE312 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
94 | 0B7C8BE81CBFD166000FE312 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
95 | 0B7C8BF81CC13E14000FE312 /* MainWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowController.swift; sourceTree = ""; };
96 | 0B7C8BFD1CC14006000FE312 /* SlidesWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SlidesWindowController.swift; sourceTree = ""; };
97 | 0B7C8BFF1CC14016000FE312 /* SlidesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SlidesViewController.swift; sourceTree = ""; };
98 | 0B7C8C011CC1422B000FE312 /* StoryboardLoading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardLoading.swift; sourceTree = ""; };
99 | 0B7C8C0B1CC20865000FE312 /* Truffaut-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Truffaut-Bridging-Header.h"; sourceTree = ""; };
100 | 0B7FADEA1D43472300000884 /* ExportController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExportController.swift; sourceTree = ""; };
101 | 0BAAD2321FF5DF270099C191 /* PreferenceWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceWindowController.swift; sourceTree = ""; };
102 | 0BAAD2361FF5E49F0099C191 /* NSWindow+Style.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWindow+Style.swift"; sourceTree = ""; };
103 | 0BAAD2381FF5E62E0099C191 /* PreferenceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceViewController.swift; sourceTree = ""; };
104 | 0BAAD23A1FF5F34B0099C191 /* UserPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreference.swift; sourceTree = ""; };
105 | 0BAB64AE1EA08D7C001CE96A /* ReadController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadController.swift; sourceTree = ""; };
106 | /* End PBXFileReference section */
107 |
108 | /* Begin PBXFrameworksBuildPhase section */
109 | 0B14BCD61E92360700EB0C6A /* Frameworks */ = {
110 | isa = PBXFrameworksBuildPhase;
111 | buildActionMask = 2147483647;
112 | files = (
113 | );
114 | runOnlyForDeploymentPostprocessing = 0;
115 | };
116 | 0B14BCFE1E92499300EB0C6A /* Frameworks */ = {
117 | isa = PBXFrameworksBuildPhase;
118 | buildActionMask = 2147483647;
119 | files = (
120 | 0B14BD061E92499300EB0C6A /* TruffautSupport.framework in Frameworks */,
121 | );
122 | runOnlyForDeploymentPostprocessing = 0;
123 | };
124 | 0B7C8BD71CBFD166000FE312 /* Frameworks */ = {
125 | isa = PBXFrameworksBuildPhase;
126 | buildActionMask = 2147483647;
127 | files = (
128 | 0B14BCE11E92360700EB0C6A /* TruffautSupport.framework in Frameworks */,
129 | );
130 | runOnlyForDeploymentPostprocessing = 0;
131 | };
132 | /* End PBXFrameworksBuildPhase section */
133 |
134 | /* Begin PBXGroup section */
135 | 0B14BCDB1E92360700EB0C6A /* TruffautSupport */ = {
136 | isa = PBXGroup;
137 | children = (
138 | 0B14BCDC1E92360700EB0C6A /* TruffautSupport.h */,
139 | 0B14BCE71E923A1200EB0C6A /* Presentation.swift */,
140 | 0B14BCE91E923A2100EB0C6A /* Page.swift */,
141 | 0B14BCEB1E923A6F00EB0C6A /* Content.swift */,
142 | 0B14BCDD1E92360700EB0C6A /* Info.plist */,
143 | );
144 | path = TruffautSupport;
145 | sourceTree = "";
146 | };
147 | 0B14BD021E92499300EB0C6A /* TruffautSupportTests */ = {
148 | isa = PBXGroup;
149 | children = (
150 | 0B14BD031E92499300EB0C6A /* TruffautSupportTests.swift */,
151 | 0B14BD051E92499300EB0C6A /* Info.plist */,
152 | );
153 | path = TruffautSupportTests;
154 | sourceTree = "";
155 | };
156 | 0B6EED841CC26C83001BC00B /* Supporting Files */ = {
157 | isa = PBXGroup;
158 | children = (
159 | 0B07128A1F557D730074EC7A /* syntax-hl.rb */,
160 | 0B7C8C0B1CC20865000FE312 /* Truffaut-Bridging-Header.h */,
161 | 0B7C8BE31CBFD166000FE312 /* Assets.xcassets */,
162 | 0B7C8BE81CBFD166000FE312 /* Info.plist */,
163 | );
164 | name = "Supporting Files";
165 | sourceTree = "";
166 | };
167 | 0B7C8BD11CBFD166000FE312 = {
168 | isa = PBXGroup;
169 | children = (
170 | 0B7C8BDC1CBFD166000FE312 /* Truffaut */,
171 | 0B14BCDB1E92360700EB0C6A /* TruffautSupport */,
172 | 0B14BD021E92499300EB0C6A /* TruffautSupportTests */,
173 | 0B7C8BDB1CBFD166000FE312 /* Products */,
174 | );
175 | sourceTree = "";
176 | };
177 | 0B7C8BDB1CBFD166000FE312 /* Products */ = {
178 | isa = PBXGroup;
179 | children = (
180 | 0B7C8BDA1CBFD166000FE312 /* Truffaut.app */,
181 | 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */,
182 | 0B14BD011E92499300EB0C6A /* TruffautSupportTests.xctest */,
183 | );
184 | name = Products;
185 | sourceTree = "";
186 | };
187 | 0B7C8BDC1CBFD166000FE312 /* Truffaut */ = {
188 | isa = PBXGroup;
189 | children = (
190 | 0B7C8BDD1CBFD166000FE312 /* AppDelegate.swift */,
191 | 0BAB64B21EA0C200001CE96A /* Interface */,
192 | 0BAB64B11EA0C1F0001CE96A /* Utilities */,
193 | 0B7C8BFB1CC13F5D000FE312 /* Document */,
194 | 0B7C8BFA1CC13F4A000FE312 /* Main Window */,
195 | 0B7C8BFC1CC13F91000FE312 /* Slides Window */,
196 | 0BAAD2311FF5DE440099C191 /* Preference Window */,
197 | 0B6EED841CC26C83001BC00B /* Supporting Files */,
198 | );
199 | path = Truffaut;
200 | sourceTree = "";
201 | };
202 | 0B7C8BFA1CC13F4A000FE312 /* Main Window */ = {
203 | isa = PBXGroup;
204 | children = (
205 | 0B7C8BF81CC13E14000FE312 /* MainWindowController.swift */,
206 | 0B7C8BDF1CBFD166000FE312 /* MainViewController.swift */,
207 | );
208 | name = "Main Window";
209 | sourceTree = "";
210 | };
211 | 0B7C8BFB1CC13F5D000FE312 /* Document */ = {
212 | isa = PBXGroup;
213 | children = (
214 | 0B7C8BE11CBFD166000FE312 /* PresentationDocument.swift */,
215 | );
216 | name = Document;
217 | sourceTree = "";
218 | };
219 | 0B7C8BFC1CC13F91000FE312 /* Slides Window */ = {
220 | isa = PBXGroup;
221 | children = (
222 | 0B7C8BFD1CC14006000FE312 /* SlidesWindowController.swift */,
223 | 0B7C8BFF1CC14016000FE312 /* SlidesViewController.swift */,
224 | 0B6214181F45978A00B66C9B /* PageViewController.swift */,
225 | 0B6214191F45978A00B66C9B /* PageViewController.xib */,
226 | );
227 | name = "Slides Window";
228 | sourceTree = "";
229 | };
230 | 0BAAD2311FF5DE440099C191 /* Preference Window */ = {
231 | isa = PBXGroup;
232 | children = (
233 | 0BAAD2321FF5DF270099C191 /* PreferenceWindowController.swift */,
234 | 0BAAD2381FF5E62E0099C191 /* PreferenceViewController.swift */,
235 | );
236 | name = "Preference Window";
237 | sourceTree = "";
238 | };
239 | 0BAB64B11EA0C1F0001CE96A /* Utilities */ = {
240 | isa = PBXGroup;
241 | children = (
242 | 0B62141C1F45A39000B66C9B /* Font.swift */,
243 | 0B7C8C011CC1422B000FE312 /* StoryboardLoading.swift */,
244 | 0BAB64AE1EA08D7C001CE96A /* ReadController.swift */,
245 | 0B0712881F557C290074EC7A /* SyntaxHighlighter.swift */,
246 | 0B14BD121E9398FE00EB0C6A /* Shell.swift */,
247 | 0B7FADEA1D43472300000884 /* ExportController.swift */,
248 | 0BAAD2361FF5E49F0099C191 /* NSWindow+Style.swift */,
249 | 0BAAD23A1FF5F34B0099C191 /* UserPreference.swift */,
250 | );
251 | name = Utilities;
252 | sourceTree = "";
253 | };
254 | 0BAB64B21EA0C200001CE96A /* Interface */ = {
255 | isa = PBXGroup;
256 | children = (
257 | 0B7C8BE51CBFD166000FE312 /* Main.storyboard */,
258 | );
259 | name = Interface;
260 | sourceTree = "";
261 | };
262 | /* End PBXGroup section */
263 |
264 | /* Begin PBXHeadersBuildPhase section */
265 | 0B14BCD71E92360700EB0C6A /* Headers */ = {
266 | isa = PBXHeadersBuildPhase;
267 | buildActionMask = 2147483647;
268 | files = (
269 | 0B14BCDE1E92360700EB0C6A /* TruffautSupport.h in Headers */,
270 | );
271 | runOnlyForDeploymentPostprocessing = 0;
272 | };
273 | /* End PBXHeadersBuildPhase section */
274 |
275 | /* Begin PBXNativeTarget section */
276 | 0B14BCD91E92360700EB0C6A /* TruffautSupport */ = {
277 | isa = PBXNativeTarget;
278 | buildConfigurationList = 0B14BCE31E92360700EB0C6A /* Build configuration list for PBXNativeTarget "TruffautSupport" */;
279 | buildPhases = (
280 | 0B14BCD51E92360700EB0C6A /* Sources */,
281 | 0B14BCD61E92360700EB0C6A /* Frameworks */,
282 | 0B14BCD71E92360700EB0C6A /* Headers */,
283 | 0B14BCD81E92360700EB0C6A /* Resources */,
284 | 0B14BD0F1E9380D100EB0C6A /* Create libTruffautSupport.dylib and TruffautSupport.swiftmodule */,
285 | );
286 | buildRules = (
287 | );
288 | dependencies = (
289 | );
290 | name = TruffautSupport;
291 | productName = TruffautSupport;
292 | productReference = 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */;
293 | productType = "com.apple.product-type.framework";
294 | };
295 | 0B14BD001E92499300EB0C6A /* TruffautSupportTests */ = {
296 | isa = PBXNativeTarget;
297 | buildConfigurationList = 0B14BD091E92499300EB0C6A /* Build configuration list for PBXNativeTarget "TruffautSupportTests" */;
298 | buildPhases = (
299 | 0B14BCFD1E92499300EB0C6A /* Sources */,
300 | 0B14BCFE1E92499300EB0C6A /* Frameworks */,
301 | 0B14BCFF1E92499300EB0C6A /* Resources */,
302 | );
303 | buildRules = (
304 | );
305 | dependencies = (
306 | 0B14BD081E92499300EB0C6A /* PBXTargetDependency */,
307 | );
308 | name = TruffautSupportTests;
309 | productName = TruffautSupportTests;
310 | productReference = 0B14BD011E92499300EB0C6A /* TruffautSupportTests.xctest */;
311 | productType = "com.apple.product-type.bundle.unit-test";
312 | };
313 | 0B7C8BD91CBFD166000FE312 /* Truffaut */ = {
314 | isa = PBXNativeTarget;
315 | buildConfigurationList = 0B7C8BEB1CBFD166000FE312 /* Build configuration list for PBXNativeTarget "Truffaut" */;
316 | buildPhases = (
317 | 0B7C8BD61CBFD166000FE312 /* Sources */,
318 | 0B7C8BD71CBFD166000FE312 /* Frameworks */,
319 | 0B7C8BD81CBFD166000FE312 /* Resources */,
320 | 0B7C8BF71CC139FD000FE312 /* Copy Frameworks */,
321 | );
322 | buildRules = (
323 | );
324 | dependencies = (
325 | 0B14BCE01E92360700EB0C6A /* PBXTargetDependency */,
326 | );
327 | name = Truffaut;
328 | productName = Truffaut;
329 | productReference = 0B7C8BDA1CBFD166000FE312 /* Truffaut.app */;
330 | productType = "com.apple.product-type.application";
331 | };
332 | /* End PBXNativeTarget section */
333 |
334 | /* Begin PBXProject section */
335 | 0B7C8BD21CBFD166000FE312 /* Project object */ = {
336 | isa = PBXProject;
337 | attributes = {
338 | LastSwiftUpdateCheck = 0830;
339 | LastUpgradeCheck = 1030;
340 | ORGANIZATIONNAME = Codezerker;
341 | TargetAttributes = {
342 | 0B14BCD91E92360700EB0C6A = {
343 | CreatedOnToolsVersion = 8.3;
344 | DevelopmentTeam = 55724LR9PN;
345 | LastSwiftMigration = 1020;
346 | ProvisioningStyle = Automatic;
347 | };
348 | 0B14BD001E92499300EB0C6A = {
349 | CreatedOnToolsVersion = 8.3;
350 | DevelopmentTeam = 55724LR9PN;
351 | ProvisioningStyle = Automatic;
352 | };
353 | 0B7C8BD91CBFD166000FE312 = {
354 | CreatedOnToolsVersion = 7.3;
355 | DevelopmentTeam = 55724LR9PN;
356 | LastSwiftMigration = 1020;
357 | ProvisioningStyle = Automatic;
358 | };
359 | };
360 | };
361 | buildConfigurationList = 0B7C8BD51CBFD166000FE312 /* Build configuration list for PBXProject "Truffaut" */;
362 | compatibilityVersion = "Xcode 3.2";
363 | developmentRegion = en;
364 | hasScannedForEncodings = 0;
365 | knownRegions = (
366 | en,
367 | Base,
368 | );
369 | mainGroup = 0B7C8BD11CBFD166000FE312;
370 | productRefGroup = 0B7C8BDB1CBFD166000FE312 /* Products */;
371 | projectDirPath = "";
372 | projectRoot = "";
373 | targets = (
374 | 0B7C8BD91CBFD166000FE312 /* Truffaut */,
375 | 0B14BCD91E92360700EB0C6A /* TruffautSupport */,
376 | 0B14BD001E92499300EB0C6A /* TruffautSupportTests */,
377 | );
378 | };
379 | /* End PBXProject section */
380 |
381 | /* Begin PBXResourcesBuildPhase section */
382 | 0B14BCD81E92360700EB0C6A /* Resources */ = {
383 | isa = PBXResourcesBuildPhase;
384 | buildActionMask = 2147483647;
385 | files = (
386 | );
387 | runOnlyForDeploymentPostprocessing = 0;
388 | };
389 | 0B14BCFF1E92499300EB0C6A /* Resources */ = {
390 | isa = PBXResourcesBuildPhase;
391 | buildActionMask = 2147483647;
392 | files = (
393 | );
394 | runOnlyForDeploymentPostprocessing = 0;
395 | };
396 | 0B7C8BD81CBFD166000FE312 /* Resources */ = {
397 | isa = PBXResourcesBuildPhase;
398 | buildActionMask = 2147483647;
399 | files = (
400 | 0B62141B1F45978A00B66C9B /* PageViewController.xib in Resources */,
401 | 0B7C8BE41CBFD166000FE312 /* Assets.xcassets in Resources */,
402 | 0B07128B1F557D730074EC7A /* syntax-hl.rb in Resources */,
403 | 0B7C8BE71CBFD166000FE312 /* Main.storyboard in Resources */,
404 | );
405 | runOnlyForDeploymentPostprocessing = 0;
406 | };
407 | /* End PBXResourcesBuildPhase section */
408 |
409 | /* Begin PBXShellScriptBuildPhase section */
410 | 0B14BD0F1E9380D100EB0C6A /* Create libTruffautSupport.dylib and TruffautSupport.swiftmodule */ = {
411 | isa = PBXShellScriptBuildPhase;
412 | buildActionMask = 2147483647;
413 | files = (
414 | );
415 | inputPaths = (
416 | );
417 | name = "Create libTruffautSupport.dylib and TruffautSupport.swiftmodule";
418 | outputPaths = (
419 | );
420 | runOnlyForDeploymentPostprocessing = 0;
421 | shellPath = /bin/sh;
422 | shellScript = "EXTERN_LIB=$TARGET_BUILD_DIR/$PRODUCT_NAME.framework/Library\nmkdir -p $EXTERN_LIB\n\nLIB_PATH=$TARGET_BUILD_DIR/$EXECUTABLE_PATH\nMODULE_PATH=$TARGET_BUILD_DIR/$EXECUTABLE_FOLDER_PATH/Modules/$PRODUCT_NAME.swiftmodule/x86_64.swiftmodule\n\ncp $LIB_PATH $EXTERN_LIB/lib$PRODUCT_NAME.dylib\ncp $MODULE_PATH $EXTERN_LIB/$PRODUCT_NAME.swiftmodule\n";
423 | };
424 | /* End PBXShellScriptBuildPhase section */
425 |
426 | /* Begin PBXSourcesBuildPhase section */
427 | 0B14BCD51E92360700EB0C6A /* Sources */ = {
428 | isa = PBXSourcesBuildPhase;
429 | buildActionMask = 2147483647;
430 | files = (
431 | 0B14BCEA1E923A2100EB0C6A /* Page.swift in Sources */,
432 | 0B14BCE81E923A1200EB0C6A /* Presentation.swift in Sources */,
433 | 0B14BCEC1E923A6F00EB0C6A /* Content.swift in Sources */,
434 | );
435 | runOnlyForDeploymentPostprocessing = 0;
436 | };
437 | 0B14BCFD1E92499300EB0C6A /* Sources */ = {
438 | isa = PBXSourcesBuildPhase;
439 | buildActionMask = 2147483647;
440 | files = (
441 | 0B14BD041E92499300EB0C6A /* TruffautSupportTests.swift in Sources */,
442 | );
443 | runOnlyForDeploymentPostprocessing = 0;
444 | };
445 | 0B7C8BD61CBFD166000FE312 /* Sources */ = {
446 | isa = PBXSourcesBuildPhase;
447 | buildActionMask = 2147483647;
448 | files = (
449 | 0B7C8C021CC1422B000FE312 /* StoryboardLoading.swift in Sources */,
450 | 0B0712891F557C290074EC7A /* SyntaxHighlighter.swift in Sources */,
451 | 0B7C8BFE1CC14006000FE312 /* SlidesWindowController.swift in Sources */,
452 | 0B7C8BE01CBFD166000FE312 /* MainViewController.swift in Sources */,
453 | 0B62141A1F45978A00B66C9B /* PageViewController.swift in Sources */,
454 | 0BAB64AF1EA08D7C001CE96A /* ReadController.swift in Sources */,
455 | 0B14BD131E9398FE00EB0C6A /* Shell.swift in Sources */,
456 | 0BAAD2391FF5E62E0099C191 /* PreferenceViewController.swift in Sources */,
457 | 0B7FADEB1D43472300000884 /* ExportController.swift in Sources */,
458 | 0BAAD2341FF5DF270099C191 /* PreferenceWindowController.swift in Sources */,
459 | 0BAAD23B1FF5F34B0099C191 /* UserPreference.swift in Sources */,
460 | 0B7C8C001CC14016000FE312 /* SlidesViewController.swift in Sources */,
461 | 0B62141D1F45A39000B66C9B /* Font.swift in Sources */,
462 | 0B7C8BDE1CBFD166000FE312 /* AppDelegate.swift in Sources */,
463 | 0BAAD2371FF5E49F0099C191 /* NSWindow+Style.swift in Sources */,
464 | 0B7C8BF91CC13E14000FE312 /* MainWindowController.swift in Sources */,
465 | 0B7C8BE21CBFD166000FE312 /* PresentationDocument.swift in Sources */,
466 | );
467 | runOnlyForDeploymentPostprocessing = 0;
468 | };
469 | /* End PBXSourcesBuildPhase section */
470 |
471 | /* Begin PBXTargetDependency section */
472 | 0B14BCE01E92360700EB0C6A /* PBXTargetDependency */ = {
473 | isa = PBXTargetDependency;
474 | target = 0B14BCD91E92360700EB0C6A /* TruffautSupport */;
475 | targetProxy = 0B14BCDF1E92360700EB0C6A /* PBXContainerItemProxy */;
476 | };
477 | 0B14BD081E92499300EB0C6A /* PBXTargetDependency */ = {
478 | isa = PBXTargetDependency;
479 | target = 0B14BCD91E92360700EB0C6A /* TruffautSupport */;
480 | targetProxy = 0B14BD071E92499300EB0C6A /* PBXContainerItemProxy */;
481 | };
482 | /* End PBXTargetDependency section */
483 |
484 | /* Begin PBXVariantGroup section */
485 | 0B7C8BE51CBFD166000FE312 /* Main.storyboard */ = {
486 | isa = PBXVariantGroup;
487 | children = (
488 | 0B7C8BE61CBFD166000FE312 /* Base */,
489 | );
490 | name = Main.storyboard;
491 | sourceTree = "";
492 | };
493 | /* End PBXVariantGroup section */
494 |
495 | /* Begin XCBuildConfiguration section */
496 | 0B14BCE41E92360700EB0C6A /* Debug */ = {
497 | isa = XCBuildConfiguration;
498 | buildSettings = {
499 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
500 | CLANG_ENABLE_MODULES = YES;
501 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
502 | CODE_SIGN_IDENTITY = "";
503 | COMBINE_HIDPI_IMAGES = YES;
504 | DEBUG_INFORMATION_FORMAT = dwarf;
505 | DEFINES_MODULE = YES;
506 | DEVELOPMENT_TEAM = 55724LR9PN;
507 | DYLIB_COMPATIBILITY_VERSION = 1;
508 | DYLIB_INSTALL_NAME_BASE = "@rpath";
509 | FRAMEWORK_VERSION = A;
510 | INFOPLIST_FILE = TruffautSupport/Info.plist;
511 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
512 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
513 | MACOSX_DEPLOYMENT_TARGET = 10.14.4;
514 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.TruffautSupport;
515 | PRODUCT_NAME = "$(TARGET_NAME)";
516 | SKIP_INSTALL = YES;
517 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
518 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
519 | SWIFT_VERSION = 5.0;
520 | VERSIONING_SYSTEM = "apple-generic";
521 | VERSION_INFO_PREFIX = "";
522 | };
523 | name = Debug;
524 | };
525 | 0B14BCE51E92360700EB0C6A /* Release */ = {
526 | isa = XCBuildConfiguration;
527 | buildSettings = {
528 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
529 | CLANG_ENABLE_MODULES = YES;
530 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
531 | CODE_SIGN_IDENTITY = "";
532 | COMBINE_HIDPI_IMAGES = YES;
533 | DEFINES_MODULE = YES;
534 | DEVELOPMENT_TEAM = 55724LR9PN;
535 | DYLIB_COMPATIBILITY_VERSION = 1;
536 | DYLIB_INSTALL_NAME_BASE = "@rpath";
537 | FRAMEWORK_VERSION = A;
538 | INFOPLIST_FILE = TruffautSupport/Info.plist;
539 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
540 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
541 | MACOSX_DEPLOYMENT_TARGET = 10.14.4;
542 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.TruffautSupport;
543 | PRODUCT_NAME = "$(TARGET_NAME)";
544 | SKIP_INSTALL = YES;
545 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
546 | SWIFT_VERSION = 5.0;
547 | VERSIONING_SYSTEM = "apple-generic";
548 | VERSION_INFO_PREFIX = "";
549 | };
550 | name = Release;
551 | };
552 | 0B14BD0A1E92499300EB0C6A /* Debug */ = {
553 | isa = XCBuildConfiguration;
554 | buildSettings = {
555 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
556 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
557 | COMBINE_HIDPI_IMAGES = YES;
558 | DEBUG_INFORMATION_FORMAT = dwarf;
559 | DEVELOPMENT_TEAM = 55724LR9PN;
560 | INFOPLIST_FILE = TruffautSupportTests/Info.plist;
561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
562 | MACOSX_DEPLOYMENT_TARGET = 10.13;
563 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.TruffautSupportTests;
564 | PRODUCT_NAME = "$(TARGET_NAME)";
565 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
566 | SWIFT_VERSION = 4.0;
567 | };
568 | name = Debug;
569 | };
570 | 0B14BD0B1E92499300EB0C6A /* Release */ = {
571 | isa = XCBuildConfiguration;
572 | buildSettings = {
573 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
574 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
575 | COMBINE_HIDPI_IMAGES = YES;
576 | DEVELOPMENT_TEAM = 55724LR9PN;
577 | INFOPLIST_FILE = TruffautSupportTests/Info.plist;
578 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
579 | MACOSX_DEPLOYMENT_TARGET = 10.13;
580 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.TruffautSupportTests;
581 | PRODUCT_NAME = "$(TARGET_NAME)";
582 | SWIFT_VERSION = 4.0;
583 | };
584 | name = Release;
585 | };
586 | 0B7C8BE91CBFD166000FE312 /* Debug */ = {
587 | isa = XCBuildConfiguration;
588 | buildSettings = {
589 | ALWAYS_SEARCH_USER_PATHS = NO;
590 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
591 | CLANG_ANALYZER_NONNULL = YES;
592 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
593 | CLANG_CXX_LIBRARY = "libc++";
594 | CLANG_ENABLE_MODULES = YES;
595 | CLANG_ENABLE_OBJC_ARC = YES;
596 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
597 | CLANG_WARN_BOOL_CONVERSION = YES;
598 | CLANG_WARN_COMMA = YES;
599 | CLANG_WARN_CONSTANT_CONVERSION = YES;
600 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
601 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
602 | CLANG_WARN_EMPTY_BODY = YES;
603 | CLANG_WARN_ENUM_CONVERSION = YES;
604 | CLANG_WARN_INFINITE_RECURSION = YES;
605 | CLANG_WARN_INT_CONVERSION = YES;
606 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
607 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
608 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
609 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
610 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
611 | CLANG_WARN_STRICT_PROTOTYPES = YES;
612 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
613 | CLANG_WARN_UNREACHABLE_CODE = YES;
614 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
615 | CODE_SIGN_IDENTITY = "-";
616 | COPY_PHASE_STRIP = NO;
617 | CURRENT_PROJECT_VERSION = 204;
618 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
619 | ENABLE_STRICT_OBJC_MSGSEND = YES;
620 | ENABLE_TESTABILITY = YES;
621 | GCC_C_LANGUAGE_STANDARD = gnu99;
622 | GCC_DYNAMIC_NO_PIC = NO;
623 | GCC_NO_COMMON_BLOCKS = YES;
624 | GCC_OPTIMIZATION_LEVEL = 0;
625 | GCC_PREPROCESSOR_DEFINITIONS = (
626 | "DEBUG=1",
627 | "$(inherited)",
628 | );
629 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
630 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
631 | GCC_WARN_UNDECLARED_SELECTOR = YES;
632 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
633 | GCC_WARN_UNUSED_FUNCTION = YES;
634 | GCC_WARN_UNUSED_VARIABLE = YES;
635 | MACOSX_DEPLOYMENT_TARGET = 10.11;
636 | MTL_ENABLE_DEBUG_INFO = YES;
637 | ONLY_ACTIVE_ARCH = YES;
638 | SDKROOT = macosx;
639 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
640 | SWIFT_VERSION = 3.0;
641 | VERSIONING_SYSTEM = "apple-generic";
642 | };
643 | name = Debug;
644 | };
645 | 0B7C8BEA1CBFD166000FE312 /* Release */ = {
646 | isa = XCBuildConfiguration;
647 | buildSettings = {
648 | ALWAYS_SEARCH_USER_PATHS = NO;
649 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
650 | CLANG_ANALYZER_NONNULL = YES;
651 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
652 | CLANG_CXX_LIBRARY = "libc++";
653 | CLANG_ENABLE_MODULES = YES;
654 | CLANG_ENABLE_OBJC_ARC = YES;
655 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
656 | CLANG_WARN_BOOL_CONVERSION = YES;
657 | CLANG_WARN_COMMA = YES;
658 | CLANG_WARN_CONSTANT_CONVERSION = YES;
659 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
660 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
661 | CLANG_WARN_EMPTY_BODY = YES;
662 | CLANG_WARN_ENUM_CONVERSION = YES;
663 | CLANG_WARN_INFINITE_RECURSION = YES;
664 | CLANG_WARN_INT_CONVERSION = YES;
665 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
666 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
667 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
668 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
669 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
670 | CLANG_WARN_STRICT_PROTOTYPES = YES;
671 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
672 | CLANG_WARN_UNREACHABLE_CODE = YES;
673 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
674 | CODE_SIGN_IDENTITY = "-";
675 | COPY_PHASE_STRIP = NO;
676 | CURRENT_PROJECT_VERSION = 204;
677 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
678 | ENABLE_NS_ASSERTIONS = NO;
679 | ENABLE_STRICT_OBJC_MSGSEND = YES;
680 | GCC_C_LANGUAGE_STANDARD = gnu99;
681 | GCC_NO_COMMON_BLOCKS = YES;
682 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
683 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
684 | GCC_WARN_UNDECLARED_SELECTOR = YES;
685 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
686 | GCC_WARN_UNUSED_FUNCTION = YES;
687 | GCC_WARN_UNUSED_VARIABLE = YES;
688 | MACOSX_DEPLOYMENT_TARGET = 10.11;
689 | MTL_ENABLE_DEBUG_INFO = NO;
690 | SDKROOT = macosx;
691 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
692 | SWIFT_VERSION = 3.0;
693 | VERSIONING_SYSTEM = "apple-generic";
694 | };
695 | name = Release;
696 | };
697 | 0B7C8BEC1CBFD166000FE312 /* Debug */ = {
698 | isa = XCBuildConfiguration;
699 | buildSettings = {
700 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
701 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
702 | CLANG_ENABLE_MODULES = YES;
703 | CODE_SIGN_IDENTITY = "Mac Developer";
704 | COMBINE_HIDPI_IMAGES = YES;
705 | DEVELOPMENT_TEAM = 55724LR9PN;
706 | ENABLE_HARDENED_RUNTIME = YES;
707 | FRAMEWORK_SEARCH_PATHS = "$(inherited)";
708 | INFOPLIST_FILE = Truffaut/Info.plist;
709 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
710 | MACOSX_DEPLOYMENT_TARGET = 10.14.4;
711 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.Truffaut;
712 | PRODUCT_NAME = "$(TARGET_NAME)";
713 | SWIFT_OBJC_BRIDGING_HEADER = "Truffaut/Truffaut-Bridging-Header.h";
714 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
715 | SWIFT_VERSION = 5.0;
716 | };
717 | name = Debug;
718 | };
719 | 0B7C8BED1CBFD166000FE312 /* Release */ = {
720 | isa = XCBuildConfiguration;
721 | buildSettings = {
722 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
723 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
724 | CLANG_ENABLE_MODULES = YES;
725 | CODE_SIGN_IDENTITY = "Mac Developer";
726 | COMBINE_HIDPI_IMAGES = YES;
727 | DEVELOPMENT_TEAM = 55724LR9PN;
728 | ENABLE_HARDENED_RUNTIME = YES;
729 | FRAMEWORK_SEARCH_PATHS = "$(inherited)";
730 | INFOPLIST_FILE = Truffaut/Info.plist;
731 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
732 | MACOSX_DEPLOYMENT_TARGET = 10.14.4;
733 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.Truffaut;
734 | PRODUCT_NAME = "$(TARGET_NAME)";
735 | SWIFT_OBJC_BRIDGING_HEADER = "Truffaut/Truffaut-Bridging-Header.h";
736 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
737 | SWIFT_VERSION = 5.0;
738 | };
739 | name = Release;
740 | };
741 | /* End XCBuildConfiguration section */
742 |
743 | /* Begin XCConfigurationList section */
744 | 0B14BCE31E92360700EB0C6A /* Build configuration list for PBXNativeTarget "TruffautSupport" */ = {
745 | isa = XCConfigurationList;
746 | buildConfigurations = (
747 | 0B14BCE41E92360700EB0C6A /* Debug */,
748 | 0B14BCE51E92360700EB0C6A /* Release */,
749 | );
750 | defaultConfigurationIsVisible = 0;
751 | defaultConfigurationName = Release;
752 | };
753 | 0B14BD091E92499300EB0C6A /* Build configuration list for PBXNativeTarget "TruffautSupportTests" */ = {
754 | isa = XCConfigurationList;
755 | buildConfigurations = (
756 | 0B14BD0A1E92499300EB0C6A /* Debug */,
757 | 0B14BD0B1E92499300EB0C6A /* Release */,
758 | );
759 | defaultConfigurationIsVisible = 0;
760 | defaultConfigurationName = Release;
761 | };
762 | 0B7C8BD51CBFD166000FE312 /* Build configuration list for PBXProject "Truffaut" */ = {
763 | isa = XCConfigurationList;
764 | buildConfigurations = (
765 | 0B7C8BE91CBFD166000FE312 /* Debug */,
766 | 0B7C8BEA1CBFD166000FE312 /* Release */,
767 | );
768 | defaultConfigurationIsVisible = 0;
769 | defaultConfigurationName = Release;
770 | };
771 | 0B7C8BEB1CBFD166000FE312 /* Build configuration list for PBXNativeTarget "Truffaut" */ = {
772 | isa = XCConfigurationList;
773 | buildConfigurations = (
774 | 0B7C8BEC1CBFD166000FE312 /* Debug */,
775 | 0B7C8BED1CBFD166000FE312 /* Release */,
776 | );
777 | defaultConfigurationIsVisible = 0;
778 | defaultConfigurationName = Release;
779 | };
780 | /* End XCConfigurationList section */
781 | };
782 | rootObject = 0B7C8BD21CBFD166000FE312 /* Project object */;
783 | }
784 |
--------------------------------------------------------------------------------
/Truffaut/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
--------------------------------------------------------------------------------