├── .editorconfig ├── .github ├── CODEOWNERS ├── dependabot.yml ├── labels.yml └── workflows │ ├── build.yml │ ├── labels.yml │ └── released.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets └── assets.go ├── build.properties ├── build.xml ├── go.mod ├── go.sum ├── main.go ├── res ├── Franz.lnk ├── app.ico ├── papp.ico ├── papp.manifest ├── papp.png ├── run.iss └── setup-mini.bmp └── tools └── tools.go /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs. 2 | # More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 2 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | 17 | [*.go] 18 | indent_style = tab 19 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @crazy-max 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | time: "08:00" 8 | timezone: "Europe/Paris" 9 | labels: 10 | - ":game_die: dependencies" 11 | - ":robot: bot" 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | time: "08:00" 17 | timezone: "Europe/Paris" 18 | labels: 19 | - ":game_die: dependencies" 20 | - ":robot: bot" 21 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | ## more info https://github.com/crazy-max/ghaction-github-labeler 2 | - # bot 3 | name: ":robot: bot" 4 | color: "69cde9" 5 | description: "" 6 | - # broken 7 | name: ":zap: broken" 8 | color: "a3090e" 9 | description: "" 10 | - # bug 11 | name: ":bug: bug" 12 | color: "b60205" 13 | description: "" 14 | - # dependencies 15 | name: ":game_die: dependencies" 16 | color: "0366d6" 17 | description: "" 18 | - # documentation 19 | name: ":memo: documentation" 20 | color: "c5def5" 21 | description: "" 22 | - # duplicate 23 | name: ":busts_in_silhouette: duplicate" 24 | color: "cccccc" 25 | description: "" 26 | - # enhancement 27 | name: ":sparkles: enhancement" 28 | color: "0054ca" 29 | description: "" 30 | - # feature request 31 | name: ":bulb: feature request" 32 | color: "0e8a16" 33 | description: "" 34 | - # feedback 35 | name: ":mega: feedback" 36 | color: "03a9f4" 37 | description: "" 38 | - # future maybe 39 | name: ":rocket: future maybe" 40 | color: "fef2c0" 41 | description: "" 42 | - # good first issue 43 | name: ":hatching_chick: good first issue" 44 | color: "7057ff" 45 | description: "" 46 | - # help wanted 47 | name: ":pray: help wanted" 48 | color: "4caf50" 49 | description: "" 50 | - # hold 51 | name: ":hand: hold" 52 | color: "24292f" 53 | description: "" 54 | - # invalid 55 | name: ":no_entry_sign: invalid" 56 | color: "e6e6e6" 57 | description: "" 58 | - # maybe bug 59 | name: ":interrobang: maybe bug" 60 | color: "ff5722" 61 | description: "" 62 | - # needs more info 63 | name: ":thinking: needs more info" 64 | color: "795548" 65 | description: "" 66 | - # question 67 | name: ":question: question" 68 | color: "3f51b5" 69 | description: "" 70 | - # trademark violation 71 | name: ":construction: trademark violation" 72 | color: "cfe524" 73 | description: "" 74 | - # upstream 75 | name: ":eyes: upstream" 76 | color: "fbca04" 77 | description: "" 78 | - # wontfix 79 | name: ":coffin: wontfix" 80 | color: "ffffff" 81 | description: "" 82 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - '*' 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | uses: portapps/.github/.github/workflows/app-build.yml@master 13 | -------------------------------------------------------------------------------- /.github/workflows/labels.yml: -------------------------------------------------------------------------------- 1 | name: labels 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | paths: 8 | - '.github/labels.yml' 9 | - '.github/workflows/labels.yml' 10 | 11 | jobs: 12 | labeler: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - 16 | name: Checkout 17 | uses: actions/checkout@v4 18 | - 19 | name: Run Labeler 20 | uses: crazy-max/ghaction-github-labeler@v5 21 | -------------------------------------------------------------------------------- /.github/workflows/released.yml: -------------------------------------------------------------------------------- 1 | name: released 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | virustotal: 9 | uses: portapps/.github/.github/workflows/app-virustotal.yml@master 10 | secrets: 11 | vt_api_key: ${{ secrets.VT_API_KEY }} 12 | vt_monitor_api_key: ${{ secrets.VT_MONITOR_API_KEY }} 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Intellij 2 | /.idea 3 | /*.iml 4 | 5 | # App 6 | /.dev 7 | /bin 8 | /vendor 9 | /*.syso 10 | /*.exe 11 | /versioninfo.json 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 5.9.1-12 (2021/05/08) 4 | 5 | * Franz 5.9.1 6 | * Portapps 3.5.0 7 | 8 | ## 5.6.1-11 (2021/01/06) 9 | 10 | * Franz 5.6.1 11 | * Portapps 3.1.0 12 | 13 | ## 5.5.0-10 (2020/05/07) 14 | 15 | * Franz 5.5.0 16 | * Portapps 2.2.4 17 | 18 | ## 5.4.1-9 (2020/02/01) 19 | 20 | * Add `cleanup` config 21 | * Portapps 1.31.0 22 | 23 | ## 5.4.1-8 (2019/11/03) 24 | 25 | * Franz 5.4.1 26 | 27 | ## 5.4.0-7 (2019/10/23) 28 | 29 | * Franz 5.4.0 30 | * Franz is now based on Electron 6 31 | * Portapps 1.28.0 32 | 33 | ## 5.3.3-6 (2019/09/17) 34 | 35 | * Franz 5.3.3 36 | 37 | ## 5.3.1-5 (2019/09/09) 38 | 39 | * Franz 5.3.1 40 | * Portapps 1.26.1 41 | 42 | ## 5.2.0-4 (2019/07/24) 43 | 44 | * Add option to disable delay (Issue #2) 45 | 46 | ## 5.2.0-3 (2019/07/21) 47 | 48 | * Franz 5.2.0 49 | 50 | ## 5.1.0-2 (2019/05/17) 51 | 52 | * Disable launch on startup (Issue #1) 53 | 54 | ## 5.1.0-1 (2019/05/08) 55 | 56 | * Initial version based on Franz 5.1.0 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2023 CrazyMax 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | GitHub release 5 | Total downloads 6 | Build Status 7 | Go Report 8 |
Become a sponsor 9 | Donate Paypal 10 |

