├── .gitattributes ├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── TODO.md ├── bin ├── postinstall.js └── shrinkray.js ├── desktop.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── WebView Finagler (Debug).xcscheme │ └── WebView Finagler (Release).xcscheme ├── desktop ├── MDAppDelegate.h ├── MDAppDelegate.m ├── MainMenu.xib ├── desktop-Info.plist ├── desktop-Prefix.pch ├── en.lproj │ └── InfoPlist.strings ├── html │ └── index.html ├── main.m ├── tiny.c └── tiny.h ├── docs └── images │ ├── drawio-app.png │ ├── mini-paint-app.png │ └── regulex-app.png ├── package.json ├── tests └── fixtures │ └── simple │ └── index.html └── tools └── create-app-template.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | *.xib binary 2 | *.strings set diff 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xcuserstate 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | 31 | 32 | # my extras 33 | *.xcsettings 34 | *.xcworkspacedata 35 | *.xccheckout 36 | 37 | node_modules 38 | npm-debug.log 39 | template 40 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | desktop 2 | desktop.xcodeproj 3 | README.md 4 | LICENSE.md 5 | build 6 | tools 7 | tests 8 | docs 9 | template/desktop.app 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Francois Laberge 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Shrinkray 2 | Create desktop apps in Javascript. Lightweight alternative to Electron because of only supporting macOS (for now). 3 | 4 | *Features* 5 | - [x] CLI for converting static website into a macOS app. 6 | - [x] Tiny App sizes (macOS: < 100K, Windows: N/A) 7 | - [x] Debuggable (Right-click window, select "Inspect Element") 8 | - [ ] Fullscreen API 9 | - [ ] File Read/Write API 10 | - [ ] Menu API 11 | 12 | #### Installation 13 | 14 | ``` 15 | npm install -g shrinkray 16 | ``` 17 | 18 | #### Usage 19 | The input path must be to a folder that has an index.html in it, everything in the folder 20 | will be copied into the generated app and accessible. 21 | 22 | ``` 23 | shrinkray -i /html -o example.app 24 | ``` 25 | 26 | #### Example Apps 27 | 28 | ##### Draw.io 29 | Source code (Click thumbnail to download app) 30 |
31 | 32 | 33 | 34 | 35 | ##### Regulex 36 | Source code (Click thumbnail to download app) 37 |
38 | 39 | 40 | 41 | 42 | ##### Mini Paint 43 | Source code (Click thumbnail to download app) 44 |
45 | 46 | 47 | 48 | 49 | ### Contributing 50 | 51 | 1. Required Tools 52 | - [XCode](https://developer.apple.com/xcode/) 53 | - [Node/NPM](https://nodejs.org/en/download/) 54 | 2. Clone the project 55 | 56 | git clone git@github.com:francoislaberge/shrinkray.git 57 | cd shrinkray 58 | 59 | 3. Install Dependencies 60 | 61 | npm install 62 | 63 | 4. Build App Template 64 | 65 | npm run build:app 66 | 67 | 5. Publish to npm 68 | 69 | npm run publish:patch 70 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | #### Todo 2 | - [ ] Fix it so that it will replace existing .app files (Or at least warn and not do it unless -f is passed) 3 | - [ ] Clean up warnings in desktop.xcodeproj 4 | - [ ] Figure out a better workflow such that you can npm link shrinkray locally and it will work 5 | - [ ] Get draw.io wrapped once we have the embedded server. Also strip draw.io down even further to make it lean 6 | - [ ] Consider making Shrinkray's design goals being to make desktop style javascript apps easy to make, but also if made 7 | easy to host in a standard container that provides the same APIs as on desktop (Menus, file save/load, etc)' 8 | - [ ] Get a domain? 9 | - io domain? 10 | - shrinkrayjs.org? 11 | - Add support for generating .dmg and .pkg 12 | - Add support for .shrinkray file that specifies things like: 13 | - icons 14 | - app name 15 | - file extensions 16 | - Reference: [electron-builder](https://github.com/electron-userland/electron-builder) 17 | - Research other similar approaches before spending too much time on this 18 | - https://nodekit.io/ (This looks pretty impressive) 19 | - https://github.com/zserge/webview/blob/master/README.md 20 | 21 | ### Done 22 | - [x] Create npm module and cli that gets basic functionality working 23 | - [x] Build in an HTTP server, and host files via http://localhost: instead of file:/// 24 | -------------------------------------------------------------------------------- /bin/postinstall.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var execSync = require('child_process').execSync; 3 | var path = require('path'); 4 | var templateFolder = path.resolve(__dirname, '../template'); 5 | 6 | execSync('unzip ' + templateFolder + '/app.zip -d ' + templateFolder); 7 | -------------------------------------------------------------------------------- /bin/shrinkray.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var program = require('commander'); 3 | var fs = require('fs-extra'); 4 | var path = require('path'); 5 | 6 | program 7 | .option('-i, --input [input]', 'Input directory (ex. project/html)') 8 | .option('-o, --output [output]', 'The desired app path (ex. output/example.app)') 9 | .parse(process.argv); 10 | 11 | // If no args were passed, print out help instructions 12 | if( process.argv.length === 2 ) { 13 | program.help(); 14 | process.exit(1); 15 | } 16 | 17 | if(!program.input){ 18 | console.log("\nError: Input directory required"); 19 | program.help(); 20 | process.exit(1); 21 | } 22 | if(!program.output){ 23 | console.log("\nError: Output path required"); 24 | program.help(); 25 | process.exit(1); 26 | } 27 | 28 | // Ensure output has .app extension 29 | if( path.parse(program.output).ext !== ".app") { 30 | console.log("\nError: Output path requires '.app' extension"); 31 | program.help(); 32 | process.exit(1); 33 | } 34 | 35 | // Copy template to output location 36 | var templateAppPath = path.resolve(__dirname, "../template/desktop.app"); 37 | var outputAppPath = path.resolve(program.output); 38 | fs.copySync(templateAppPath, outputAppPath ); 39 | 40 | // Replace html content with supplied content 41 | var outputAppHTMLPath = path.resolve(outputAppPath, "Contents/Resources/html"); 42 | fs.copySync(program.input, outputAppHTMLPath ); 43 | 44 | console.log("App generation complete. \n " + outputAppPath); 45 | -------------------------------------------------------------------------------- /desktop.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1E5AA05C1DE4D06E00AA696A /* en.lproj in Resources */ = {isa = PBXBuildFile; fileRef = 1E5AA0571DE4D06E00AA696A /* en.lproj */; }; 11 | 1E5AA05E1DE4D06E00AA696A /* html in Resources */ = {isa = PBXBuildFile; fileRef = 1E5AA0591DE4D06E00AA696A /* html */; }; 12 | 1E5AA05F1DE4D06E00AA696A /* LICENSE.md in Sources */ = {isa = PBXBuildFile; fileRef = 1E5AA05A1DE4D06E00AA696A /* LICENSE.md */; }; 13 | 1E5AA0601DE4D06E00AA696A /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = 1E5AA05B1DE4D06E00AA696A /* README.md */; }; 14 | 1E5AA0651DE4D08300AA696A /* MDAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E5AA0611DE4D08300AA696A /* MDAppDelegate.m */; }; 15 | 1E5AA0661DE4D08300AA696A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E5AA0631DE4D08300AA696A /* main.m */; }; 16 | 1E5AA0691DE4D0F700AA696A /* desktop-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 1E5AA0681DE4D0F700AA696A /* desktop-Info.plist */; }; 17 | 1E69224B1E99E7E1002ECD99 /* tiny.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E69224A1E99E7E1002ECD99 /* tiny.c */; }; 18 | 1E7F01891E9026CF00E352BF /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1E7F01881E9026CF00E352BF /* MainMenu.xib */; }; 19 | C168778216AFD5DC008FFFF0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C168778116AFD5DC008FFFF0 /* Cocoa.framework */; }; 20 | C168779F16AFD5E4008FFFF0 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C168779E16AFD5E4008FFFF0 /* WebKit.framework */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 1E5AA0571DE4D06E00AA696A /* en.lproj */ = {isa = PBXFileReference; lastKnownFileType = folder; name = en.lproj; path = desktop/en.lproj; sourceTree = ""; }; 25 | 1E5AA0591DE4D06E00AA696A /* html */ = {isa = PBXFileReference; lastKnownFileType = folder; name = html; path = desktop/html; sourceTree = ""; }; 26 | 1E5AA05A1DE4D06E00AA696A /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = ""; }; 27 | 1E5AA05B1DE4D06E00AA696A /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 28 | 1E5AA0611DE4D08300AA696A /* MDAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MDAppDelegate.m; path = desktop/MDAppDelegate.m; sourceTree = SOURCE_ROOT; }; 29 | 1E5AA0631DE4D08300AA696A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = desktop/main.m; sourceTree = SOURCE_ROOT; }; 30 | 1E5AA0641DE4D08300AA696A /* MDAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MDAppDelegate.h; path = desktop/MDAppDelegate.h; sourceTree = SOURCE_ROOT; }; 31 | 1E5AA0671DE4D0F700AA696A /* desktop-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "desktop-Prefix.pch"; path = "desktop/desktop-Prefix.pch"; sourceTree = SOURCE_ROOT; }; 32 | 1E5AA0681DE4D0F700AA696A /* desktop-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "desktop-Info.plist"; path = "desktop/desktop-Info.plist"; sourceTree = SOURCE_ROOT; }; 33 | 1E69224A1E99E7E1002ECD99 /* tiny.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tiny.c; path = desktop/tiny.c; sourceTree = SOURCE_ROOT; }; 34 | 1E7F01881E9026CF00E352BF /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = MainMenu.xib; path = desktop/MainMenu.xib; sourceTree = SOURCE_ROOT; }; 35 | 1EA04B2F1E9A690300F037DE /* tiny.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = tiny.h; path = desktop/tiny.h; sourceTree = SOURCE_ROOT; }; 36 | C168777E16AFD5DC008FFFF0 /* desktop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = desktop.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | C168778116AFD5DC008FFFF0 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 38 | C168778416AFD5DC008FFFF0 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 39 | C168778616AFD5DC008FFFF0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 40 | C168779E16AFD5E4008FFFF0 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | C168777B16AFD5DC008FFFF0 /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | C168778216AFD5DC008FFFF0 /* Cocoa.framework in Frameworks */, 49 | C168779F16AFD5E4008FFFF0 /* WebKit.framework in Frameworks */, 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | 1E5AA0561DE4D02A00AA696A /* Resources */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 1E5AA0681DE4D0F700AA696A /* desktop-Info.plist */, 60 | 1E5AA0571DE4D06E00AA696A /* en.lproj */, 61 | 1E5AA0591DE4D06E00AA696A /* html */, 62 | 1E5AA05A1DE4D06E00AA696A /* LICENSE.md */, 63 | 1E5AA05B1DE4D06E00AA696A /* README.md */, 64 | ); 65 | name = Resources; 66 | sourceTree = ""; 67 | }; 68 | C168777516AFD5DC008FFFF0 = { 69 | isa = PBXGroup; 70 | children = ( 71 | 1E5AA0561DE4D02A00AA696A /* Resources */, 72 | C168778716AFD5DC008FFFF0 /* Source */, 73 | C168778016AFD5DC008FFFF0 /* Frameworks */, 74 | C168777F16AFD5DC008FFFF0 /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | C168777F16AFD5DC008FFFF0 /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | C168777E16AFD5DC008FFFF0 /* desktop.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | C168778016AFD5DC008FFFF0 /* Frameworks */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | C168778116AFD5DC008FFFF0 /* Cocoa.framework */, 90 | C168779E16AFD5E4008FFFF0 /* WebKit.framework */, 91 | C168778316AFD5DC008FFFF0 /* Other Frameworks */, 92 | ); 93 | name = Frameworks; 94 | sourceTree = ""; 95 | }; 96 | C168778316AFD5DC008FFFF0 /* Other Frameworks */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | C168778416AFD5DC008FFFF0 /* AppKit.framework */, 100 | C168778616AFD5DC008FFFF0 /* Foundation.framework */, 101 | ); 102 | name = "Other Frameworks"; 103 | sourceTree = ""; 104 | }; 105 | C168778716AFD5DC008FFFF0 /* Source */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 1E69224A1E99E7E1002ECD99 /* tiny.c */, 109 | 1E7F01881E9026CF00E352BF /* MainMenu.xib */, 110 | 1E5AA0671DE4D0F700AA696A /* desktop-Prefix.pch */, 111 | 1E5AA0611DE4D08300AA696A /* MDAppDelegate.m */, 112 | 1E5AA0631DE4D08300AA696A /* main.m */, 113 | 1E5AA0641DE4D08300AA696A /* MDAppDelegate.h */, 114 | 1EA04B2F1E9A690300F037DE /* tiny.h */, 115 | ); 116 | name = Source; 117 | path = "WebView Finagler"; 118 | sourceTree = ""; 119 | }; 120 | /* End PBXGroup section */ 121 | 122 | /* Begin PBXNativeTarget section */ 123 | C168777D16AFD5DC008FFFF0 /* desktop */ = { 124 | isa = PBXNativeTarget; 125 | buildConfigurationList = C168779B16AFD5DC008FFFF0 /* Build configuration list for PBXNativeTarget "desktop" */; 126 | buildPhases = ( 127 | C168777A16AFD5DC008FFFF0 /* Sources */, 128 | C168777B16AFD5DC008FFFF0 /* Frameworks */, 129 | C168777C16AFD5DC008FFFF0 /* Resources */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | ); 135 | name = desktop; 136 | productName = "WebView Finagler"; 137 | productReference = C168777E16AFD5DC008FFFF0 /* desktop.app */; 138 | productType = "com.apple.product-type.application"; 139 | }; 140 | /* End PBXNativeTarget section */ 141 | 142 | /* Begin PBXProject section */ 143 | C168777616AFD5DC008FFFF0 /* Project object */ = { 144 | isa = PBXProject; 145 | attributes = { 146 | CLASSPREFIX = MD; 147 | LastUpgradeCheck = 0460; 148 | ORGANIZATIONNAME = "Mark Douma"; 149 | }; 150 | buildConfigurationList = C168777916AFD5DC008FFFF0 /* Build configuration list for PBXProject "desktop" */; 151 | compatibilityVersion = "Xcode 3.2"; 152 | developmentRegion = English; 153 | hasScannedForEncodings = 0; 154 | knownRegions = ( 155 | en, 156 | ); 157 | mainGroup = C168777516AFD5DC008FFFF0; 158 | productRefGroup = C168777F16AFD5DC008FFFF0 /* Products */; 159 | projectDirPath = ""; 160 | projectRoot = ""; 161 | targets = ( 162 | C168777D16AFD5DC008FFFF0 /* desktop */, 163 | ); 164 | }; 165 | /* End PBXProject section */ 166 | 167 | /* Begin PBXResourcesBuildPhase section */ 168 | C168777C16AFD5DC008FFFF0 /* Resources */ = { 169 | isa = PBXResourcesBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | 1E5AA0691DE4D0F700AA696A /* desktop-Info.plist in Resources */, 173 | 1E5AA05C1DE4D06E00AA696A /* en.lproj in Resources */, 174 | 1E5AA05E1DE4D06E00AA696A /* html in Resources */, 175 | 1E7F01891E9026CF00E352BF /* MainMenu.xib in Resources */, 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXResourcesBuildPhase section */ 180 | 181 | /* Begin PBXSourcesBuildPhase section */ 182 | C168777A16AFD5DC008FFFF0 /* Sources */ = { 183 | isa = PBXSourcesBuildPhase; 184 | buildActionMask = 2147483647; 185 | files = ( 186 | 1E69224B1E99E7E1002ECD99 /* tiny.c in Sources */, 187 | 1E5AA05F1DE4D06E00AA696A /* LICENSE.md in Sources */, 188 | 1E5AA0601DE4D06E00AA696A /* README.md in Sources */, 189 | 1E5AA0651DE4D08300AA696A /* MDAppDelegate.m in Sources */, 190 | 1E5AA0661DE4D08300AA696A /* main.m in Sources */, 191 | ); 192 | runOnlyForDeploymentPostprocessing = 0; 193 | }; 194 | /* End PBXSourcesBuildPhase section */ 195 | 196 | /* Begin XCBuildConfiguration section */ 197 | C168779916AFD5DC008FFFF0 /* Debug */ = { 198 | isa = XCBuildConfiguration; 199 | buildSettings = { 200 | ALWAYS_SEARCH_USER_PATHS = NO; 201 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 202 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 203 | CLANG_CXX_LIBRARY = "libc++"; 204 | CLANG_ENABLE_OBJC_ARC = YES; 205 | CLANG_WARN_CONSTANT_CONVERSION = YES; 206 | CLANG_WARN_EMPTY_BODY = YES; 207 | CLANG_WARN_ENUM_CONVERSION = YES; 208 | CLANG_WARN_INT_CONVERSION = YES; 209 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 210 | COPY_PHASE_STRIP = NO; 211 | GCC_C_LANGUAGE_STANDARD = gnu99; 212 | GCC_DYNAMIC_NO_PIC = NO; 213 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 214 | GCC_OPTIMIZATION_LEVEL = 0; 215 | GCC_PREPROCESSOR_DEFINITIONS = ( 216 | "DEBUG=1", 217 | "$(inherited)", 218 | ); 219 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 220 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 221 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 222 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 223 | GCC_WARN_UNUSED_VARIABLE = YES; 224 | MACOSX_DEPLOYMENT_TARGET = 10.8; 225 | ONLY_ACTIVE_ARCH = YES; 226 | SDKROOT = macosx; 227 | }; 228 | name = Debug; 229 | }; 230 | C168779A16AFD5DC008FFFF0 /* Release */ = { 231 | isa = XCBuildConfiguration; 232 | buildSettings = { 233 | ALWAYS_SEARCH_USER_PATHS = NO; 234 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 236 | CLANG_CXX_LIBRARY = "libc++"; 237 | CLANG_ENABLE_OBJC_ARC = YES; 238 | CLANG_WARN_CONSTANT_CONVERSION = YES; 239 | CLANG_WARN_EMPTY_BODY = YES; 240 | CLANG_WARN_ENUM_CONVERSION = YES; 241 | CLANG_WARN_INT_CONVERSION = YES; 242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 243 | COPY_PHASE_STRIP = YES; 244 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 245 | GCC_C_LANGUAGE_STANDARD = gnu99; 246 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 247 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 248 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 249 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 250 | GCC_WARN_UNUSED_VARIABLE = YES; 251 | MACOSX_DEPLOYMENT_TARGET = 10.8; 252 | SDKROOT = macosx; 253 | }; 254 | name = Release; 255 | }; 256 | C168779C16AFD5DC008FFFF0 /* Debug */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | COMBINE_HIDPI_IMAGES = YES; 260 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 261 | GCC_PREFIX_HEADER = "desktop/desktop-Prefix.pch"; 262 | INFOPLIST_FILE = "desktop/desktop-Info.plist"; 263 | PRODUCT_BUNDLE_IDENTIFIER = com.francoislaberge.desktop; 264 | PRODUCT_NAME = "$(TARGET_NAME)"; 265 | WRAPPER_EXTENSION = app; 266 | }; 267 | name = Debug; 268 | }; 269 | C168779D16AFD5DC008FFFF0 /* Release */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | COMBINE_HIDPI_IMAGES = YES; 273 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 274 | GCC_PREFIX_HEADER = "desktop/desktop-Prefix.pch"; 275 | INFOPLIST_FILE = "desktop/desktop-Info.plist"; 276 | PRODUCT_BUNDLE_IDENTIFIER = com.francoislaberge.desktop; 277 | PRODUCT_NAME = "$(TARGET_NAME)"; 278 | WRAPPER_EXTENSION = app; 279 | }; 280 | name = Release; 281 | }; 282 | /* End XCBuildConfiguration section */ 283 | 284 | /* Begin XCConfigurationList section */ 285 | C168777916AFD5DC008FFFF0 /* Build configuration list for PBXProject "desktop" */ = { 286 | isa = XCConfigurationList; 287 | buildConfigurations = ( 288 | C168779916AFD5DC008FFFF0 /* Debug */, 289 | C168779A16AFD5DC008FFFF0 /* Release */, 290 | ); 291 | defaultConfigurationIsVisible = 0; 292 | defaultConfigurationName = Release; 293 | }; 294 | C168779B16AFD5DC008FFFF0 /* Build configuration list for PBXNativeTarget "desktop" */ = { 295 | isa = XCConfigurationList; 296 | buildConfigurations = ( 297 | C168779C16AFD5DC008FFFF0 /* Debug */, 298 | C168779D16AFD5DC008FFFF0 /* Release */, 299 | ); 300 | defaultConfigurationIsVisible = 0; 301 | defaultConfigurationName = Release; 302 | }; 303 | /* End XCConfigurationList section */ 304 | }; 305 | rootObject = C168777616AFD5DC008FFFF0 /* Project object */; 306 | } 307 | -------------------------------------------------------------------------------- /desktop.xcodeproj/xcshareddata/xcschemes/WebView Finagler (Debug).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 | -------------------------------------------------------------------------------- /desktop.xcodeproj/xcshareddata/xcschemes/WebView Finagler (Release).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 | -------------------------------------------------------------------------------- /desktop/MDAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // MDAppDelegate.h 3 | // WebView Finagler 4 | // 5 | // Created by Mark Douma on 1/23/2013. 6 | // Copyright (c) 2013 Mark Douma. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class WebView; 12 | 13 | 14 | @interface MDAppDelegate : NSObject 15 | 16 | @property (weak) IBOutlet WebView *webView; 17 | @property (assign) IBOutlet NSWindow *window; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /desktop/MDAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // MDAppDelegate.m 3 | // WebView Finagler 4 | // 5 | // Created by Mark Douma on 1/23/2013. 6 | // Copyright (c) 2013 Mark Douma. All rights reserved. 7 | // 8 | 9 | #import "MDAppDelegate.h" 10 | #import 11 | #import "tiny.h" 12 | 13 | @implementation MDAppDelegate 14 | 15 | pid_t serverPid; 16 | 17 | + (void)initialize { 18 | [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"WebKitDeveloperExtras": @YES, 19 | @"WebKitScriptDebuggerEnabled": @YES, 20 | @"WebKitScriptProfilerEnabled": @YES}]; 21 | } 22 | 23 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 24 | 25 | // Get an available port 26 | int port = getPort(); 27 | printf("port number %d\n", port); 28 | 29 | // Start basic http file server that hosts every file in Resources/html 30 | // Get main bundle's 'Resources' directory 31 | NSString* resourceDirectory = [[NSBundle mainBundle] resourcePath]; 32 | // Add 'html' path to it for the actual html files directory 33 | NSString* htmlDirectory = [resourceDirectory stringByAppendingString:@"/html"] ; 34 | char* directory = [htmlDirectory cStringUsingEncoding:NSUTF8StringEncoding]; 35 | printf(directory); 36 | NSLog(@"\n\nServing files in directory: \n%@\n\n", htmlDirectory); 37 | // Start actual static file hosting server. Save the server's forked process pid so we can force kill it on exit 38 | serverPid = serveFiles(directory, port); 39 | 40 | // Now load the wrapped website the webview 41 | // Create home page url using our dynamically selected port 42 | NSString* urlString = [NSString stringWithFormat:@"http://localhost:%d", port]; 43 | // Convert string to a proper URL structure 44 | NSURL* fileURL = [NSURL URLWithString:urlString]; 45 | // Tell the webview itself to load the url 46 | NSURLRequest *request = [NSURLRequest requestWithURL:fileURL]; 47 | [self.webView.mainFrame loadRequest:request]; 48 | } 49 | 50 | - (void)applicationWillTerminate:(NSNotification *)notification{ 51 | // Kill the main file server process 52 | kill(serverPid, SIGTERM); 53 | } 54 | 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /desktop/MainMenu.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 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 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 | -------------------------------------------------------------------------------- /desktop/desktop-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 100 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2017 Francois Laberge. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | NSAppTransportSecurity 34 | 35 | NSAllowsArbitraryLoads 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /desktop/desktop-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'WebView Finagler' target in the 'WebView Finagler' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /desktop/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | -------------------------------------------------------------------------------- /desktop/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Small Apps 5 | 13 | 14 | 15 |

Hello!

16 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /desktop/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // WebView Finagler 4 | // 5 | // Created by Mark Douma on 1/23/2013. 6 | // Copyright (c) 2013 Mark Douma. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | /* 14 | * Procedural way enabling the webinspector so people can debug their 'desktop' applications. 15 | * Another way would be to have them use this terminal command instead: 16 | * https://developer.apple.com/library/content/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/GettingStarted/GettingStarted.html#//apple_ref/doc/uid/TP40007874-CH2-SW1 17 | * 18 | * We need to do this as early in the app initialization as possible, or it doesn't get picked up, suggested via this thread: 19 | * https://stackoverflow.com/questions/695038/is-there-a-way-to-use-the-webkit-web-inspector-from-a-cocoa-webview-object#comment509019_695078 20 | */ 21 | [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"WebKitDeveloperExtras"]; 22 | [[NSUserDefaults standardUserDefaults] synchronize]; 23 | 24 | return NSApplicationMain(argc, (const char **)argv); 25 | } 26 | -------------------------------------------------------------------------------- /desktop/tiny.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Original source code: https://github.com/ankushagarwal/nweb 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "tiny.h" 17 | #define VERSION 23 18 | #define BUFSIZE 8096 19 | #define ERROR 42 20 | #define LOG 44 21 | #define FORBIDDEN 403 22 | #define NOTFOUND 404 23 | 24 | #ifndef SIGCLD 25 | # define SIGCLD SIGCHLD 26 | #endif 27 | 28 | struct { 29 | char *ext; 30 | char *filetype; 31 | } extensions [] = { 32 | {"gif", "image/gif" }, 33 | {"jpg", "image/jpg" }, 34 | {"jpeg","image/jpeg"}, 35 | {"png", "image/png" }, 36 | {"ico", "image/ico" }, 37 | {"zip", "image/zip" }, 38 | {"gz", "image/gz" }, 39 | {"tar", "image/tar" }, 40 | {"htm", "text/html" }, 41 | {"html","text/html" }, 42 | {"css","text/css" }, 43 | {"xml","application/octet-stream" }, 44 | {"js","text/javascript" }, 45 | {0,0} }; 46 | char* genericMimetype= "application/octet-stream"; 47 | 48 | void logger(int type, char *s1, char *s2, int socket_fd) 49 | { 50 | int fd ; 51 | char logbuffer[BUFSIZE*2]; 52 | 53 | switch (type) { 54 | case ERROR: (void)sprintf(logbuffer,"ERROR: %s:%s Errno=%d exiting pid=%d",s1, s2, errno,getpid()); 55 | break; 56 | case FORBIDDEN: 57 | (void)write(socket_fd, "HTTP/1.1 403 Forbidden\nContent-Length: 185\nConnection: close\nContent-Type: text/html\n\n\n403 Forbidden\n\n