11 | 12 | ## Notice of Non-Affiliation and Disclaimer 13 | 14 | Portapps is not affiliated, associated, authorized, endorsed by, or in any way officially connected with Franz™, or any of its subsidiaries or its affiliates. 15 | 16 | The official Franz™ website can be found at https://meetfranz.com/. 17 | 18 | The name Franz™ as well as related names, marks, emblems and images are registered trademarks of their respective owners. 19 | 20 | ## About 21 | 22 | Franz™ portable app made with 🚀 [Portapps](https://portapps.io).
23 | Documentation and downloads can be found on https://portapps.io/app/franz-portable/ 24 | 25 | ## Contributing 26 | 27 | Want to contribute? Awesome! The most basic way to show your support is to star the project, or to raise issues. If 28 | you want to open a pull request, please read the [contributing guidelines](https://portapps.io/doc/contribute/). 29 | 30 | You can also support this project by [**becoming a sponsor on GitHub**](https://github.com/sponsors/crazy-max) or by 31 | making a [Paypal donation](https://www.paypal.me/crazyws) to ensure this journey continues indefinitely! 32 | 33 | Thanks again for your support, it is much appreciated! :pray: 34 | 35 | ## License 36 | 37 | MIT. See `LICENSE` for more details.
38 | Rocket icon credit to [Squid Ink](http://thesquid.ink). 39 | -------------------------------------------------------------------------------- /assets/assets.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-bindata. DO NOT EDIT. 2 | // sources: 3 | // res/Franz.lnk (2.371kB) 4 | 5 | package assets 6 | 7 | import ( 8 | "bytes" 9 | "compress/gzip" 10 | "crypto/sha256" 11 | "fmt" 12 | "io" 13 | "os" 14 | "path/filepath" 15 | "strings" 16 | "time" 17 | ) 18 | 19 | func bindataRead(data []byte, name string) ([]byte, error) { 20 | gz, err := gzip.NewReader(bytes.NewBuffer(data)) 21 | if err != nil { 22 | return nil, fmt.Errorf("read %q: %w", name, err) 23 | } 24 | 25 | var buf bytes.Buffer 26 | _, err = io.Copy(&buf, gz) 27 | clErr := gz.Close() 28 | 29 | if err != nil { 30 | return nil, fmt.Errorf("read %q: %w", name, err) 31 | } 32 | if clErr != nil { 33 | return nil, err 34 | } 35 | 36 | return buf.Bytes(), nil 37 | } 38 | 39 | type asset struct { 40 | bytes []byte 41 | info os.FileInfo 42 | digest [sha256.Size]byte 43 | } 44 | 45 | type bindataFileInfo struct { 46 | name string 47 | size int64 48 | mode os.FileMode 49 | modTime time.Time 50 | } 51 | 52 | func (fi bindataFileInfo) Name() string { 53 | return fi.name 54 | } 55 | func (fi bindataFileInfo) Size() int64 { 56 | return fi.size 57 | } 58 | func (fi bindataFileInfo) Mode() os.FileMode { 59 | return fi.mode 60 | } 61 | func (fi bindataFileInfo) ModTime() time.Time { 62 | return fi.modTime 63 | } 64 | func (fi bindataFileInfo) IsDir() bool { 65 | return false 66 | } 67 | func (fi bindataFileInfo) Sys() interface{} { 68 | return nil 69 | } 70 | 71 | var _franzLnk = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x95\x6f\x68\x5b\xd5\x1b\xc7\x3f\xb7\x7f\x18\xdd\x5e\xac\xfb\xfd\xca\xd4\x52\x69\x50\x5b\x26\x9a\x4b\xda\x25\x6d\x93\x29\xb6\xe6\x4f\x53\x9b\xae\x31\x59\xb4\x1b\xd7\xe2\x25\xbd\x4d\x4b\x9a\x26\xbb\xc9\xb0\x89\xa2\x74\xa0\xb3\xc3\x29\x38\xfc\x33\x7d\xd1\x0e\x84\x55\xd0\xa2\x22\x0a\x6e\x8e\xbe\xb0\x08\xa2\x43\xb1\x7d\x53\xa1\x65\x03\x05\x45\x57\xd8\x10\xa7\x8e\x21\xe7\x24\x8d\x69\x2c\x28\xa8\x20\xe2\x03\xc9\xf9\xf7\x9c\xcf\xf3\xbd\xcf\x73\xee\x3d\x01\x40\xa9\xab\x40\xd8\xbc\xfc\xc7\xb7\xda\x09\x16\x60\xf6\xca\x43\x5b\x97\xab\x97\x94\x95\xe5\xc1\x6d\xa2\x65\xc7\xd3\x57\x26\x7f\x5e\x54\x9e\x6c\x7f\xa9\x5a\x38\x2a\x6c\xb4\xba\x0a\x17\x8d\x9e\xee\xfa\xca\xfd\xe6\x5d\xa7\x3c\x53\x0b\x91\xe7\xae\xc5\x6d\x17\x9b\x51\x68\x66\xed\x6c\x2d\xe0\xba\x56\xad\x5e\x78\x7c\x51\xb9\xf4\xc1\xc8\xb7\x2b\xca\x52\xb1\xad\xe3\x30\x19\x1a\x70\xfb\xc2\xbe\x9d\xb4\x48\x5a\xae\x6f\xfe\xfd\xff\xd1\x95\x4a\x79\xf4\x8c\x0e\x64\xea\xf7\x0f\x3e\xbf\xfa\xb9\xff\x58\xec\xba\xdd\x67\x2e\xee\x7a\x6f\xe1\x93\x9f\x56\xa7\x63\xf7\x75\x4d\x75\x2f\x7c\x18\x9f\x7f\xe7\xb1\x4e\x6a\xa8\x62\xed\xac\xd8\x27\x7e\x2a\xe0\xed\x17\x1a\x2b\xcb\x74\xbe\xfb\x72\x8d\xd2\x45\x8a\x14\x1e\x74\x32\x08\xfa\xdd\x04\x0b\x71\x67\xf7\x76\x9f\xac\x25\x90\x8c\xea\x63\xdc\x51\xc2\x14\xf3\x82\x39\xb0\x29\x73\x70\x4f\x9c\x00\x49\xa2\xe8\x8c\x89\x5c\x70\xa0\xc8\xf3\x9e\xac\x25\x68\x26\x63\xa6\x9e\x48\x8b\x48\x79\xe6\xc3\x7d\x7b\xde\x10\x6b\x82\x69\xbf\x57\x78\x56\x94\xe7\xf3\xab\x0a\x25\x88\x49\x92\x18\x26\x3a\x09\xd2\xc0\xf5\x65\x4a\x87\x4d\x7d\x3c\x57\x54\x2a\x88\xeb\x4a\xbf\x79\x40\x10\x6b\xcb\xa8\xee\xc3\x37\x31\x2c\x79\xe3\xe4\xa4\x52\x8d\x56\x44\x4d\x4f\xec\xfd\x6c\xd2\x82\x4f\xf0\x54\x63\xc2\xc0\x53\x64\xfa\x8a\xcc\xed\xdf\x8b\xd2\x57\xf3\x5b\xf3\x15\x99\x2a\x06\x13\x18\x52\x2b\x32\x1b\x0d\x85\xd3\x22\x5a\x47\xc1\x3f\x0e\xdc\x50\xc8\xa4\xfb\x74\x83\x26\x74\x7a\x46\x6f\x39\x78\xc8\xb0\xb8\x71\xbb\xb4\x48\xda\x30\xd3\x5a\xd4\xd4\x73\x59\xad\x70\x0a\x34\x59\x15\x6d\x3d\x97\x9a\x7c\x74\xed\x57\xc1\x84\xe8\xc3\x20\x4d\x1a\x9d\x18\xa3\x8c\x13\xc3\x82\x2e\x6b\x6d\x61\x98\x24\x26\x16\xee\x67\x44\xd6\x3d\x4d\xfe\x14\xdc\x8e\x85\x30\x63\xe8\x44\x89\xcb\xd1\x3e\x0c\xc6\x30\x8a\x79\x17\x73\x7e\x46\x49\xe1\x2e\xec\xcd\xcf\xe8\x32\x42\x92\x43\x92\x66\x91\xe3\x21\x2c\x24\x64\x2f\x5b\xd6\x13\xd1\x0d\x54\x6e\x45\x45\x45\x63\xe3\x19\xd4\x4a\xce\x8f\x46\x79\xdd\xb5\x92\x9a\x69\x9b\xe4\xfa\x36\xdc\xb8\xd0\x88\x90\xc6\xc0\x94\x3b\xa2\xd2\x2b\x47\xf6\x4f\xc4\x72\xfc\x4d\xdc\xcd\x9e\xa1\xae\x12\xb6\xc0\x4c\x53\x24\xec\x0d\x05\x43\xfd\xbe\x9e\x80\xb7\xe9\x0f\xd7\xfe\xdf\x65\x4d\x44\x08\xe3\x25\x44\x90\x10\xfd\xf8\xe8\x21\x80\x97\xa6\xbf\x38\xe7\xff\xd9\x3f\xdb\xc4\x17\x73\x1b\xcc\x3c\xb2\xfd\xdc\x65\xa5\xe7\xe3\x7b\x9e\xf0\x2f\x2d\xf6\x56\xad\x3d\xe3\x02\x1e\xcc\x7f\x3d\x67\x06\x0a\xbe\xa9\xa8\x75\x44\x1f\x32\xd2\xeb\x7b\x87\xe6\xe6\x32\xd9\x4b\xaf\x79\x9f\x9d\xfc\xf2\xd5\xe5\xd9\x23\x55\x3f\x84\xaf\x1e\x78\xfb\xe0\xd7\x3b\x4e\x1c\xdf\xf7\x56\xe5\xd6\xa3\x81\xdf\x5b\xbf\x59\x81\x1a\x98\x99\x02\x5a\xc2\xc1\xf0\x85\xa3\x03\xbe\x33\x81\x0e\xf7\xe9\xab\xff\x3f\xde\xfc\x62\xe2\x5c\x02\xa8\x12\x81\x1a\x01\x71\x39\x84\xb1\xd2\x82\x15\x07\x56\x5a\x65\x6f\x37\xed\x38\xb1\x63\xc7\x89\x93\x36\x3a\x68\x93\x6b\x4e\x1c\xd8\xb0\xcb\x71\x2b\x1d\xd2\xd3\x81\x1d\x1b\x36\x5a\x68\x97\x3b\xda\x24\x2d\x3f\x93\xb7\x48\x41\x48\x64\x57\x60\x3a\x3b\xed\xec\x9d\xfd\xf4\xfc\xa2\xf5\xfc\xd2\x65\xe7\xfa\x95\xd4\x28\xef\x33\x88\x92\x24\x81\x4a\x02\x03\x83\x4c\xc9\x3b\xa0\x6e\xb8\xfb\x84\x39\x0b\xd0\x37\x77\x26\x3c\xaf\x1f\x4b\xf9\x4f\xf9\x3b\xd5\x57\xee\x9c\x78\xea\x46\x60\x44\x38\xf8\x81\xd5\x95\xfa\xef\x8e\xfc\xb8\xe6\x7f\x61\xee\x8b\xde\x2d\x1f\x3d\x6a\x2b\x2d\xd1\x2f\x01\x00\x00\xff\xff\x27\xd0\x90\x1e\x43\x09\x00\x00") 72 | 73 | func franzLnkBytes() ([]byte, error) { 74 | return bindataRead( 75 | _franzLnk, 76 | "Franz.lnk", 77 | ) 78 | } 79 | 80 | func franzLnk() (*asset, error) { 81 | bytes, err := franzLnkBytes() 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | info := bindataFileInfo{name: "Franz.lnk", size: 2371, mode: os.FileMode(0666), modTime: time.Unix(1557346214, 0)} 87 | a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x20, 0xa4, 0x6, 0x39, 0x2c, 0x53, 0xf9, 0x4a, 0x54, 0x22, 0x3d, 0x9a, 0x78, 0x61, 0xb0, 0x38, 0x9b, 0x68, 0xb, 0x62, 0xdb, 0xc, 0xfb, 0xeb, 0x90, 0x2c, 0x79, 0x77, 0xc6, 0xd6, 0x67, 0x80}} 88 | return a, nil 89 | } 90 | 91 | // Asset loads and returns the asset for the given name. 92 | // It returns an error if the asset could not be found or 93 | // could not be loaded. 94 | func Asset(name string) ([]byte, error) { 95 | canonicalName := strings.Replace(name, "\\", "/", -1) 96 | if f, ok := _bindata[canonicalName]; ok { 97 | a, err := f() 98 | if err != nil { 99 | return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) 100 | } 101 | return a.bytes, nil 102 | } 103 | return nil, fmt.Errorf("Asset %s not found", name) 104 | } 105 | 106 | // AssetString returns the asset contents as a string (instead of a []byte). 107 | func AssetString(name string) (string, error) { 108 | data, err := Asset(name) 109 | return string(data), err 110 | } 111 | 112 | // MustAsset is like Asset but panics when Asset would return an error. 113 | // It simplifies safe initialization of global variables. 114 | func MustAsset(name string) []byte { 115 | a, err := Asset(name) 116 | if err != nil { 117 | panic("asset: Asset(" + name + "): " + err.Error()) 118 | } 119 | 120 | return a 121 | } 122 | 123 | // MustAssetString is like AssetString but panics when Asset would return an 124 | // error. It simplifies safe initialization of global variables. 125 | func MustAssetString(name string) string { 126 | return string(MustAsset(name)) 127 | } 128 | 129 | // AssetInfo loads and returns the asset info for the given name. 130 | // It returns an error if the asset could not be found or 131 | // could not be loaded. 132 | func AssetInfo(name string) (os.FileInfo, error) { 133 | canonicalName := strings.Replace(name, "\\", "/", -1) 134 | if f, ok := _bindata[canonicalName]; ok { 135 | a, err := f() 136 | if err != nil { 137 | return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) 138 | } 139 | return a.info, nil 140 | } 141 | return nil, fmt.Errorf("AssetInfo %s not found", name) 142 | } 143 | 144 | // AssetDigest returns the digest of the file with the given name. It returns an 145 | // error if the asset could not be found or the digest could not be loaded. 146 | func AssetDigest(name string) ([sha256.Size]byte, error) { 147 | canonicalName := strings.Replace(name, "\\", "/", -1) 148 | if f, ok := _bindata[canonicalName]; ok { 149 | a, err := f() 150 | if err != nil { 151 | return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) 152 | } 153 | return a.digest, nil 154 | } 155 | return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) 156 | } 157 | 158 | // Digests returns a map of all known files and their checksums. 159 | func Digests() (map[string][sha256.Size]byte, error) { 160 | mp := make(map[string][sha256.Size]byte, len(_bindata)) 161 | for name := range _bindata { 162 | a, err := _bindata[name]() 163 | if err != nil { 164 | return nil, err 165 | } 166 | mp[name] = a.digest 167 | } 168 | return mp, nil 169 | } 170 | 171 | // AssetNames returns the names of the assets. 172 | func AssetNames() []string { 173 | names := make([]string, 0, len(_bindata)) 174 | for name := range _bindata { 175 | names = append(names, name) 176 | } 177 | return names 178 | } 179 | 180 | // _bindata is a table, holding each asset generator, mapped to its name. 181 | var _bindata = map[string]func() (*asset, error){ 182 | "Franz.lnk": franzLnk, 183 | } 184 | 185 | // AssetDebug is true if the assets were built with the debug flag enabled. 186 | const AssetDebug = false 187 | 188 | // AssetDir returns the file names below a certain 189 | // directory embedded in the file by go-bindata. 190 | // For example if you run go-bindata on data/... and data contains the 191 | // following hierarchy: 192 | // 193 | // data/ 194 | // foo.txt 195 | // img/ 196 | // a.png 197 | // b.png 198 | // 199 | // then AssetDir("data") would return []string{"foo.txt", "img"}, 200 | // AssetDir("data/img") would return []string{"a.png", "b.png"}, 201 | // AssetDir("foo.txt") and AssetDir("notexist") would return an error, and 202 | // AssetDir("") will return []string{"data"}. 203 | func AssetDir(name string) ([]string, error) { 204 | node := _bintree 205 | if len(name) != 0 { 206 | canonicalName := strings.Replace(name, "\\", "/", -1) 207 | pathList := strings.Split(canonicalName, "/") 208 | for _, p := range pathList { 209 | node = node.Children[p] 210 | if node == nil { 211 | return nil, fmt.Errorf("Asset %s not found", name) 212 | } 213 | } 214 | } 215 | if node.Func != nil { 216 | return nil, fmt.Errorf("Asset %s not found", name) 217 | } 218 | rv := make([]string, 0, len(node.Children)) 219 | for childName := range node.Children { 220 | rv = append(rv, childName) 221 | } 222 | return rv, nil 223 | } 224 | 225 | type bintree struct { 226 | Func func() (*asset, error) 227 | Children map[string]*bintree 228 | } 229 | 230 | var _bintree = &bintree{nil, map[string]*bintree{ 231 | "Franz.lnk": {franzLnk, map[string]*bintree{}}, 232 | }} 233 | 234 | // RestoreAsset restores an asset under the given directory. 235 | func RestoreAsset(dir, name string) error { 236 | data, err := Asset(name) 237 | if err != nil { 238 | return err 239 | } 240 | info, err := AssetInfo(name) 241 | if err != nil { 242 | return err 243 | } 244 | err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) 245 | if err != nil { 246 | return err 247 | } 248 | err = os.WriteFile(_filePath(dir, name), data, info.Mode()) 249 | if err != nil { 250 | return err 251 | } 252 | return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) 253 | } 254 | 255 | // RestoreAssets restores an asset under the given directory recursively. 256 | func RestoreAssets(dir, name string) error { 257 | children, err := AssetDir(name) 258 | // File 259 | if err != nil { 260 | return RestoreAsset(dir, name) 261 | } 262 | // Dir 263 | for _, child := range children { 264 | err = RestoreAssets(dir, filepath.Join(name, child)) 265 | if err != nil { 266 | return err 267 | } 268 | } 269 | return nil 270 | } 271 | 272 | func _filePath(dir, name string) string { 273 | canonicalName := strings.Replace(name, "\\", "/", -1) 274 | return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) 275 | } 276 | -------------------------------------------------------------------------------- /build.properties: -------------------------------------------------------------------------------- 1 | # Portapps 2 | core.dir = ../portapps 3 | 4 | # App 5 | app = franz 6 | app.name = Franz 7 | app.type = archive 8 | app.version = 5.9.1 9 | app.release = 12 10 | app.homepage = https://meetfranz.com/ 11 | 12 | # Portable app 13 | papp.id = ${app}-portable 14 | papp.guid = {3AD7920B-9BE4-4E5F-BB6B-D79A4F913D8B} 15 | papp.name = ${app.name} Portable 16 | papp.desc = ${app.name} portable on Windows by Portapps 17 | papp.url = https://github.com/portapps/${papp.id} 18 | papp.folder = app 19 | 20 | # Electron 21 | electron.appasar.file = electron/ipc-api/index.js 22 | electron.appasar.search = var _autoUpdate=_interopRequireDefault(require("./autoUpdate")), 23 | electron.appasar.replace = var 24 | electron.appasar.search2 = ,(0,_autoUpdate.default)(e), 25 | electron.appasar.replace2 = , 26 | electron.appasar2.file = components/settings/settings/EditSettingsForm.js 27 | electron.appasar2.search = field:t.$("autoLaunchOnStart") 28 | electron.appasar2.replace = field:t.$("autoLaunchOnStart"),disabled:true 29 | electron.appasar3.file = containers/settings/EditSettingsScreen.js 30 | electron.appasar3.search = enable:e.autoLaunchOnStart 31 | electron.appasar3.replace = enable:false 32 | electron.appasar4.file = features/delayApp/index.js 33 | electron.appasar4.search = if(t){ 34 | electron.appasar4.replace = if(t && process.env.FRANZ_DELAY == "true"){ 35 | 36 | # Official artifacts 37 | atf.id = franz 38 | atf.win32.filename = franz-setup-${app.version} 39 | atf.win32.ext = .exe 40 | atf.win32.url = https://github.com/meetfranz/franz/releases/download/v${app.version}/franz-setup-${app.version}.exe 41 | atf.win32.assertextract = $PLUGINSDIR/app-32.7z 42 | atf.win64.filename = franz-setup-${app.version} 43 | atf.win64.ext = .exe 44 | atf.win64.url = https://github.com/meetfranz/franz/releases/download/v${app.version}/franz-setup-${app.version}.exe 45 | atf.win64.assertextract = $PLUGINSDIR/app-64.7z 46 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/portapps/franz-portable 2 | 3 | go 1.23 4 | 5 | require ( 6 | github.com/kevinburke/go-bindata/v4 v4.0.2 7 | github.com/portapps/portapps/v3 v3.14.1 8 | ) 9 | 10 | require ( 11 | github.com/akavel/rsrc v0.10.2 // indirect 12 | github.com/go-ole/go-ole v1.3.0 // indirect 13 | github.com/go-viper/mapstructure/v2 v2.2.1 // indirect 14 | github.com/ilya1st/rotatewriter v0.0.0-20171126183947-3df0c1a3ed6d // indirect 15 | github.com/josephspurrier/goversioninfo v1.4.1 // indirect 16 | github.com/mattn/go-colorable v0.1.13 // indirect 17 | github.com/mattn/go-isatty v0.0.19 // indirect 18 | github.com/pkg/errors v0.9.1 // indirect 19 | github.com/rs/zerolog v1.33.0 // indirect 20 | golang.org/x/sys v0.27.0 // indirect 21 | gopkg.in/yaml.v3 v3.0.1 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw= 2 | github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 3 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= 8 | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= 9 | github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= 10 | github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 11 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 12 | github.com/ilya1st/rotatewriter v0.0.0-20171126183947-3df0c1a3ed6d h1:OGuVAVny/97zsQ5BWg0mOjzTBBD9zR+Lug1co144+rU= 13 | github.com/ilya1st/rotatewriter v0.0.0-20171126183947-3df0c1a3ed6d/go.mod h1:S1q6q+21PRGd0WRX+fHjQ+TOe3CgpSv7zgCWnZcbxCs= 14 | github.com/josephspurrier/goversioninfo v1.4.1 h1:5LvrkP+n0tg91J9yTkoVnt/QgNnrI1t4uSsWjIonrqY= 15 | github.com/josephspurrier/goversioninfo v1.4.1/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY= 16 | github.com/kevinburke/go-bindata/v4 v4.0.2 h1:6qQI0nNTL27wM1En8zQHGBEPp3ETzgFU6hVdSjlkrfE= 17 | github.com/kevinburke/go-bindata/v4 v4.0.2/go.mod h1:M/CkBqw2qCZ1Ztv5JyKgocGYWyUkYlDqkqXS1ktLe5c= 18 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 19 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 20 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 21 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 22 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 23 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 24 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 25 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 26 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 27 | github.com/portapps/portapps/v3 v3.14.1 h1:/MD+QBSq64KIg82hZ/hMQHxW+UZ/Th9TWMRkY5danfY= 28 | github.com/portapps/portapps/v3 v3.14.1/go.mod h1:PJTha9WxFuumcnCd+TjHDdam5wH5b5akiKyF/HZieco= 29 | github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 30 | github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= 31 | github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= 32 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 33 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 34 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 35 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 36 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 37 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 38 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 39 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 40 | golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= 41 | golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 42 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 43 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 44 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 45 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 46 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 47 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | //go:generate go install -v github.com/kevinburke/go-bindata/v4/go-bindata 2 | //go:generate go-bindata -prefix res/ -pkg assets -o assets/assets.go res/Franz.lnk 3 | //go:generate go install -v github.com/josephspurrier/goversioninfo/cmd/goversioninfo 4 | //go:generate goversioninfo -icon=res/papp.ico -manifest=res/papp.manifest 5 | package main 6 | 7 | import ( 8 | "os" 9 | "path" 10 | 11 | "github.com/portapps/franz-portable/assets" 12 | "github.com/portapps/portapps/v3" 13 | "github.com/portapps/portapps/v3/pkg/log" 14 | "github.com/portapps/portapps/v3/pkg/shortcut" 15 | "github.com/portapps/portapps/v3/pkg/utl" 16 | ) 17 | 18 | type config struct { 19 | Cleanup bool `yaml:"cleanup" mapstructure:"cleanup"` 20 | DisableDelay bool `yaml:"disable_delay" mapstructure:"disable_delay"` 21 | } 22 | 23 | var ( 24 | app *portapps.App 25 | cfg *config 26 | ) 27 | 28 | func init() { 29 | var err error 30 | 31 | // Default config 32 | cfg = &config{ 33 | Cleanup: false, 34 | DisableDelay: false, 35 | } 36 | 37 | // Init app 38 | if app, err = portapps.NewWithCfg("franz-portable", "Franz", cfg); err != nil { 39 | log.Fatal().Err(err).Msg("Cannot initialize application. See log file for more info.") 40 | } 41 | } 42 | 43 | func main() { 44 | utl.CreateFolder(app.DataPath) 45 | app.Process = utl.PathJoin(app.AppPath, "Franz.exe") 46 | app.Args = []string{ 47 | "--user-data-dir=" + app.DataPath, 48 | } 49 | 50 | // Data dir 51 | os.Setenv("FRANZ_APPDATA_DIR", app.DataPath) 52 | 53 | // Cleanup on exit 54 | if cfg.Cleanup { 55 | defer func() { 56 | utl.Cleanup([]string{ 57 | path.Join(os.Getenv("APPDATA"), "Franz"), 58 | }) 59 | }() 60 | } 61 | 62 | // Copy default shortcut 63 | shortcutPath := path.Join(os.Getenv("APPDATA"), "Microsoft", "Windows", "Start Menu", "Programs", "Franz Portable.lnk") 64 | defaultShortcut, err := assets.Asset("Franz.lnk") 65 | if err != nil { 66 | log.Error().Err(err).Msg("Cannot load asset Franz.lnk") 67 | } 68 | err = os.WriteFile(shortcutPath, defaultShortcut, 0644) 69 | if err != nil { 70 | log.Error().Err(err).Msg("Cannot write default shortcut") 71 | } 72 | 73 | // Check delay 74 | if cfg.DisableDelay { 75 | os.Setenv("FRANZ_DELAY", "false") 76 | } else { 77 | os.Setenv("FRANZ_DELAY", "true") 78 | } 79 | 80 | // Update default shortcut 81 | err = shortcut.Create(shortcut.Shortcut{ 82 | ShortcutPath: shortcutPath, 83 | TargetPath: app.Process, 84 | Arguments: shortcut.Property{Clear: true}, 85 | Description: shortcut.Property{Value: "Franz Portable by Portapps"}, 86 | IconLocation: shortcut.Property{Value: app.Process}, 87 | WorkingDirectory: shortcut.Property{Value: app.AppPath}, 88 | }) 89 | if err != nil { 90 | log.Error().Err(err).Msg("Cannot create shortcut") 91 | } 92 | defer func() { 93 | if err := os.Remove(shortcutPath); err != nil { 94 | log.Error().Err(err).Msg("Cannot remove shortcut") 95 | } 96 | }() 97 | 98 | defer app.Close() 99 | app.Launch(os.Args[1:]) 100 | } 101 | -------------------------------------------------------------------------------- /res/Franz.lnk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/portapps/franz-portable/b00868fb5875ecfe168a8ae160d4c1a7f0d62142/res/Franz.lnk -------------------------------------------------------------------------------- /res/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/portapps/franz-portable/b00868fb5875ecfe168a8ae160d4c1a7f0d62142/res/app.ico -------------------------------------------------------------------------------- /res/papp.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/portapps/franz-portable/b00868fb5875ecfe168a8ae160d4c1a7f0d62142/res/papp.ico -------------------------------------------------------------------------------- /res/papp.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /res/papp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/portapps/franz-portable/b00868fb5875ecfe168a8ae160d4c1a7f0d62142/res/papp.png -------------------------------------------------------------------------------- /res/run.iss: -------------------------------------------------------------------------------- 1 | [Run] 2 | Filename: {app}\{#pappId}.exe; Description: Run {#pappName}; Flags: nowait postinstall skipifsilent unchecked 3 | -------------------------------------------------------------------------------- /res/setup-mini.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/portapps/franz-portable/b00868fb5875ecfe168a8ae160d4c1a7f0d62142/res/setup-mini.bmp -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | // Package tools tracks dependencies on binaries not referenced in this codebase. 5 | // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 6 | package tools 7 | 8 | import ( 9 | _ "github.com/kevinburke/go-bindata/v4" 10 | _ "github.com/portapps/portapps/v3/tools" 11 | ) 12 | --------------------------------------------------------------------------------