Forbidden

\nThe requested URL, file type or operation is not allowed on this simple static file webserver.\n\n",271); 58 | (void)sprintf(logbuffer,"FORBIDDEN: %s:%s",s1, s2); 59 | break; 60 | case NOTFOUND: 61 | (void)write(socket_fd, "HTTP/1.1 404 Not Found\nContent-Length: 136\nConnection: close\nContent-Type: text/html\n\n\n404 Not Found\n\n

Not Found

\nThe requested URL was not found on this server.\n\n",224); 62 | (void)sprintf(logbuffer,"NOT FOUND: %s:%s",s1, s2); 63 | break; 64 | // case LOG: (void)sprintf(logbuffer," INFO: %s:%s:%d",s1, s2,socket_fd); break; 65 | } 66 | /* No checks here, nothing can be done with a failure anyway */ 67 | if((fd = open("nweb.log", O_CREAT| O_WRONLY | O_APPEND,0644)) >= 0) { 68 | (void)write(fd,logbuffer,strlen(logbuffer)); 69 | (void)write(fd,"\n",1); 70 | (void)close(fd); 71 | } 72 | if(type == ERROR || type == NOTFOUND || type == FORBIDDEN) exit(3); 73 | } 74 | 75 | /* this is a child web server process, so we can exit on errors */ 76 | void web(int fd, int hit) 77 | { 78 | int j, file_fd; 79 | unsigned long buflen; 80 | long i, ret, len; 81 | char * fstr; 82 | static char buffer[BUFSIZE+1]; /* static so zero filled */ 83 | 84 | ret =read(fd,buffer,BUFSIZE); /* read Web request in one go */ 85 | if(ret == 0 || ret == -1) { /* read failure stop now */ 86 | logger(FORBIDDEN,"failed to read browser request","",fd); 87 | } 88 | if(ret > 0 && ret < BUFSIZE) /* return code is valid chars */ 89 | buffer[ret]=0; /* terminate the buffer */ 90 | else buffer[0]=0; 91 | for(i=0;i 0 ) { 144 | (void)write(fd,buffer,ret); 145 | } 146 | sleep(1); /* allow socket to drain before signalling the socket is closed */ 147 | close(fd); 148 | exit(1); 149 | } 150 | 151 | int getPort(){ 152 | struct sockaddr_in sin; 153 | int _socket, port = 0; 154 | 155 | _socket = socket(AF_INET, SOCK_STREAM, 0); 156 | if(_socket == -1) 157 | return -1; 158 | 159 | sin.sin_port = htons(port); 160 | sin.sin_addr.s_addr = 0; 161 | sin.sin_addr.s_addr = INADDR_ANY; 162 | sin.sin_family = AF_INET; 163 | 164 | if (bind(_socket, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) == -1) { 165 | if (errno == EADDRINUSE) 166 | printf("Port in use"); 167 | return -1; 168 | } 169 | 170 | struct sockaddr_in _sin; 171 | socklen_t len = sizeof(_sin); 172 | if (getsockname(_socket, (struct sockaddr *)&_sin, &len) != -1){ 173 | return (int)_sin.sin_port; 174 | } 175 | return -1; 176 | } 177 | 178 | pid_t serveFiles(char* dir, int port) 179 | { 180 | int i, pid, listenfd, socketfd, hit; 181 | socklen_t length; 182 | static struct sockaddr_in cli_addr; // static = initialised to zeros 183 | static struct sockaddr_in serv_addr; // static = initialised to zeros 184 | 185 | if( !strncmp(dir,"/" ,2 ) || !strncmp(dir,"/etc", 5 ) || 186 | !strncmp(dir,"/bin",5 ) || !strncmp(dir,"/lib", 5 ) || 187 | !strncmp(dir,"/tmp",5 ) || !strncmp(dir,"/usr", 5 ) || 188 | !strncmp(dir,"/dev",5 ) || !strncmp(dir,"/sbin",6) ){ 189 | (void)printf("ERROR: Bad top directory %s, see nweb -?\n",dir); 190 | exit(3); 191 | } 192 | if(chdir(dir) == -1){ 193 | (void)printf("ERROR: Can't Change to directory %s\n",dir); 194 | exit(4); 195 | } 196 | 197 | // Become deamon + unstopable and no zombies children (= no wait()) 198 | pid_t childPid = fork(); 199 | if(childPid != 0){ 200 | return childPid; 201 | } 202 | 203 | (void)signal(SIGCLD, SIG_IGN); // ignore child death 204 | (void)signal(SIGHUP, SIG_IGN); // ignore terminal hangups 205 | for(i=0;i<32;i++) 206 | (void)close(i); // close open files 207 | // Don't want processes to become daemons, or they will keep running after the main application closes 208 | //(void)setpgrp(); // break away from process group 209 | 210 | logger(LOG,"nweb starting", port, getpid()); 211 | 212 | // setup the network socket 213 | if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0) 214 | logger(ERROR, "system call","socket",0); 215 | 216 | if(port < 0 || port >60000) 217 | logger(ERROR,"Invalid port number (try 1->60000)",port,0); 218 | serv_addr.sin_family = AF_INET; 219 | serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 220 | serv_addr.sin_port = htons(port); 221 | if(bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0) 222 | logger(ERROR,"system call","bind",0); 223 | if( listen(listenfd,64) <0) 224 | logger(ERROR,"system call","listen",0); 225 | for(hit=1; ;hit++) { 226 | length = sizeof(cli_addr); 227 | if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0) 228 | logger(ERROR,"system call","accept",0); 229 | if((pid = fork()) < 0) { 230 | logger(ERROR,"system call","fork",0); 231 | } 232 | else { 233 | if(pid == 0) { // child 234 | (void)close(listenfd); 235 | web(socketfd,hit); // never returns 236 | } else { // parent 237 | (void)close(socketfd); 238 | } 239 | } 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /desktop/tiny.h: -------------------------------------------------------------------------------- 1 | // 2 | // tiny.h 3 | // desktop 4 | // 5 | // Created by Francois Laberge on 4/9/17. 6 | // Copyright © 2017 Mark Douma. All rights reserved. 7 | // 8 | 9 | #ifndef tiny_h 10 | #define tiny_h 11 | 12 | int getPort(); 13 | pid_t serveFiles(char* dir, int port); 14 | 15 | #endif /* tiny_h */ 16 | -------------------------------------------------------------------------------- /docs/images/drawio-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seflless/shrinkray/a1e52d92c23334488e2de3281f5927345e58c1ed/docs/images/drawio-app.png -------------------------------------------------------------------------------- /docs/images/mini-paint-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seflless/shrinkray/a1e52d92c23334488e2de3281f5927345e58c1ed/docs/images/mini-paint-app.png -------------------------------------------------------------------------------- /docs/images/regulex-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seflless/shrinkray/a1e52d92c23334488e2de3281f5927345e58c1ed/docs/images/regulex-app.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shrinkray", 3 | "version": "1.1.2", 4 | "description": "Create desktop apps using HTML/CSS/Javascript", 5 | "main": "lib/index.js", 6 | "bin": { 7 | "shrinkray": "bin/shrinkray.js" 8 | }, 9 | "scripts": { 10 | "test": "./bin/shrinkray.js -i tests/fixtures/simple -o build/hello.app", 11 | "build:app": "./tools/create-app-template.sh", 12 | "build:list": "xcodebuild -list -project desktop.xcodeproj", 13 | "publish:patch": "npm run build:app; npm version patch; git push; git push --tags; npm publish", 14 | "publish:minor": "npm run build:app; npm version minor; git push; git push --tags; npm publish", 15 | "publish:major": "npm run build:app; npm version major; git push; git push --tags; npm publish", 16 | "postinstall": "node ./bin/postinstall.js" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/francoislaberge/shrinkray.git" 21 | }, 22 | "publishConfig": { 23 | "registry": "https://registry.npmjs.org" 24 | }, 25 | "keywords": [ 26 | "Desktop", 27 | "App", 28 | "Generator", 29 | "CLI" 30 | ], 31 | "author": "Francois Laberge ", 32 | "license": "MIT", 33 | "bugs": { 34 | "url": "https://github.com/francoislaberge/shrinkray/issues" 35 | }, 36 | "homepage": "https://github.com/francoislaberge/shrinkray#readme", 37 | "dependencies": { 38 | "commander": "^2.9.0", 39 | "fs-extra": "^2.1.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/fixtures/simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple App 5 | 10 | 11 | 12 |

Hello!

13 | 14 | 15 | -------------------------------------------------------------------------------- /tools/create-app-template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Clean last build 4 | echo "Cleaning..." 5 | rm -rf build 6 | rm -rf template 7 | mkdir -p template 8 | 9 | # Build app 10 | echo "Building desktop.app..." 11 | /usr/bin/xcodebuild -target desktop -configuration Debug 12 | 13 | # Copy app to template directory 14 | echo "Copying to template..." 15 | cd build/Debug/; 16 | # Copy desktop.app to templates folder 17 | cp -R desktop.app ../../template/ 18 | # Make a zip of it too for publishing distribution 19 | zip -r -X ../../template/app.zip desktop.app 20 | --------------------------------------------------------------------------------