56 |
--------------------------------------------------------------------------------
/assets/img/clickpress_logo_weiss.min.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/css/spectre.scss:
--------------------------------------------------------------------------------
1 | // Variables and mixins
2 | @import "variables";
3 |
4 | @import "../../node_modules/spectre.css/src/mixins";
5 |
6 | /*! Spectre.css v#{$version} | MIT License | github.com/picturepan2/spectre */
7 | // Reset and dependencies
8 | @import "../../node_modules/spectre.css/src/normalize";
9 | @import "../../node_modules/spectre.css/src/base";
10 |
11 | // Elements
12 | @import "../../node_modules/spectre.css/src/typography";
13 | // @import "../../node_modules/spectre.css/src/asian";
14 | // @import "../../node_modules/spectre.css/src/tables";
15 | @import "../../node_modules/spectre.css/src/buttons";
16 | @import "../../node_modules/spectre.css/src/forms";
17 | @import "../../node_modules/spectre.css/src/labels";
18 | // @import "../../node_modules/spectre.css/src/codes";
19 | // @import "../../node_modules/spectre.css/src/media";
20 |
21 | // Layout
22 | @import "../../node_modules/spectre.css/src/layout";
23 | //@import "../../node_modules/spectre.css/src/navbar";
24 |
25 | // Components
26 | //@import "../../node_modules/spectre.css/src/accordions";
27 | //@import "../../node_modules/spectre.css/src/autocomplete";
28 | //@import "../../node_modules/spectre.css/src/avatars";
29 | //@import "../../node_modules/spectre.css/src/badges";
30 | //@import "../../node_modules/spectre.css/src/breadcrumbs";
31 | //@import "../../node_modules/spectre.css/src/bars";
32 | //@import "../../node_modules/spectre.css/src/cards";
33 | //@import "../../node_modules/spectre.css/src/chips";
34 | //@import "../../node_modules/spectre.css/src/dropdowns";
35 | //@import "../../node_modules/spectre.css/src/empty";
36 | //@import "../../node_modules/spectre.css/src/menus";
37 | //@import "../../node_modules/spectre.css/src/modals";
38 | //@import "../../node_modules/spectre.css/src/navs";
39 | //@import "../../node_modules/spectre.css/src/pagination";
40 | //@import "../../node_modules/spectre.css/src/panels";
41 | //@import "../../node_modules/spectre.css/src/popovers";
42 | //@import "../../node_modules/spectre.css/src/steps";
43 | //@import "../../node_modules/spectre.css/src/tabs";
44 | //@import "../../node_modules/spectre.css/src/tiles";
45 | //@import "../../node_modules/spectre.css/src/toasts";
46 | //@import "../../node_modules/spectre.css/src/tooltips";
47 |
48 | // Utility classes
49 | @import "../../node_modules/spectre.css/src/animations";
50 | @import "../../node_modules/spectre.css/src/utilities";
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "image-shrinker",
3 | "version": "1.6.5",
4 | "description": "Minify your images and graphics with just one drop",
5 | "main": "main.js",
6 | "title": "Image Shrinker",
7 | "productName": "Image Shrinker",
8 | "icon": "build/icon.icns",
9 | "background": "test-background.png",
10 | "target": "mac",
11 | "contents": [
12 | {
13 | "x": 448,
14 | "y": 344,
15 | "type": "link",
16 | "path": "/Applications"
17 | },
18 | {
19 | "x": 192,
20 | "y": 344,
21 | "type": "file",
22 | "path": "ImageShrinker.app"
23 | }
24 | ],
25 | "scripts": {
26 | "start": "electron .",
27 | "build": "electron-builder build --dir",
28 | "build-mac": "electron-builder build --mac",
29 | "build-linux": "electron-builder build --linux",
30 | "build-win": "electron-builder build --win",
31 | "build-all": "electron-builder -mwl",
32 | "publish": "electron-builder build -p always"
33 | },
34 | "repository": "https://github.com/stefansl/image-shrinker",
35 | "keywords": [
36 | "SVG",
37 | "svgo",
38 | "SVG Shrinking",
39 | "shrinking tool",
40 | "image minifying",
41 | "image",
42 | "png",
43 | "jpg",
44 | "minifying"
45 | ],
46 | "author": "Stefan Schulz-Lauterbach",
47 | "license": "CC0-1.0",
48 | "devDependencies": {
49 | "electron": "^11.2.1",
50 | "electron-builder": "^22.9.1",
51 | "electron-installer-dmg": "^3.0.0",
52 | "electron-notarize": "^1.0.0",
53 | "fs-extra": "^9.1.0"
54 | },
55 | "dependencies": {
56 | "dotenv": "^8.2.0",
57 | "electron-log": "^4.2.2",
58 | "electron-settings": "^4.0.2",
59 | "electron-updater": "^4.3.1",
60 | "gifsicle": "^5.1.0",
61 | "make-dir": "^3.1.0",
62 | "mozjpeg": "^7.0.0",
63 | "pngquant-bin": "^6.0.0",
64 | "spectre.css": "^0.5.9",
65 | "svgo": "^1.3.0"
66 | },
67 | "build": {
68 | "appId": "de.clickpress.image-shrinker",
69 | "afterSign": "lib/notarize.js",
70 | "mac": {
71 | "category": "public.app-category.developer-tools",
72 | "target": [],
73 | "type": "distribution",
74 | "hardenedRuntime": true,
75 | "gatekeeperAssess": false,
76 | "entitlements": "build/entitlements.mac.plist",
77 | "entitlementsInherit": "build/entitlements.mac.plist"
78 | },
79 | "win": {
80 | "icon": "256x256.png"
81 | },
82 | "asar": true,
83 | "files": [
84 | "**/*",
85 | "!node-modules/*",
86 | "!release-builds/*"
87 | ],
88 | "extraResources": [
89 | "build/*"
90 | ],
91 | "fileAssociations": [
92 | {
93 | "name": "SVG",
94 | "ext": "svg"
95 | },
96 | {
97 | "name": "PNG",
98 | "ext": "png"
99 | },
100 | {
101 | "name": "GIF",
102 | "ext": "gif"
103 | },
104 | {
105 | "name": "JPG",
106 | "ext": "jpg"
107 | }
108 | ],
109 | "dmg": {
110 | "background": "build/bg_dmg.tiff",
111 | "icon": "build/icon_dmg.icns",
112 | "format": "ULFO",
113 | "contents": [
114 | {
115 | "x": 168,
116 | "y": 240
117 | },
118 | {
119 | "x": 372,
120 | "y": 240,
121 | "type": "link",
122 | "path": "/Applications"
123 | }
124 | ]
125 | },
126 | "pkg": {},
127 | "publish": {
128 | "provider": "github",
129 | "owner": "stefansl",
130 | "repo": "image-shrinker"
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/assets/css/_variables.scss:
--------------------------------------------------------------------------------
1 | // Core variables
2 | $version: "0.5.0";
3 |
4 | // Core colors
5 | $primary-color: #46c2fe !default;
6 | $primary-color-dark: darken($primary-color, 3%) !default;
7 | $primary-color-light: lighten($primary-color, 3%) !default;
8 | $secondary-color: #d86550 !default;
9 | $secondary-color-dark: darken($secondary-color, 3%) !default;
10 | $secondary-color-light: lighten($secondary-color, 3%) !default;
11 |
12 | $main-color: #F3FFE2;
13 |
14 | // Gray colors
15 | $dark-color: #111 !default;
16 | $light-color: #fff !default;
17 | $gray-color: lighten($dark-color, 40%) !default;
18 | $gray-color-dark: darken($gray-color, 25%) !default;
19 | $gray-color-light: lighten($gray-color, 20%) !default;
20 |
21 | $border-color: lighten($dark-color, 60%) !default;
22 | $border-color-dark: darken($border-color, 10%) !default;
23 | $bg-color: lighten($dark-color, 66%) !default;
24 | $bg-color-dark: darken($bg-color, 3%) !default;
25 | $bg-color-light: $light-color !default;
26 |
27 | // Control colors
28 | $success-color: #32b643 !default;
29 | $warning-color: #ffb700 !default;
30 | $error-color: #e85600 !default;
31 |
32 | // Other colors
33 | $code-color: #e06870 !default;
34 | $highlight-color: #ffe9b3 !default;
35 | $body-bg: $bg-color-light !default;
36 | $body-font-color: lighten($dark-color, 5%) !default;
37 | $link-color: $primary-color !default;
38 | $link-color-dark: darken($link-color, 5%) !default;
39 |
40 | // Fonts
41 | // Credit: https://www.smashingmagazine.com/2015/11/using-system-ui-fonts-practical-guide/
42 | $base-font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto !default;
43 | $mono-font-family: "SF Mono", "Segoe UI Mono", "Roboto Mono", Menlo, Courier, monospace !default;
44 | $fallback-font-family: "Helvetica Neue", sans-serif !default;
45 | $cjk-zh-font-family: $base-font-family, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", $fallback-font-family !default;
46 | $cjk-jp-font-family: $base-font-family, "Hiragino Sans", "Hiragino Kaku Gothic Pro", "Yu Gothic", YuGothic, Meiryo, $fallback-font-family !default;
47 | $cjk-ko-font-family: $base-font-family, "Malgun Gothic", $fallback-font-family !default;
48 | $body-font-family: $base-font-family, $fallback-font-family !default;
49 |
50 | // Unit sizes
51 | $unit-o: .05rem !default;
52 | $unit-h: .1rem !default;
53 | $unit-1: .2rem !default;
54 | $unit-2: .4rem !default;
55 | $unit-3: .6rem !default;
56 | $unit-4: .8rem !default;
57 | $unit-5: 1rem !default;
58 | $unit-6: 1.2rem !default;
59 | $unit-7: 1.4rem !default;
60 | $unit-8: 1.6rem !default;
61 | $unit-9: 1.8rem !default;
62 | $unit-10: 2rem !default;
63 | $unit-12: 2.4rem !default;
64 | $unit-16: 3.2rem !default;
65 |
66 | // Font sizes
67 | $html-font-size: 20px !default;
68 | $html-line-height: 1.5 !default;
69 | $font-size: .8rem !default;
70 | $font-size-sm: .7rem !default;
71 | $font-size-lg: .9rem !default;
72 | $line-height: 1rem !default;
73 |
74 | // Sizes
75 | $layout-spacing: $unit-2 !default;
76 | $layout-spacing-sm: $unit-1 !default;
77 | $layout-spacing-lg: $unit-4 !default;
78 | $border-radius: $unit-h !default;
79 | $border-width: $unit-o !default;
80 | $border-width-lg: $unit-h !default;
81 | $control-size: $unit-9 !default;
82 | $control-size-sm: $unit-7 !default;
83 | $control-size-lg: $unit-10 !default;
84 | $control-padding-x: $unit-2 !default;
85 | $control-padding-x-sm: $unit-2 * .75 !default;
86 | $control-padding-x-lg: $unit-2 * 1.5 !default;
87 | $control-padding-y: ($control-size - $line-height) / 2 - $border-width !default;
88 | $control-padding-y-sm: ($control-size-sm - $line-height) / 2 - $border-width !default;
89 | $control-padding-y-lg: ($control-size-lg - $line-height) / 2 - $border-width !default;
90 | $control-icon-size: .8rem !default;
91 |
92 | $control-width-xs: 180px !default;
93 | $control-width-sm: 320px !default;
94 | $control-width-md: 640px !default;
95 | $control-width-lg: 960px !default;
96 | $control-width-xl: 1280px !default;
97 |
98 | // Responsive breakpoints
99 | $size-xs: 480px !default;
100 | $size-sm: 600px !default;
101 | $size-md: 840px !default;
102 | $size-lg: 960px !default;
103 | $size-xl: 1280px !default;
104 | $size-2x: 1440px !default;
105 |
106 | $responsive-breakpoint: $size-xs !default;
107 |
108 | // Z-index
109 | $zindex-0: 1 !default;
110 | $zindex-1: 100 !default;
111 | $zindex-2: 200 !default;
112 | $zindex-3: 300 !default;
113 | $zindex-4: 400 !default;
114 |
--------------------------------------------------------------------------------
/assets/fonts/LICENSE:
--------------------------------------------------------------------------------
1 | Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
2 | with Reserved Font Name < Fira >,
3 |
4 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
5 | This license is copied below, and is also available with a FAQ at:
6 | http://scripts.sil.org/OFL
7 |
8 |
9 | -----------------------------------------------------------
10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
11 | -----------------------------------------------------------
12 |
13 | PREAMBLE
14 | The goals of the Open Font License (OFL) are to stimulate worldwide
15 | development of collaborative font projects, to support the font creation
16 | efforts of academic and linguistic communities, and to provide a free and
17 | open framework in which fonts may be shared and improved in partnership
18 | with others.
19 |
20 | The OFL allows the licensed fonts to be used, studied, modified and
21 | redistributed freely as long as they are not sold by themselves. The
22 | fonts, including any derivative works, can be bundled, embedded,
23 | redistributed and/or sold with any software provided that any reserved
24 | names are not used by derivative works. The fonts and derivatives,
25 | however, cannot be released under any other type of license. The
26 | requirement for fonts to remain under this license does not apply
27 | to any document created using the fonts or their derivatives.
28 |
29 | DEFINITIONS
30 | "Font Software" refers to the set of files released by the Copyright
31 | Holder(s) under this license and clearly marked as such. This may
32 | include source files, build scripts and documentation.
33 |
34 | "Reserved Font Name" refers to any names specified as such after the
35 | copyright statement(s).
36 |
37 | "Original Version" refers to the collection of Font Software components as
38 | distributed by the Copyright Holder(s).
39 |
40 | "Modified Version" refers to any derivative made by adding to, deleting,
41 | or substituting -- in part or in whole -- any of the components of the
42 | Original Version, by changing formats or by porting the Font Software to a
43 | new environment.
44 |
45 | "Author" refers to any designer, engineer, programmer, technical
46 | writer or other person who contributed to the Font Software.
47 |
48 | PERMISSION & CONDITIONS
49 | Permission is hereby granted, free of charge, to any person obtaining
50 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
51 | redistribute, and sell modified and unmodified copies of the Font
52 | Software, subject to the following conditions:
53 |
54 | 1) Neither the Font Software nor any of its individual components,
55 | in Original or Modified Versions, may be sold by itself.
56 |
57 | 2) Original or Modified Versions of the Font Software may be bundled,
58 | redistributed and/or sold with any software, provided that each copy
59 | contains the above copyright notice and this license. These can be
60 | included either as stand-alone text files, human-readable headers or
61 | in the appropriate machine-readable metadata fields within text or
62 | binary files as long as those fields can be easily viewed by the user.
63 |
64 | 3) No Modified Version of the Font Software may use the Reserved Font
65 | Name(s) unless explicit written permission is granted by the corresponding
66 | Copyright Holder. This restriction only applies to the primary font name as
67 | presented to the users.
68 |
69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
70 | Software shall not be used to promote, endorse or advertise any
71 | Modified Version, except to acknowledge the contribution(s) of the
72 | Copyright Holder(s) and the Author(s) or with their explicit written
73 | permission.
74 |
75 | 5) The Font Software, modified or unmodified, in part or in whole,
76 | must be distributed entirely under this license, and must not be
77 | distributed under any other license. The requirement for fonts to
78 | remain under this license does not apply to any document created
79 | using the Font Software.
80 |
81 | TERMINATION
82 | This license becomes null and void if any of the above conditions are
83 | not met.
84 |
85 | DISCLAIMER
86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
94 | OTHER DEALINGS IN THE FONT SOFTWARE.
95 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Image Shrinker
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Drag files here
18 | only SVG, JPG, GIF and PNG allowed
19 |
20 |
21 |
22 |
23 |
121 |
122 |
125 |
126 |
129 |
130 |
--------------------------------------------------------------------------------
/assets/css/imageshrinker.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Fira Sans';
3 | src: local("Fira Sans Italic"), local("FiraSans-Italic"), url(../fonts/FiraSans-Italic.woff2) format("woff2");
4 | font-weight: 400;
5 | font-style: italic; }
6 | @font-face {
7 | font-family: 'Fira Sans';
8 | src: local("Fira Sans"), local("FiraSans"), url(../fonts/FiraSans-Regular.woff2) format("woff2");
9 | font-weight: 400;
10 | font-style: normal; }
11 | @font-face {
12 | font-family: 'Fira Sans';
13 | src: local("Fira Sans Bold Italic"), local("FiraSans-BoldItalic"), url(../fonts/FiraSans-BoldItalic.woff2) format("woff2");
14 | font-weight: 700;
15 | font-style: italic; }
16 | * {
17 | box-sizing: border-box; }
18 |
19 | html {
20 | -webkit-app-region: drag; }
21 |
22 | body {
23 | color: #111;
24 | font-family: 'Fira Sans', sans-serif;
25 | font-weight: 400;
26 | overflow: hidden;
27 | padding: 2rem .5rem; }
28 |
29 | #wrapper {
30 | max-height: calc(100vh - 4rem); }
31 |
32 | #background {
33 | background-image: url("../img/bg_poly.min.svg");
34 | background-size: 120% 120%;
35 | height: 100vh;
36 | left: 0;
37 | position: absolute;
38 | top: 0;
39 | transform: scale(1.1);
40 | transition: transform .7s ease-out;
41 | width: 100vw;
42 | z-index: -1;
43 | perspective: 8000px;
44 | transform-style: preserve-3d; }
45 |
46 | #dragzone {
47 | border: 2px dashed #111;
48 | color: #111;
49 | cursor: pointer;
50 | font-style: italic;
51 | opacity: .5;
52 | padding: 1em;
53 | position: relative;
54 | text-align: center;
55 | transition: opacity .1s ease-out; }
56 | #dragzone.drag-active {
57 | opacity: .9; }
58 | #dragzone::after {
59 | background-color: transparent;
60 | content: " ";
61 | display: block;
62 | height: 100%;
63 | left: 0;
64 | opacity: 0;
65 | position: absolute;
66 | top: 0;
67 | transition: all .4s ease-out;
68 | width: 100%; }
69 | #dragzone.is--processing::after {
70 | background: rgba(216, 101, 80, 0.5) url("../img/tail-spin.min.svg") no-repeat center center;
71 | background-size: 15%;
72 | opacity: 1; }
73 | #dragzone:hover {
74 | opacity: .75; }
75 | #dragzone strong {
76 | font-style: italic;
77 | font-weight: 400; }
78 | #dragzone h1 {
79 | font-size: 1.6rem;
80 | font-weight: 700;
81 | margin: 0 0 .5rem; }
82 |
83 | #result {
84 | max-height: 400px;
85 | overflow-y: scroll;
86 | -webkit-mask-image: -webkit-linear-gradient(bottom, transparent 1%, #fff 20%, #fff 90%, transparent 99%); }
87 |
88 | .resLine {
89 | border-bottom: 1px solid rgba(17, 17, 17, 0.3);
90 | padding: 1rem .25rem; }
91 | .resLine span {
92 | color: rgba(17, 17, 17, 0.7);
93 | font-size: 8px;
94 | font-weight: 400; }
95 | .resLine a {
96 | color: rgba(17, 17, 17, 0.9);
97 | display: block;
98 | font-size: 10px;
99 | font-style: italic; }
100 |
101 | #menuSettings {
102 | background-color: rgba(17, 17, 17, 0.9);
103 | color: rgba(255, 255, 255, 0.95);
104 | font-size: .675rem;
105 | height: 100vh;
106 | left: 0;
107 | padding: 3rem 1rem;
108 | position: absolute;
109 | top: 0;
110 | transform: translate3d(-100%, 0, 0);
111 | transition: transform 450ms cubic-bezier(0.23, 1, 0.32, 1);
112 | width: 100vw;
113 | z-index: 10; }
114 | #menuSettings.is--open {
115 | transform: translate3d(0, 0, 0); }
116 | #menuSettings li {
117 | border-bottom: 1px solid rgba(255, 255, 255, 0.2);
118 | padding: .5rem 1rem; }
119 | #menuSettings li:after {
120 | clear: both;
121 | content: " "; }
122 | #menuSettings .columns {
123 | border-bottom: 1px solid rgba(255, 255, 255, 0.1);
124 | padding: .5rem 0; }
125 | #menuSettings .column {
126 | align-self: center; }
127 | #menuSettings .column span {
128 | line-height: 1rem; }
129 |
130 | #btnOpenSettings {
131 | z-index: 5; }
132 | #btnOpenSettings:hover {
133 | color: rgba(17, 17, 17, 0.5); }
134 |
135 | #btnCloseSettings:hover {
136 | color: rgba(255, 255, 255, 0.75); }
137 |
138 | #btnOpenSettings, #btnCloseSettings {
139 | bottom: 1rem;
140 | position: absolute;
141 | right: 1rem; }
142 |
143 | .icon {
144 | cursor: pointer; }
145 |
146 | .copyright {
147 | bottom: 1rem;
148 | color: #fff;
149 | display: table;
150 | left: 1.5rem;
151 | margin: 0 auto;
152 | position: absolute; }
153 | .copyright span {
154 | display: table-cell;
155 | font-size: .6em;
156 | opacity: .4;
157 | padding-right: .75em;
158 | vertical-align: middle; }
159 | .copyright img {
160 | opacity: .4;
161 | transition: opacity 200ms cubic-bezier(0.79, 0.14, 0.15, 0.86); }
162 | .copyright a {
163 | display: block;
164 | line-height: 0; }
165 | .copyright a:hover img {
166 | opacity: 1; }
167 |
168 | .text-small {
169 | font-size: .5rem; }
170 |
171 | .btn.btn-sm {
172 | font-size: .5rem; }
173 |
174 | #overlay {
175 | height: 100vh;
176 | left: 0;
177 | position: absolute;
178 | top: 0;
179 | width: 100vw;
180 | z-index: 10; }
181 |
182 | #btnSavepath {
183 | width: calc(100vw - 3rem); }
184 |
185 | /*# sourceMappingURL=imageshrinker.css.map */
186 |
--------------------------------------------------------------------------------
/assets/css/imageshrinker.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 | // Fira Sans
3 | @font-face {
4 | font-family: 'Fira Sans';
5 | src: local('Fira Sans Italic'), local('FiraSans-Italic'), url(../fonts/FiraSans-Italic.woff2) format('woff2');
6 | font-weight: 400;
7 | font-style: italic;
8 | }
9 |
10 | @font-face {
11 | font-family: 'Fira Sans';
12 | src: local('Fira Sans'), local('FiraSans'), url(../fonts/FiraSans-Regular.woff2) format('woff2');
13 | font-weight: 400;
14 | font-style: normal;
15 | }
16 |
17 | @font-face {
18 | font-family: 'Fira Sans';
19 | src: local('Fira Sans Bold Italic'), local('FiraSans-BoldItalic'), url(../fonts/FiraSans-BoldItalic.woff2) format('woff2');
20 | font-weight: 700;
21 | font-style: italic;
22 | }
23 |
24 | * {
25 | box-sizing: border-box;
26 | }
27 |
28 | html {
29 | -webkit-app-region: drag;
30 | }
31 |
32 | body {
33 | color: $dark-color;
34 | font-family: 'Fira Sans', sans-serif;
35 | font-weight: 400;
36 | overflow: hidden;
37 | padding: 2rem .5rem;
38 | }
39 |
40 | #wrapper {
41 | max-height: calc(100vh - 4rem);
42 | }
43 |
44 | #background {
45 | background-image: url('../img/bg_poly.min.svg');
46 | background-size: 120% 120%;
47 | height: 100vh;
48 | left: 0;
49 | position: absolute;
50 | top: 0;
51 | transform: scale(1.1);
52 | transition: transform .7s ease-out;
53 | width: 100vw;
54 | z-index: -1;
55 | perspective: 8000px;
56 | transform-style: preserve-3d;
57 |
58 | }
59 |
60 | #dragzone {
61 | border: 2px dashed $dark-color;
62 | color: $dark-color;
63 | cursor: pointer;
64 | font-style: italic;
65 | opacity: .5;
66 | padding: 1em;
67 | position: relative;
68 | text-align: center;
69 | transition: opacity .1s ease-out;
70 | &.drag-active {
71 | opacity: .9;
72 | }
73 | &::after {
74 | background-color: transparent;
75 | content: " ";
76 | display: block;
77 | height: 100%;
78 | left: 0;
79 | opacity: 0;
80 | position: absolute;
81 | top: 0;
82 | transition: all .4s ease-out;
83 | width: 100%;
84 | }
85 | &.is--processing::after {
86 | background: rgba($secondary-color, .5) url("../img/tail-spin.min.svg") no-repeat center center;
87 | background-size: 15%;
88 | opacity: 1;
89 | }
90 | &:hover {
91 | opacity: .75;
92 | }
93 | strong {
94 | font-style: italic;
95 | font-weight: 400;
96 | }
97 | h1 {
98 | font-size: 1.6rem;
99 | font-weight: 700;
100 | margin: 0 0 .5rem;
101 | }
102 | }
103 |
104 | #result {
105 | max-height: 400px;
106 | overflow-y: scroll;
107 | -webkit-mask-image: -webkit-linear-gradient(bottom, transparent 1%, $light-color 20%, $light-color 90%, transparent 99%);
108 |
109 | }
110 |
111 | .resLine {
112 | border-bottom: 1px solid rgba($dark-color, .3);
113 | padding: 1rem .25rem;
114 | span {
115 | color: rgba($dark-color, .7);
116 | font-size: 8px;
117 | font-weight: 400;
118 | }
119 | a {
120 | color: rgba($dark-color, .9);
121 | display: block;
122 | font-size: 10px;
123 | font-style: italic;
124 | }
125 | }
126 |
127 | #menuSettings {
128 | background-color: rgba($dark-color, .9);
129 | color: rgba($light-color, .95);
130 | font-size: .675rem;
131 | height: 100vh;
132 | left: 0;
133 | padding: 3rem 1rem;
134 | position: absolute;
135 | top: 0;
136 | transform: translate3d(-100%, 0, 0);
137 | transition: transform 450ms cubic-bezier(.23, 1, .32, 1);
138 | width: 100vw;
139 | z-index: 10;
140 | &.is--open {
141 | transform: translate3d(0, 0, 0);
142 | }
143 | li {
144 | border-bottom: 1px solid rgba($light-color, .2);
145 | padding: .5rem 1rem;
146 | &:after {
147 | clear: both;
148 | content: " ";
149 |
150 | }
151 | }
152 | .columns {
153 | border-bottom: 1px solid rgba($light-color, .1);
154 | padding: .5rem 0;
155 | }
156 | .column {
157 | align-self: center;
158 | span {
159 | line-height: 1rem;
160 | }
161 | }
162 | }
163 |
164 | #btnOpenSettings {
165 | z-index: 5;
166 | &:hover {
167 | color: rgba($dark-color, .50);
168 | }
169 | }
170 |
171 | #btnCloseSettings {
172 | &:hover {
173 | color: rgba($light-color, .75);
174 | }
175 |
176 | }
177 |
178 | #btnOpenSettings, #btnCloseSettings {
179 | bottom: 1rem;
180 | position: absolute;
181 | right: 1rem;
182 | }
183 |
184 | .icon {
185 | cursor: pointer;
186 | }
187 |
188 | .copyright {
189 | bottom: 1rem;
190 | color: $light-color;
191 | display: table;
192 | left: 1.5rem;
193 | margin: 0 auto;
194 | position: absolute;
195 | span {
196 | display: table-cell;
197 | font-size: .6em;
198 | opacity: .4;
199 | padding-right: .75em;
200 | vertical-align: middle;
201 | }
202 | img {
203 | opacity: .4;
204 | transition: opacity 200ms cubic-bezier(.79, .14, .15, .86);
205 | }
206 | a {
207 | display: block;
208 | line-height: 0;
209 | &:hover img {
210 | opacity: 1;
211 | }
212 | }
213 | }
214 |
215 | .text-small {
216 | font-size: .5rem;
217 | }
218 |
219 | .btn.btn-sm {
220 | font-size: .5rem;
221 |
222 | }
223 |
224 | #overlay {
225 | height: 100vh;
226 | left: 0;
227 | position: absolute;
228 | top: 0;
229 | width: 100vw;
230 | z-index: 10;
231 | }
232 |
233 | #btnSavepath {
234 | width: calc(100vw - 3rem);
235 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 | ==================
3 |
4 | Statement of Purpose
5 | ---------------------
6 |
7 | The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
8 |
9 | Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
10 |
11 | For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
12 |
13 | 1. Copyright and Related Rights.
14 | --------------------------------
15 | A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
16 |
17 | i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
18 | ii. moral rights retained by the original author(s) and/or performer(s);
19 | iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
20 | iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
21 | v. rights protecting the extraction, dissemination, use and reuse of data in a Work;
22 | vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
23 | vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
24 |
25 | 2. Waiver.
26 | -----------
27 | To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
28 |
29 | 3. Public License Fallback.
30 | ----------------------------
31 | Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
32 |
33 | 4. Limitations and Disclaimers.
34 | --------------------------------
35 |
36 | a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
37 | b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
38 | c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
39 | d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
40 |
--------------------------------------------------------------------------------
/renderer.js:
--------------------------------------------------------------------------------
1 | const { ipcRenderer, shell } = require('electron');
2 | const settings = require('electron-settings');
3 | const { dialog } = require('electron').remote;
4 | const fs = require('fs');
5 | const path = require('path');
6 | const log = require('electron-log');
7 |
8 | const dragzone = document.getElementById('dragzone'),
9 | resultBox = document.getElementById('result'),
10 | btnOpenSettings = document.getElementById('btnOpenSettings'),
11 | btnCloseSettings = document.getElementById('btnCloseSettings'),
12 | menuSettings = document.getElementById('menuSettings'),
13 | switches = document.getElementsByTagName('input'),
14 | openInBrowserLink = document.getElementsByClassName('openInBrowser'),
15 | btnSavepath = document.getElementById('btnSavepath'),
16 | wrapperSavePath = document.getElementById('wrapperSavePath'),
17 | folderswitch = document.getElementById('folderswitch'),
18 | clearlist = document.getElementById('clearlist'),
19 | updatecheck = document.getElementById('updatecheck'),
20 | suffix = document.getElementById('suffix'),
21 | subfolder = document.getElementById('subfolder'),
22 | notification = document.getElementById('notification');
23 |
24 | /*
25 | * Settings
26 | */
27 | let userSetting = settings.getSync();
28 | notification.checked = userSetting.notification;
29 | clearlist.checked = userSetting.clearlist;
30 | updatecheck.checked = userSetting.updatecheck;
31 | suffix.checked = userSetting.suffix;
32 | subfolder.checked = userSetting.subfolder;
33 |
34 | if (userSetting.folderswitch === false)
35 | {
36 | folderswitch.checked = false;
37 | wrapperSavePath.classList.remove('d-none');
38 | } else
39 | {
40 | folderswitch.checked = true;
41 | }
42 |
43 |
44 | /*
45 | * Cut path from beginning, if necessary
46 | * @param {string} path Filepath
47 | * @param {integer} length max length
48 | * @return {string} truncated string
49 | */
50 | const cutFolderName = (path, length = 20) => {
51 | return path.length >= length ? '... ' + path.substr(path.length - length) : path;
52 | };
53 |
54 |
55 | /**
56 | * @param {{savepath:string}} userSetting
57 | */
58 | if (userSetting.savepath)
59 | btnSavepath.innerText = cutFolderName(userSetting.savepath[0], 48);
60 |
61 | /*
62 | * Open filepicker
63 | */
64 | dragzone.onclick = () => {
65 | dialog.showOpenDialog(
66 | {
67 | properties: ['openFile', 'multiSelections']
68 | }).then(result => {
69 |
70 | if (result.canceled)
71 | {
72 | return;
73 | }
74 |
75 | // Add loader
76 | dragzone.classList.add('is--processing');
77 | if (settings.getSync('clearlist') === true)
78 | {
79 | resultBox.innerHTML = '';
80 | }
81 |
82 | for (let f of result.filePaths)
83 | {
84 | let filename = path.parse(f).base;
85 | ipcRenderer.send('shrinkImage', filename, f);
86 | }
87 |
88 | }).catch(err => {
89 | log.error(err);
90 | });
91 |
92 | };
93 |
94 | document.ondragover = () => {
95 | dragzone.classList.add('drag-active');
96 | return false;
97 | };
98 |
99 | document.ondragleave = () => {
100 | dragzone.classList.remove('drag-active');
101 | return false;
102 | };
103 |
104 | document.ondragend = () => {
105 | dragzone.classList.remove('drag-active');
106 | return false;
107 | };
108 |
109 | /*
110 | * Action on drag drop
111 | */
112 | document.ondrop = e => {
113 | e.preventDefault();
114 |
115 | let items = e.dataTransfer.items;
116 | for (let i = 0; i < items.length; i++)
117 | {
118 | // webkitGetAsEntry is where the magic happens
119 | let item = items[i].webkitGetAsEntry();
120 | if (item)
121 | {
122 | traverseFileTree(item);
123 | }
124 | }
125 |
126 | if (settings.getSync('clearlist'))
127 | {
128 | resultBox.innerHTML = '';
129 | }
130 |
131 | dragzone.classList.add('is--processing');
132 | dragzone.classList.remove('drag-active');
133 |
134 | return false;
135 | };
136 |
137 | /*
138 | * Choose folder for saving shrunken images
139 | */
140 | btnSavepath.onclick = () => {
141 | dialog.showOpenDialog(
142 | {
143 | properties: ['openDirectory', 'createDirectory']
144 | }).then(result => {
145 | if (result.filePaths)
146 | {
147 | btnSavepath.innerText = cutFolderName(result.filePaths[0], 48);
148 | settings.set('savepath', result.filePaths);
149 | }
150 | }).catch(err => {
151 | log.error(err);
152 | });
153 | };
154 |
155 | /*
156 | * Save settings
157 | */
158 | Array.from(switches).forEach(switchEl => {
159 | switchEl.onchange = e => {
160 | settings.setSync(e.target['name'], e.target['checked']);
161 | if (e.target['name'] === 'folderswitch')
162 | {
163 | if (!e.target['checked'])
164 | {
165 | wrapperSavePath.classList.remove('d-none');
166 | } else
167 | {
168 | wrapperSavePath.classList.add('d-none');
169 | }
170 | }
171 | };
172 | });
173 | /*
174 | * Settings menu
175 | */
176 | // Open
177 | btnOpenSettings.onclick = e => {
178 | e.preventDefault();
179 | menuSettings.classList.add('is--open');
180 | };
181 |
182 | // Close on pressing close icon
183 | btnCloseSettings.onclick = e => {
184 | e.preventDefault();
185 | menuSettings.classList.remove('is--open');
186 | };
187 |
188 | // Close on pressing ESC
189 | document.onkeyup = e => {
190 | if (e.key === 'Escape')
191 | {
192 | menuSettings.classList.remove('is--open');
193 | }
194 | };
195 |
196 | /*
197 | * Renderer process
198 | */
199 | ipcRenderer.on('isShrinked', (event, path, sizeBefore, sizeAfter) => {
200 | const percent = Math.round((100 / sizeBefore) * (sizeBefore - sizeAfter));
201 |
202 | // Remove loader
203 | dragzone.classList.remove('is--processing');
204 |
205 | // Create container
206 | const resContainer = document.createElement('div');
207 | resContainer.className = 'resLine';
208 | resContainer.innerHTML =
209 | 'You saved ' +
210 | percent +
211 | '%. Your shrunken image is here:
';
212 |
213 | // Create link
214 | let resElement = document.createElement('a');
215 | resElement.setAttribute('href', '#');
216 | let resText = document.createTextNode(path);
217 | resElement.appendChild(resText);
218 |
219 | // Add click event
220 | resElement.onclick = el => {
221 | el.preventDefault();
222 | shell.showItemInFolder(path);
223 | };
224 |
225 | resContainer.appendChild(resElement);
226 | resultBox.prepend(resContainer);
227 |
228 | // Notification
229 |
230 | if (settings.getSync('notification'))
231 | {
232 | new window.Notification('Image shrunk, pal!', {
233 | body: path,
234 | silent: true
235 | });
236 | }
237 | }).on('openSettings', () => {
238 | menuSettings.classList.add('is--open');
239 | }).on('error', () => {
240 | // Remove loader
241 | dragzone.classList.remove('is--processing');
242 | });
243 |
244 | /*
245 | * Parallax background
246 | */
247 | let bg = document.getElementById('background'),
248 | winX = window.innerWidth / 2,
249 | winY = window.innerHeight / 2;
250 |
251 | // Fix window size on resize
252 | window.onresize = () => {
253 | setTimeout(() => {
254 | winX = window.innerWidth / 2;
255 | winY = window.innerHeight / 2;
256 | }, 700);
257 | };
258 |
259 | // Let's do some parallax stuff
260 | document.onmousemove = e => {
261 | let transX = e.clientX - winX,
262 | transY = e.clientY - winY,
263 | tiltX = transX / winY,
264 | tiltY = -(transY / winX),
265 | radius = Math.sqrt(Math.pow(tiltX, 2) + Math.pow(tiltY, 2)),
266 | transformX = Math.floor(tiltX * Math.PI),
267 | transformY = Math.floor(tiltY * Math.PI),
268 | degree = radius * 15,
269 | transform;
270 |
271 | transform = 'scale(1.15)';
272 | transform += ' rotate3d(' + tiltX + ', ' + tiltY + ', 0, ' + degree + 'deg)';
273 | transform += ' translate3d(' + transformX + 'px, ' + transformY + 'px, 0)';
274 |
275 | bg.style.transform = transform;
276 | };
277 |
278 | // Reset, if mouse leaves window
279 | document.onmouseleave = () => {
280 | bg.style.transform = '';
281 | };
282 |
283 | // (opt) event, text as return value
284 | ipcRenderer.on('updateReady', () => {
285 | // changes the text of the button
286 | const container = document.getElementById('ready');
287 | container.innerHTML = 'new version available!';
288 | });
289 |
290 | /*
291 | * Open external links in browser
292 | */
293 | Array.from(openInBrowserLink).forEach((el) => {
294 | el.addEventListener('click', (event) => {
295 | event.preventDefault();
296 | shell.openExternal(el.getAttribute('href')).catch((error) => {
297 | log.error(error);
298 | });
299 | });
300 | });
301 |
302 | /**
303 | * Traverse down the folders and exclude files in `exclude` array
304 | */
305 | function traverseFileTree(item, path)
306 | {
307 | const exclude = ['.DS_Store'];
308 | path = path || '';
309 |
310 | if (item.isFile)
311 | {
312 | // Get file
313 | item.file(function(f) {
314 | if (fs.statSync(f.path).isDirectory() || exclude.includes(f.name))
315 | {
316 | dragzone.classList.remove('drag-active');
317 |
318 | return false;
319 | }
320 |
321 | ipcRenderer.send('shrinkImage', f.name, f.path, f.lastModified);
322 | });
323 | } else if (item.isDirectory)
324 | {
325 | // Get folder contents
326 | const dirReader = item.createReader();
327 | dirReader.readEntries(function(entries) {
328 | for (let i in entries)
329 | {
330 | traverseFileTree(entries[i], path + item.name + '/');
331 | }
332 | });
333 | }
334 | }
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | const { app, BrowserWindow, ipcMain, dialog, TouchBar } = require(
2 | 'electron');
3 | const dotenv = require('dotenv').config();
4 | const nativeImage = require('electron').nativeImage;
5 | const { autoUpdater } = require('electron-updater');
6 | const log = require('electron-log');
7 | const fs = require('fs');
8 | const path = require('path');
9 | const settings = require('electron-settings');
10 | const svgo = require('svgo');
11 | const execFile = require('child_process').execFile;
12 | const mozjpeg = require('mozjpeg');
13 | const pngquant = require('pngquant-bin');
14 | const makeDir = require('make-dir');
15 | const { TouchBarButton } = TouchBar;
16 | const gifsicle = require('gifsicle');
17 |
18 | global.debug = {
19 | devTools: 0
20 | };
21 |
22 | /**
23 | * Support for .env
24 | */
25 | if(!dotenv.error){
26 | global.debug = {
27 | devTools: process.env.ELECTRON_DEBUG
28 | };
29 | }
30 |
31 | /**
32 | * Start logging in os log
33 | */
34 | autoUpdater.logger = log;
35 |
36 | autoUpdater.logger['transports'].file.level = 'info';
37 | log.info('App starting...');
38 |
39 | /**
40 | * Init vars
41 | */
42 | let svg = new svgo();
43 | let mainWindow;
44 |
45 |
46 | /**
47 | * Create the browser window
48 | */
49 | const createWindow = () => {
50 |
51 | /** Create the browser window. */
52 | mainWindow = new BrowserWindow({
53 | titleBarStyle: 'hiddenInset',
54 | width: 340,
55 | height: 550,
56 | minWidth: 340,
57 | minHeight: 550,
58 | frame: false,
59 | backgroundColor: '#F7F7F7',
60 | resizable: true,
61 | show: false,
62 | //icon: path.join(__dirname, 'assets/icons/png/64x64.png'),
63 | webPreferences: {
64 | nodeIntegration: true,
65 | enableRemoteModule: true
66 | }
67 | });
68 |
69 | /** Show window when ready */
70 | mainWindow.on('ready-to-show', () => {
71 | mainWindow.show();
72 | });
73 |
74 | /** Load index.html of the app. */
75 | mainWindow.loadURL(path.join('file://', __dirname, '/index.html')).then(
76 | () => {
77 | /** Open the DevTools. */
78 | global['debug'].devTools === 0 ||
79 | mainWindow.webContents.openDevTools();
80 | }
81 | ).catch(
82 | (error) => {
83 | log.error(error);
84 | }
85 | );
86 |
87 | /** Open the DevTools. */
88 | global['debug'].devTools === 0 || mainWindow.webContents.openDevTools();
89 |
90 | /** Window closed */
91 | mainWindow.on('closed', () => {
92 | mainWindow = null;
93 | });
94 |
95 | /** Default settings */
96 | const defaultSettings = {
97 | notification: true,
98 | folderswitch: true,
99 | clearlist: false,
100 | suffix: true,
101 | updatecheck: true,
102 | subfolder: false,
103 | };
104 |
105 | /** set missing settings */
106 | const newSettings = Object.assign({}, defaultSettings, settings.getSync());
107 | settings.setSync(newSettings);
108 | mainWindow.setTouchBar(touchBar);
109 | require('./menu/mainmenu');
110 | };
111 |
112 | /** Touchbar support */
113 | let touchBarResult = new TouchBarButton({
114 | label: 'Let me shrink some images!',
115 | backgroundColor: '#000000',
116 | click: () => {
117 | dialog.showOpenDialog({
118 | properties: ['openFile', 'multiSelections']
119 | }).then(result => {
120 | if (result.canceled)
121 | {
122 | return;
123 | }
124 | for (let filePath of result.filePaths)
125 | {
126 | processFile(filePath, path.basename(filePath));
127 | }
128 | }).catch(err => {
129 | log.error(err);
130 | });
131 | }
132 | });
133 |
134 | let touchBarIcon = new TouchBarButton({
135 | backgroundColor: '#000000',
136 | 'icon': nativeImage.createFromPath(path.join(__dirname, 'assets/icons/png/16x16.png')),
137 | iconPosition: 'center'
138 | });
139 |
140 | const touchBar = new TouchBar({
141 | items: [
142 | touchBarResult
143 | ]
144 | });
145 |
146 | /** Add Touchbar icon */
147 | touchBar.escapeItem = touchBarIcon;
148 |
149 | app.on('will-finish-launching', () => {
150 | app.on('open-file', (event, filePath) => {
151 | event.preventDefault();
152 | processFile(filePath, path.basename(filePath));
153 | });
154 | });
155 |
156 | /** Start app */
157 | app.on('ready', () => {
158 | createWindow();
159 | if (settings.getSync('updatecheck') === true)
160 | {
161 | autoUpdater.checkForUpdatesAndNotify().catch((error) => {
162 | log.error(error);
163 | });
164 | }
165 | });
166 |
167 | /** Quit when all windows are closed. */
168 | app.on('window-all-closed', () => {
169 | if (process.platform !== 'darwin')
170 | {
171 | app.quit();
172 | }
173 | });
174 |
175 | app.on('activate', () => {
176 | if (mainWindow === null)
177 | {
178 | createWindow();
179 | }
180 | });
181 |
182 | /** When the update has been downloaded and is ready to be installed, notify the BrowserWindow */
183 | autoUpdater.on('update-downloaded', (info) => {
184 | log.info(info);
185 | mainWindow.webContents.send('updateReady');
186 | });
187 |
188 | /** When receiving a quitAndInstall signal, quit and install the new version ;) */
189 | ipcMain.on('quitAndInstall', (event, arg) => {
190 | log.info(event);
191 | log.info(arg);
192 | autoUpdater.quitAndInstall();
193 | });
194 |
195 | /** Main logic */
196 | ipcMain.on(
197 | 'shrinkImage', (event, fileName, filePath) => {
198 | processFile(filePath, fileName);
199 | }
200 | );
201 |
202 | /**
203 | * Shrinking the image
204 | * @param {string} filePath Filepath
205 | * @param {string} fileName Filename
206 | */
207 | const processFile = (filePath, fileName) => {
208 |
209 | /** Focus window on drag */
210 | !mainWindow || mainWindow.focus();
211 |
212 | /** Change Touchbar */
213 | touchBarResult.label = 'I am shrinking for you';
214 |
215 | /** Get filesize */
216 | let sizeOrig = getFileSize(filePath, false);
217 |
218 | /** Process image(s) */
219 | fs.readFile(filePath, 'utf8', (err, data) => {
220 |
221 | if (err)
222 | {
223 | throw err;
224 | }
225 |
226 | app.addRecentDocument(filePath);
227 | const newFile = generateNewPath(filePath);
228 |
229 | switch (path.extname(fileName).toLowerCase())
230 | {
231 | case '.svg':
232 | {
233 | svg.optimize(data).then((result) => {
234 | fs.writeFile(newFile, result.data, (err) => {
235 | touchBarResult.label = 'Your shrunken image: ' +
236 | newFile;
237 |
238 | sendToRenderer(err, newFile, sizeOrig);
239 | });
240 | }).catch((error) => {
241 | dialog.showErrorBox('Error', error.message);
242 | });
243 | break;
244 | }
245 | case '.jpg':
246 | case '.jpeg':
247 | {
248 | /** Create temp file from original, see #54 **/
249 | let origFile;
250 | let addTmpFile = !settings.getSync('suffix') && !settings.getSync('subfolder');
251 |
252 | if (addTmpFile) {
253 | origFile = newFile + '.tmp';
254 | fs.copyFileSync(filePath, origFile);
255 | }else {
256 | origFile = filePath;
257 | }
258 |
259 | execFile(mozjpeg, ['-outfile', newFile, origFile], (err) => {
260 |
261 | /** Delete tmp file **/
262 | !addTmpFile || fs.unlinkSync(origFile);
263 |
264 | touchBarResult.label = 'Your shrunken image: ' + newFile;
265 | sendToRenderer(err, newFile, sizeOrig);
266 | });
267 |
268 | break;
269 | }
270 | case '.png':
271 | {
272 | execFile(pngquant, ['-fo', newFile, filePath], (err) => {
273 | touchBarResult.label = 'Your shrunken image: ' + newFile;
274 | sendToRenderer(err, newFile, sizeOrig);
275 | });
276 | break;
277 | }
278 | case '.gif':
279 | {
280 | execFile(gifsicle, ['-o', newFile, filePath, '-O=2', '-i'],
281 | err => {
282 | touchBarResult.label = 'Your shrunken image: ' +
283 | newFile;
284 | sendToRenderer(err, newFile, sizeOrig);
285 | });
286 | break;
287 | }
288 | default:
289 | mainWindow.webContents.send('error');
290 | dialog.showMessageBoxSync({
291 | 'type': 'error',
292 | 'message': 'Only PNG SVG, JPG and GIF allowed'
293 | });
294 | }
295 | });
296 | };
297 |
298 | /**
299 | * Generate new path to shrunken file
300 | * @param {string} pathName Filepath
301 | * @return {object} filepath object
302 | */
303 | const generateNewPath = (pathName) => {
304 |
305 | let objPath = path.parse(pathName);
306 |
307 | if ( settings.getSync('folderswitch') === false &&
308 | typeof settings.getSync('savepath') !== 'undefined')
309 | {
310 | objPath.dir = settings.getSync('savepath')[0];
311 | }
312 |
313 | if (settings.getSync('subfolder'))
314 | {
315 | objPath.dir = objPath.dir + '/minified';
316 | }
317 |
318 | makeDir.sync(objPath.dir);
319 |
320 | /** Suffix setting */
321 | const suffix = settings.getSync('suffix') ? '.min' : '';
322 | objPath.base = objPath.name + suffix + objPath.ext;
323 |
324 | return path.format(objPath);
325 | };
326 |
327 | /**
328 | * Calculate filesize
329 | * @param {string} filePath Filepath
330 | * @param {boolean} mb If true return as MB
331 | * @return {number} filesize in MB or KB
332 | */
333 | const getFileSize = (filePath, mb) => {
334 | const stats = fs.statSync(filePath);
335 |
336 | if (mb)
337 | {
338 | return stats.size / 1024;
339 | }
340 |
341 | return stats.size;
342 | };
343 |
344 | /**
345 | * Send data to renderer script
346 | * @param {object} err Error message
347 | * @param {string} newFile New filename
348 | * @param {number} sizeOrig Original filesize
349 | */
350 | const sendToRenderer = (err, newFile, sizeOrig) => {
351 |
352 | if (!err)
353 | {
354 | let sizeShrinked = getFileSize(newFile, false);
355 |
356 | mainWindow.webContents.send(
357 | 'isShrinked',
358 | newFile,
359 | sizeOrig,
360 | sizeShrinked
361 | );
362 | } else
363 | {
364 | log.error(err);
365 | mainWindow.webContents.send('error');
366 | dialog.showMessageBoxSync({
367 | 'type': 'error',
368 | 'message': 'I\'m not able to write your new image. Sorry! Error: ' + err
369 | });
370 | }
371 | };
372 |
--------------------------------------------------------------------------------
/assets/img/bg_poly.min.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/css/spectre.css:
--------------------------------------------------------------------------------
1 | /*! Spectre.css v0.5.0 | MIT License | github.com/picturepan2/spectre */
2 | /* Manually forked from Normalize.css */
3 | /* normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
4 | /**
5 | * 1. Change the default font family in all browsers (opinionated).
6 | * 2. Correct the line height in all browsers.
7 | * 3. Prevent adjustments of font size after orientation changes in
8 | * IE on Windows Phone and in iOS.
9 | */
10 | /* Document
11 | ========================================================================== */
12 | html {
13 | font-family: sans-serif;
14 | /* 1 */
15 | -ms-text-size-adjust: 100%;
16 | /* 3 */
17 | -webkit-text-size-adjust: 100%;
18 | /* 3 */ }
19 |
20 | /* Sections
21 | ========================================================================== */
22 | /**
23 | * Remove the margin in all browsers (opinionated).
24 | */
25 | body {
26 | margin: 0; }
27 |
28 | /**
29 | * Add the correct display in IE 9-.
30 | */
31 | article,
32 | aside,
33 | footer,
34 | header,
35 | nav,
36 | section {
37 | display: block; }
38 |
39 | /**
40 | * Correct the font size and margin on `h1` elements within `section` and
41 | * `article` contexts in Chrome, Firefox, and Safari.
42 | */
43 | h1 {
44 | font-size: 2em;
45 | margin: 0.67em 0; }
46 |
47 | /* Grouping content
48 | ========================================================================== */
49 | /**
50 | * Add the correct display in IE 9-.
51 | * 1. Add the correct display in IE.
52 | */
53 | figcaption,
54 | figure,
55 | main {
56 | /* 1 */
57 | display: block; }
58 |
59 | /**
60 | * Add the correct margin in IE 8 (removed).
61 | */
62 | /**
63 | * 1. Add the correct box sizing in Firefox.
64 | * 2. Show the overflow in Edge and IE.
65 | */
66 | hr {
67 | box-sizing: content-box;
68 | /* 1 */
69 | height: 0;
70 | /* 1 */
71 | overflow: visible;
72 | /* 2 */ }
73 |
74 | /**
75 | * 1. Correct the inheritance and scaling of font size in all browsers. (removed)
76 | * 2. Correct the odd `em` font sizing in all browsers.
77 | */
78 | /* Text-level semantics
79 | ========================================================================== */
80 | /**
81 | * 1. Remove the gray background on active links in IE 10.
82 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
83 | */
84 | a {
85 | background-color: transparent;
86 | /* 1 */
87 | -webkit-text-decoration-skip: objects;
88 | /* 2 */ }
89 |
90 | /**
91 | * Remove the outline on focused links when they are also active or hovered
92 | * in all browsers (opinionated).
93 | */
94 | a:active,
95 | a:hover {
96 | outline-width: 0; }
97 |
98 | /**
99 | * Modify default styling of address.
100 | */
101 | address {
102 | font-style: normal; }
103 |
104 | /**
105 | * 1. Remove the bottom border in Firefox 39-.
106 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. (removed)
107 | */
108 | /**
109 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
110 | */
111 | b,
112 | strong {
113 | font-weight: inherit; }
114 |
115 | /**
116 | * Add the correct font weight in Chrome, Edge, and Safari.
117 | */
118 | b,
119 | strong {
120 | font-weight: bolder; }
121 |
122 | /**
123 | * 1. Correct the inheritance and scaling of font size in all browsers.
124 | * 2. Correct the odd `em` font sizing in all browsers.
125 | */
126 | code,
127 | kbd,
128 | pre,
129 | samp {
130 | font-family: "SF Mono", "Segoe UI Mono", "Roboto Mono", Menlo, Courier, monospace;
131 | /* 1 (changed) */
132 | font-size: 1em;
133 | /* 2 */ }
134 |
135 | /**
136 | * Add the correct font style in Android 4.3-.
137 | */
138 | dfn {
139 | font-style: italic; }
140 |
141 | /**
142 | * Add the correct background and color in IE 9-. (Removed)
143 | */
144 | /**
145 | * Add the correct font size in all browsers.
146 | */
147 | small {
148 | font-size: 80%;
149 | font-weight: 400;
150 | /* (added) */ }
151 |
152 | /**
153 | * Prevent `sub` and `sup` elements from affecting the line height in
154 | * all browsers.
155 | */
156 | sub,
157 | sup {
158 | font-size: 75%;
159 | line-height: 0;
160 | position: relative;
161 | vertical-align: baseline; }
162 |
163 | sub {
164 | bottom: -0.25em; }
165 |
166 | sup {
167 | top: -0.5em; }
168 |
169 | /* Embedded content
170 | ========================================================================== */
171 | /**
172 | * Add the correct display in IE 9-.
173 | */
174 | audio,
175 | video {
176 | display: inline-block; }
177 |
178 | /**
179 | * Add the correct display in iOS 4-7.
180 | */
181 | audio:not([controls]) {
182 | display: none;
183 | height: 0; }
184 |
185 | /**
186 | * Remove the border on images inside links in IE 10-.
187 | */
188 | img {
189 | border-style: none; }
190 |
191 | /**
192 | * Hide the overflow in IE.
193 | */
194 | svg:not(:root) {
195 | overflow: hidden; }
196 |
197 | /* Forms
198 | ========================================================================== */
199 | /**
200 | * 1. Change the font styles in all browsers (opinionated).
201 | * 2. Remove the margin in Firefox and Safari.
202 | */
203 | button,
204 | input,
205 | optgroup,
206 | select,
207 | textarea {
208 | font-family: inherit;
209 | /* 1 (changed) */
210 | font-size: inherit;
211 | /* 1 (changed) */
212 | line-height: inherit;
213 | /* 1 (changed) */
214 | margin: 0;
215 | /* 2 */ }
216 |
217 | /**
218 | * Show the overflow in IE.
219 | * 1. Show the overflow in Edge.
220 | */
221 | button,
222 | input {
223 | /* 1 */
224 | overflow: visible; }
225 |
226 | /**
227 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
228 | * 1. Remove the inheritance of text transform in Firefox.
229 | */
230 | button,
231 | select {
232 | /* 1 */
233 | text-transform: none; }
234 |
235 | /**
236 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
237 | * controls in Android 4.
238 | * 2. Correct the inability to style clickable types in iOS and Safari.
239 | */
240 | button,
241 | html [type="button"],
242 | [type="reset"],
243 | [type="submit"] {
244 | -webkit-appearance: button;
245 | /* 2 */ }
246 |
247 | /**
248 | * Remove the inner border and padding in Firefox.
249 | */
250 | button::-moz-focus-inner,
251 | [type="button"]::-moz-focus-inner,
252 | [type="reset"]::-moz-focus-inner,
253 | [type="submit"]::-moz-focus-inner {
254 | border-style: none;
255 | padding: 0; }
256 |
257 | /**
258 | * Restore the focus styles unset by the previous rule (removed).
259 | */
260 | /**
261 | * Change the border, margin, and padding in all browsers (opinionated) (changed).
262 | */
263 | fieldset {
264 | border: 0;
265 | margin: 0;
266 | padding: 0; }
267 |
268 | /**
269 | * 1. Correct the text wrapping in Edge and IE.
270 | * 2. Correct the color inheritance from `fieldset` elements in IE.
271 | * 3. Remove the padding so developers are not caught out when they zero out
272 | * `fieldset` elements in all browsers.
273 | */
274 | legend {
275 | box-sizing: border-box;
276 | /* 1 */
277 | color: inherit;
278 | /* 2 */
279 | display: table;
280 | /* 1 */
281 | max-width: 100%;
282 | /* 1 */
283 | padding: 0;
284 | /* 3 */
285 | white-space: normal;
286 | /* 1 */ }
287 |
288 | /**
289 | * 1. Add the correct display in IE 9-.
290 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
291 | */
292 | progress {
293 | display: inline-block;
294 | /* 1 */
295 | vertical-align: baseline;
296 | /* 2 */ }
297 |
298 | /**
299 | * Remove the default vertical scrollbar in IE.
300 | */
301 | textarea {
302 | overflow: auto; }
303 |
304 | /**
305 | * 1. Add the correct box sizing in IE 10-.
306 | * 2. Remove the padding in IE 10-.
307 | */
308 | [type="checkbox"],
309 | [type="radio"] {
310 | box-sizing: border-box;
311 | /* 1 */
312 | padding: 0;
313 | /* 2 */ }
314 |
315 | /**
316 | * Correct the cursor style of increment and decrement buttons in Chrome.
317 | */
318 | [type="number"]::-webkit-inner-spin-button,
319 | [type="number"]::-webkit-outer-spin-button {
320 | height: auto; }
321 |
322 | /**
323 | * 1. Correct the odd appearance in Chrome and Safari.
324 | * 2. Correct the outline style in Safari.
325 | */
326 | [type="search"] {
327 | -webkit-appearance: textfield;
328 | /* 1 */
329 | outline-offset: -2px;
330 | /* 2 */ }
331 |
332 | /**
333 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
334 | */
335 | [type="search"]::-webkit-search-cancel-button,
336 | [type="search"]::-webkit-search-decoration {
337 | -webkit-appearance: none; }
338 |
339 | /**
340 | * 1. Correct the inability to style clickable types in iOS and Safari.
341 | * 2. Change font properties to `inherit` in Safari.
342 | */
343 | ::-webkit-file-upload-button {
344 | -webkit-appearance: button;
345 | /* 1 */
346 | font: inherit;
347 | /* 2 */ }
348 |
349 | /* Interactive
350 | ========================================================================== */
351 | /*
352 | * Add the correct display in IE 9-.
353 | * 1. Add the correct display in Edge, IE, and Firefox.
354 | */
355 | details,
356 | menu {
357 | display: block; }
358 |
359 | /*
360 | * Add the correct display in all browsers.
361 | */
362 | summary {
363 | display: list-item;
364 | outline: none; }
365 |
366 | /* Scripting
367 | ========================================================================== */
368 | /**
369 | * Add the correct display in IE 9-.
370 | */
371 | canvas {
372 | display: inline-block; }
373 |
374 | /**
375 | * Add the correct display in IE.
376 | */
377 | template {
378 | display: none; }
379 |
380 | /* Hidden
381 | ========================================================================== */
382 | /**
383 | * Add the correct display in IE 10-.
384 | */
385 | [hidden] {
386 | display: none; }
387 |
388 | *,
389 | *::before,
390 | *::after {
391 | box-sizing: inherit; }
392 |
393 | html {
394 | box-sizing: border-box;
395 | font-size: 20px;
396 | line-height: 1.5;
397 | -webkit-tap-highlight-color: transparent; }
398 |
399 | body {
400 | background: #fff;
401 | color: #1e1e1e;
402 | font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
403 | font-size: 0.8rem;
404 | overflow-x: hidden;
405 | text-rendering: optimizeLegibility; }
406 |
407 | a {
408 | color: #46c2fe;
409 | outline: none;
410 | text-decoration: none; }
411 | a:focus {
412 | box-shadow: 0 0 0 0.1rem rgba(70, 194, 254, 0.2); }
413 | a:focus, a:hover, a:active, a.active {
414 | color: #2dbafe;
415 | text-decoration: underline; }
416 |
417 | h1,
418 | h2,
419 | h3,
420 | h4,
421 | h5,
422 | h6 {
423 | color: inherit;
424 | font-weight: 500;
425 | line-height: 1.2;
426 | margin-bottom: .5em;
427 | margin-top: 0; }
428 |
429 | .h1,
430 | .h2,
431 | .h3,
432 | .h4,
433 | .h5,
434 | .h6 {
435 | font-weight: 500; }
436 |
437 | h1,
438 | .h1 {
439 | font-size: 2rem; }
440 |
441 | h2,
442 | .h2 {
443 | font-size: 1.6rem; }
444 |
445 | h3,
446 | .h3 {
447 | font-size: 1.4rem; }
448 |
449 | h4,
450 | .h4 {
451 | font-size: 1.2rem; }
452 |
453 | h5,
454 | .h5 {
455 | font-size: 1rem; }
456 |
457 | h6,
458 | .h6 {
459 | font-size: .8rem; }
460 |
461 | p {
462 | margin: 0 0 1rem; }
463 |
464 | a,
465 | ins,
466 | u {
467 | text-decoration-skip: ink edges; }
468 |
469 | abbr[title] {
470 | border-bottom: 0.05rem dotted;
471 | cursor: help;
472 | text-decoration: none; }
473 |
474 | kbd {
475 | border-radius: 0.1rem;
476 | line-height: 1.2;
477 | padding: .1rem .15rem;
478 | background: #111;
479 | color: #fff;
480 | font-size: 0.7rem; }
481 |
482 | mark {
483 | background: #ffe9b3;
484 | color: #1e1e1e;
485 | border-radius: 0.1rem;
486 | padding: .05rem; }
487 |
488 | blockquote {
489 | border-left: 0.1rem solid #aaaaaa;
490 | margin-left: 0;
491 | padding: 0.4rem 0.8rem; }
492 | blockquote p:last-child {
493 | margin-bottom: 0; }
494 |
495 | ul,
496 | ol {
497 | margin: 0.8rem 0 0.8rem 0.8rem;
498 | padding: 0; }
499 | ul ul,
500 | ul ol,
501 | ol ul,
502 | ol ol {
503 | margin: 0.8rem 0 0.8rem 0.8rem; }
504 | ul li,
505 | ol li {
506 | margin-top: 0.4rem; }
507 |
508 | ul {
509 | list-style: disc inside; }
510 | ul ul {
511 | list-style-type: circle; }
512 |
513 | ol {
514 | list-style: decimal inside; }
515 | ol ol {
516 | list-style-type: lower-alpha; }
517 |
518 | dl dt {
519 | font-weight: bold; }
520 | dl dd {
521 | margin: 0.4rem 0 0.8rem 0; }
522 |
523 | .btn {
524 | transition: all .2s ease;
525 | appearance: none;
526 | background: #fff;
527 | border: 0.05rem solid #46c2fe;
528 | border-radius: 0.1rem;
529 | color: #46c2fe;
530 | cursor: pointer;
531 | display: inline-block;
532 | font-size: 0.8rem;
533 | height: 1.8rem;
534 | line-height: 1rem;
535 | outline: none;
536 | padding: 0.35rem 0.4rem;
537 | text-align: center;
538 | text-decoration: none;
539 | user-select: none;
540 | vertical-align: middle;
541 | white-space: nowrap; }
542 | .btn:focus {
543 | box-shadow: 0 0 0 0.1rem rgba(70, 194, 254, 0.2); }
544 | .btn:focus, .btn:hover {
545 | background: #d86550;
546 | border-color: #37bdfe;
547 | text-decoration: none; }
548 | .btn:active, .btn.active {
549 | background: #37bdfe;
550 | border-color: #1db5fe;
551 | color: #fff;
552 | text-decoration: none; }
553 | .btn:active.loading::after, .btn.active.loading::after {
554 | border-bottom-color: #fff;
555 | border-left-color: #fff; }
556 | .btn[disabled], .btn:disabled, .btn.disabled {
557 | cursor: default;
558 | opacity: .5;
559 | pointer-events: none; }
560 | .btn.btn-primary {
561 | background: #46c2fe;
562 | border-color: #37bdfe;
563 | color: #fff; }
564 | .btn.btn-primary:focus, .btn.btn-primary:hover {
565 | background: #2dbafe;
566 | border-color: #1db5fe;
567 | color: #fff; }
568 | .btn.btn-primary:active, .btn.btn-primary.active {
569 | background: #22b6fe;
570 | border-color: #13b1fe;
571 | color: #fff; }
572 | .btn.btn-primary.loading::after {
573 | border-bottom-color: #fff;
574 | border-left-color: #fff; }
575 | .btn.btn-success {
576 | background: #32b643;
577 | border-color: #2faa3f;
578 | color: #fff; }
579 | .btn.btn-success:focus {
580 | box-shadow: 0 0 0 0.1rem rgba(50, 182, 67, 0.2); }
581 | .btn.btn-success:focus, .btn.btn-success:hover {
582 | background: #30ae40;
583 | border-color: #2da23c;
584 | color: #fff; }
585 | .btn.btn-success:active, .btn.btn-success.active {
586 | background: #2a9a39;
587 | border-color: #278e34;
588 | color: #fff; }
589 | .btn.btn-success.loading::after {
590 | border-bottom-color: #fff;
591 | border-left-color: #fff; }
592 | .btn.btn-error {
593 | background: #e85600;
594 | border-color: #d95000;
595 | color: #fff; }
596 | .btn.btn-error:focus {
597 | box-shadow: 0 0 0 0.1rem rgba(232, 86, 0, 0.2); }
598 | .btn.btn-error:focus, .btn.btn-error:hover {
599 | background: #de5200;
600 | border-color: #cf4d00;
601 | color: #fff; }
602 | .btn.btn-error:active, .btn.btn-error.active {
603 | background: #c44900;
604 | border-color: #b54300;
605 | color: #fff; }
606 | .btn.btn-error.loading::after {
607 | border-bottom-color: #fff;
608 | border-left-color: #fff; }
609 | .btn.btn-link {
610 | background: transparent;
611 | border-color: transparent;
612 | color: #46c2fe; }
613 | .btn.btn-link:focus, .btn.btn-link:hover, .btn.btn-link:active, .btn.btn-link.active {
614 | color: #2dbafe; }
615 | .btn.btn-sm {
616 | font-size: 0.7rem;
617 | height: 1.4rem;
618 | padding: 0.15rem 0.3rem; }
619 | .btn.btn-lg {
620 | font-size: 0.9rem;
621 | height: 2rem;
622 | padding: 0.45rem 0.6rem; }
623 | .btn.btn-block {
624 | display: block;
625 | width: 100%; }
626 | .btn.btn-action {
627 | width: 1.8rem;
628 | padding-left: 0;
629 | padding-right: 0; }
630 | .btn.btn-action.btn-sm {
631 | width: 1.4rem; }
632 | .btn.btn-action.btn-lg {
633 | width: 2rem; }
634 | .btn.btn-clear {
635 | background: transparent;
636 | border: 0;
637 | color: currentColor;
638 | height: 0.8rem;
639 | line-height: 0.8rem;
640 | margin-left: 0.2rem;
641 | margin-right: -2px;
642 | opacity: 1;
643 | padding: 0;
644 | text-decoration: none;
645 | width: 0.8rem; }
646 | .btn.btn-clear:hover {
647 | opacity: .95; }
648 | .btn.btn-clear::before {
649 | content: "\2715"; }
650 |
651 | .btn-group {
652 | display: inline-flex;
653 | flex-wrap: wrap; }
654 | .btn-group .btn {
655 | flex: 1 0 auto; }
656 | .btn-group .btn:first-child:not(:last-child) {
657 | border-bottom-right-radius: 0;
658 | border-top-right-radius: 0; }
659 | .btn-group .btn:not(:first-child):not(:last-child) {
660 | border-radius: 0;
661 | margin-left: -0.05rem; }
662 | .btn-group .btn:last-child:not(:first-child) {
663 | border-bottom-left-radius: 0;
664 | border-top-left-radius: 0;
665 | margin-left: -0.05rem; }
666 | .btn-group .btn:focus, .btn-group .btn:hover, .btn-group .btn:active, .btn-group .btn.active {
667 | z-index: 1; }
668 | .btn-group.btn-group-block {
669 | display: flex; }
670 | .btn-group.btn-group-block .btn {
671 | flex: 1 0 0; }
672 |
673 | .form-group:not(:last-child) {
674 | margin-bottom: 0.4rem; }
675 |
676 | fieldset {
677 | margin-bottom: 0.8rem; }
678 |
679 | legend {
680 | font-size: 0.9rem;
681 | font-weight: 500;
682 | margin-bottom: 0.8rem; }
683 |
684 | .form-label {
685 | display: block;
686 | line-height: 1rem;
687 | padding: 0.4rem 0; }
688 | .form-label.label-sm {
689 | font-size: 0.7rem;
690 | padding: 0.2rem 0; }
691 | .form-label.label-lg {
692 | font-size: 0.9rem;
693 | padding: 0.5rem 0; }
694 |
695 | .form-input {
696 | transition: all .2s ease;
697 | appearance: none;
698 | background: #fff;
699 | background-image: none;
700 | border: 0.05rem solid #919191;
701 | border-radius: 0.1rem;
702 | color: #1e1e1e;
703 | display: block;
704 | font-size: 0.8rem;
705 | height: 1.8rem;
706 | line-height: 1rem;
707 | max-width: 100%;
708 | outline: none;
709 | padding: 0.35rem 0.4rem;
710 | position: relative;
711 | width: 100%; }
712 | .form-input:focus {
713 | box-shadow: 0 0 0 0.1rem rgba(70, 194, 254, 0.2);
714 | border-color: #46c2fe; }
715 | .form-input::placeholder {
716 | color: #777777; }
717 | .form-input.input-sm {
718 | font-size: 0.7rem;
719 | height: 1.4rem;
720 | padding: 0.15rem 0.3rem; }
721 | .form-input.input-lg {
722 | font-size: 0.9rem;
723 | height: 2rem;
724 | padding: 0.45rem 0.6rem; }
725 | .form-input.input-inline {
726 | display: inline-block;
727 | vertical-align: middle;
728 | width: auto; }
729 | .form-input[type="file"] {
730 | height: auto; }
731 |
732 | textarea.form-input {
733 | height: auto; }
734 |
735 | .form-input-hint {
736 | color: #777777;
737 | font-size: 0.7rem;
738 | margin-top: 0.2rem; }
739 | .has-success .form-input-hint, .is-success + .form-input-hint {
740 | color: #32b643; }
741 | .has-error .form-input-hint, .is-error + .form-input-hint {
742 | color: #e85600; }
743 |
744 | .form-select {
745 | appearance: none;
746 | border: 0.05rem solid #919191;
747 | border-radius: 0.1rem;
748 | color: inherit;
749 | font-size: 0.8rem;
750 | height: 1.8rem;
751 | line-height: 1rem;
752 | outline: none;
753 | padding: 0.35rem 0.4rem;
754 | vertical-align: middle;
755 | width: 100%; }
756 | .form-select[size], .form-select[multiple] {
757 | height: auto; }
758 | .form-select[size] option, .form-select[multiple] option {
759 | padding: 0.1rem 0.2rem; }
760 | .form-select:not([multiple]):not([size]) {
761 | background: #fff url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%204%205'%3E%3Cpath%20fill='%23667189'%20d='M2%200L0%202h4zm0%205L0%203h4z'/%3E%3C/svg%3E") no-repeat right 0.35rem center/0.4rem 0.5rem;
762 | padding-right: 1.2rem; }
763 | .form-select:focus {
764 | box-shadow: 0 0 0 0.1rem rgba(70, 194, 254, 0.2);
765 | border-color: #46c2fe; }
766 | .form-select::-ms-expand {
767 | display: none; }
768 | .form-select.select-sm {
769 | font-size: 0.7rem;
770 | height: 1.4rem;
771 | padding: 0.15rem 1.1rem 0.15rem 0.3rem; }
772 | .form-select.select-lg {
773 | font-size: 0.9rem;
774 | height: 2rem;
775 | padding: 0.45rem 1.4rem 0.45rem 0.6rem; }
776 |
777 | .has-icon-left,
778 | .has-icon-right {
779 | position: relative; }
780 | .has-icon-left .form-icon,
781 | .has-icon-right .form-icon {
782 | height: 0.8rem;
783 | margin: 0 0.35rem;
784 | position: absolute;
785 | top: 50%;
786 | transform: translateY(-50%);
787 | width: 0.8rem;
788 | z-index: 2; }
789 |
790 | .has-icon-left .form-icon {
791 | left: 0.05rem; }
792 | .has-icon-left .form-input {
793 | padding-left: 1.5rem; }
794 |
795 | .has-icon-right .form-icon {
796 | right: 0.05rem; }
797 | .has-icon-right .form-input {
798 | padding-right: 1.5rem; }
799 |
800 | .form-checkbox,
801 | .form-radio,
802 | .form-switch {
803 | display: inline-block;
804 | line-height: 1rem;
805 | margin: 0.2rem 0;
806 | min-height: 1.2rem;
807 | padding: 0.2rem 0.4rem 0.2rem 1.2rem;
808 | position: relative; }
809 | .form-checkbox input,
810 | .form-radio input,
811 | .form-switch input {
812 | clip: rect(0, 0, 0, 0);
813 | height: 1px;
814 | margin: -1px;
815 | overflow: hidden;
816 | position: absolute;
817 | width: 1px; }
818 | .form-checkbox input:focus + .form-icon,
819 | .form-radio input:focus + .form-icon,
820 | .form-switch input:focus + .form-icon {
821 | box-shadow: 0 0 0 0.1rem rgba(70, 194, 254, 0.2);
822 | border-color: #46c2fe; }
823 | .form-checkbox input:checked + .form-icon,
824 | .form-radio input:checked + .form-icon,
825 | .form-switch input:checked + .form-icon {
826 | background: #46c2fe;
827 | border-color: #46c2fe; }
828 | .form-checkbox .form-icon,
829 | .form-radio .form-icon,
830 | .form-switch .form-icon {
831 | transition: all .2s ease;
832 | border: 0.05rem solid #919191;
833 | cursor: pointer;
834 | display: inline-block;
835 | position: absolute; }
836 | .form-checkbox.input-sm,
837 | .form-radio.input-sm,
838 | .form-switch.input-sm {
839 | font-size: 0.7rem;
840 | margin: 0; }
841 | .form-checkbox.input-lg,
842 | .form-radio.input-lg,
843 | .form-switch.input-lg {
844 | font-size: 0.9rem;
845 | margin: 0.3rem 0; }
846 |
847 | .form-checkbox .form-icon,
848 | .form-radio .form-icon {
849 | background: #fff;
850 | height: 0.8rem;
851 | left: 0;
852 | top: 0.3rem;
853 | width: 0.8rem; }
854 | .form-checkbox input:active + .form-icon,
855 | .form-radio input:active + .form-icon {
856 | background: #b2b2b2; }
857 |
858 | .form-checkbox .form-icon {
859 | border-radius: 0.1rem; }
860 | .form-checkbox input:checked + .form-icon::before {
861 | background-clip: padding-box;
862 | border: 0.1rem solid #fff;
863 | border-left-width: 0;
864 | border-top-width: 0;
865 | content: "";
866 | height: 12px;
867 | left: 50%;
868 | margin-left: -4px;
869 | margin-top: -8px;
870 | position: absolute;
871 | top: 50%;
872 | transform: rotate(45deg);
873 | width: 8px; }
874 | .form-checkbox input:indeterminate + .form-icon {
875 | background: #46c2fe;
876 | border-color: #46c2fe; }
877 | .form-checkbox input:indeterminate + .form-icon::before {
878 | background: #fff;
879 | content: "";
880 | height: 2px;
881 | left: 50%;
882 | margin-left: -5px;
883 | margin-top: -1px;
884 | position: absolute;
885 | top: 50%;
886 | width: 10px; }
887 |
888 | .form-radio .form-icon {
889 | border-radius: 50%; }
890 | .form-radio input:checked + .form-icon::before {
891 | background: #fff;
892 | border-radius: 50%;
893 | content: "";
894 | height: 4px;
895 | left: 50%;
896 | position: absolute;
897 | top: 50%;
898 | transform: translate(-50%, -50%);
899 | width: 4px; }
900 |
901 | .form-switch {
902 | padding-left: 2rem; }
903 | .form-switch .form-icon {
904 | background: #aaaaaa;
905 | background-clip: padding-box;
906 | border-radius: 0.45rem;
907 | height: 0.9rem;
908 | left: 0;
909 | top: 0.25rem;
910 | width: 1.6rem; }
911 | .form-switch .form-icon::before {
912 | transition: all .2s ease;
913 | background: #fff;
914 | border-radius: 50%;
915 | content: "";
916 | display: block;
917 | height: 0.8rem;
918 | left: 0;
919 | position: absolute;
920 | top: 0;
921 | width: 0.8rem; }
922 | .form-switch input:checked + .form-icon::before {
923 | left: 14px; }
924 | .form-switch input:active + .form-icon::before {
925 | background: #b9b9b9; }
926 |
927 | .input-group {
928 | display: flex; }
929 | .input-group .input-group-addon {
930 | background: #b9b9b9;
931 | border: 0.05rem solid #919191;
932 | border-radius: 0.1rem;
933 | line-height: 1rem;
934 | padding: 0.35rem 0.4rem;
935 | white-space: nowrap; }
936 | .input-group .input-group-addon.addon-sm {
937 | font-size: 0.7rem;
938 | padding: 0.15rem 0.3rem; }
939 | .input-group .input-group-addon.addon-lg {
940 | font-size: 0.9rem;
941 | padding: 0.45rem 0.6rem; }
942 | .input-group .form-input,
943 | .input-group .form-select {
944 | flex: 1 1 auto; }
945 | .input-group .input-group-btn {
946 | z-index: 1; }
947 | .input-group .form-input:first-child:not(:last-child),
948 | .input-group .form-select:first-child:not(:last-child),
949 | .input-group .input-group-addon:first-child:not(:last-child),
950 | .input-group .input-group-btn:first-child:not(:last-child) {
951 | border-bottom-right-radius: 0;
952 | border-top-right-radius: 0; }
953 | .input-group .form-input:not(:first-child):not(:last-child),
954 | .input-group .form-select:not(:first-child):not(:last-child),
955 | .input-group .input-group-addon:not(:first-child):not(:last-child),
956 | .input-group .input-group-btn:not(:first-child):not(:last-child) {
957 | border-radius: 0;
958 | margin-left: -0.05rem; }
959 | .input-group .form-input:last-child:not(:first-child),
960 | .input-group .form-select:last-child:not(:first-child),
961 | .input-group .input-group-addon:last-child:not(:first-child),
962 | .input-group .input-group-btn:last-child:not(:first-child) {
963 | border-bottom-left-radius: 0;
964 | border-top-left-radius: 0;
965 | margin-left: -0.05rem; }
966 | .input-group .form-input:focus,
967 | .input-group .form-select:focus,
968 | .input-group .input-group-addon:focus,
969 | .input-group .input-group-btn:focus {
970 | z-index: 2; }
971 | .input-group .form-select {
972 | width: auto; }
973 | .input-group.input-inline {
974 | display: inline-flex; }
975 |
976 | .has-success .form-input, .form-input.is-success,
977 | .has-success .form-select, .form-select.is-success {
978 | border-color: #32b643; }
979 | .has-success .form-input:focus, .form-input.is-success:focus,
980 | .has-success .form-select:focus, .form-select.is-success:focus {
981 | box-shadow: 0 0 0 0.1rem rgba(50, 182, 67, 0.2); }
982 | .has-error .form-input, .form-input.is-error, .has-error .form-select, .form-select.is-error {
983 | border-color: #e85600; }
984 | .has-error .form-input:focus, .form-input.is-error:focus, .has-error .form-select:focus, .form-select.is-error:focus {
985 | box-shadow: 0 0 0 0.1rem rgba(232, 86, 0, 0.2); }
986 |
987 | .has-error .form-checkbox .form-icon, .form-checkbox.is-error .form-icon,
988 | .has-error .form-radio .form-icon, .form-radio.is-error .form-icon,
989 | .has-error .form-switch .form-icon, .form-switch.is-error .form-icon {
990 | border-color: #e85600; }
991 | .has-error .form-checkbox input:checked + .form-icon, .form-checkbox.is-error input:checked + .form-icon,
992 | .has-error .form-radio input:checked + .form-icon, .form-radio.is-error input:checked + .form-icon,
993 | .has-error .form-switch input:checked + .form-icon, .form-switch.is-error input:checked + .form-icon {
994 | background: #e85600;
995 | border-color: #e85600; }
996 | .has-error .form-checkbox input:focus + .form-icon, .form-checkbox.is-error input:focus + .form-icon,
997 | .has-error .form-radio input:focus + .form-icon, .form-radio.is-error input:focus + .form-icon,
998 | .has-error .form-switch input:focus + .form-icon, .form-switch.is-error input:focus + .form-icon {
999 | box-shadow: 0 0 0 0.1rem rgba(232, 86, 0, 0.2);
1000 | border-color: #e85600; }
1001 |
1002 | .form-input:not(:placeholder-shown):invalid {
1003 | border-color: #e85600; }
1004 | .form-input:not(:placeholder-shown):invalid:focus {
1005 | box-shadow: 0 0 0 0.1rem rgba(232, 86, 0, 0.2); }
1006 | .form-input:not(:placeholder-shown):invalid + .form-input-hint {
1007 | color: #e85600; }
1008 |
1009 | .form-input:disabled, .form-input.disabled,
1010 | .form-select:disabled,
1011 | .form-select.disabled {
1012 | background-color: #b2b2b2;
1013 | cursor: not-allowed;
1014 | opacity: .5; }
1015 |
1016 | .form-input[readonly] {
1017 | background-color: #b9b9b9; }
1018 |
1019 | input:disabled + .form-icon, input.disabled + .form-icon {
1020 | background: #b2b2b2;
1021 | cursor: not-allowed;
1022 | opacity: .5; }
1023 |
1024 | .form-switch input:disabled + .form-icon::before, .form-switch input.disabled + .form-icon::before {
1025 | background: #fff; }
1026 |
1027 | .form-horizontal {
1028 | padding: 0.4rem 0; }
1029 | .form-horizontal .form-group {
1030 | display: flex;
1031 | flex-wrap: wrap; }
1032 |
1033 | .label {
1034 | border-radius: 0.1rem;
1035 | line-height: 1.2;
1036 | padding: .1rem .15rem;
1037 | background: #b2b2b2;
1038 | color: #2b2b2b;
1039 | display: inline-block; }
1040 | .label.label-rounded {
1041 | border-radius: 5rem;
1042 | padding-left: .4rem;
1043 | padding-right: .4rem; }
1044 | .label.label-primary {
1045 | background: #46c2fe;
1046 | color: #fff; }
1047 | .label.label-secondary {
1048 | background: #d86550;
1049 | color: #46c2fe; }
1050 | .label.label-success {
1051 | background: #32b643;
1052 | color: #fff; }
1053 | .label.label-warning {
1054 | background: #ffb700;
1055 | color: #fff; }
1056 | .label.label-error {
1057 | background: #e85600;
1058 | color: #fff; }
1059 |
1060 | .container {
1061 | margin-left: auto;
1062 | margin-right: auto;
1063 | padding-left: 0.4rem;
1064 | padding-right: 0.4rem;
1065 | width: 100%; }
1066 | .container.grid-xl {
1067 | max-width: 1296px; }
1068 | .container.grid-lg {
1069 | max-width: 976px; }
1070 | .container.grid-md {
1071 | max-width: 856px; }
1072 | .container.grid-sm {
1073 | max-width: 616px; }
1074 | .container.grid-xs {
1075 | max-width: 496px; }
1076 |
1077 | .show-xs,
1078 | .show-sm,
1079 | .show-md,
1080 | .show-lg,
1081 | .show-xl {
1082 | display: none !important; }
1083 |
1084 | .columns {
1085 | display: flex;
1086 | flex-wrap: wrap;
1087 | margin-left: -0.4rem;
1088 | margin-right: -0.4rem; }
1089 | .columns.col-gapless {
1090 | margin-left: 0;
1091 | margin-right: 0; }
1092 | .columns.col-gapless > .column {
1093 | padding-left: 0;
1094 | padding-right: 0; }
1095 | .columns.col-oneline {
1096 | flex-wrap: nowrap;
1097 | overflow-x: auto; }
1098 |
1099 | .column {
1100 | flex: 1;
1101 | max-width: 100%;
1102 | padding-left: 0.4rem;
1103 | padding-right: 0.4rem; }
1104 | .column.col-12, .column.col-11, .column.col-10, .column.col-9, .column.col-8, .column.col-7, .column.col-6, .column.col-5, .column.col-4, .column.col-3, .column.col-2, .column.col-1 {
1105 | flex: none; }
1106 |
1107 | .col-12 {
1108 | width: 100%; }
1109 |
1110 | .col-11 {
1111 | width: 91.66666667%; }
1112 |
1113 | .col-10 {
1114 | width: 83.33333333%; }
1115 |
1116 | .col-9 {
1117 | width: 75%; }
1118 |
1119 | .col-8 {
1120 | width: 66.66666667%; }
1121 |
1122 | .col-7 {
1123 | width: 58.33333333%; }
1124 |
1125 | .col-6 {
1126 | width: 50%; }
1127 |
1128 | .col-5 {
1129 | width: 41.66666667%; }
1130 |
1131 | .col-4 {
1132 | width: 33.33333333%; }
1133 |
1134 | .col-3 {
1135 | width: 25%; }
1136 |
1137 | .col-2 {
1138 | width: 16.66666667%; }
1139 |
1140 | .col-1 {
1141 | width: 8.33333333%; }
1142 |
1143 | .col-auto {
1144 | flex: 0 0 auto;
1145 | max-width: none;
1146 | width: auto; }
1147 |
1148 | .col-mx-auto {
1149 | margin-left: auto;
1150 | margin-right: auto; }
1151 |
1152 | .col-ml-auto {
1153 | margin-left: auto; }
1154 |
1155 | .col-mr-auto {
1156 | margin-right: auto; }
1157 |
1158 | @media (max-width: 1280px) {
1159 | .col-xl-12,
1160 | .col-xl-11,
1161 | .col-xl-10,
1162 | .col-xl-9,
1163 | .col-xl-8,
1164 | .col-xl-7,
1165 | .col-xl-6,
1166 | .col-xl-5,
1167 | .col-xl-4,
1168 | .col-xl-3,
1169 | .col-xl-2,
1170 | .col-xl-1 {
1171 | flex: none; }
1172 |
1173 | .col-xl-12 {
1174 | width: 100%; }
1175 |
1176 | .col-xl-11 {
1177 | width: 91.66666667%; }
1178 |
1179 | .col-xl-10 {
1180 | width: 83.33333333%; }
1181 |
1182 | .col-xl-9 {
1183 | width: 75%; }
1184 |
1185 | .col-xl-8 {
1186 | width: 66.66666667%; }
1187 |
1188 | .col-xl-7 {
1189 | width: 58.33333333%; }
1190 |
1191 | .col-xl-6 {
1192 | width: 50%; }
1193 |
1194 | .col-xl-5 {
1195 | width: 41.66666667%; }
1196 |
1197 | .col-xl-4 {
1198 | width: 33.33333333%; }
1199 |
1200 | .col-xl-3 {
1201 | width: 25%; }
1202 |
1203 | .col-xl-2 {
1204 | width: 16.66666667%; }
1205 |
1206 | .col-xl-1 {
1207 | width: 8.33333333%; }
1208 |
1209 | .hide-xl {
1210 | display: none !important; }
1211 |
1212 | .show-xl {
1213 | display: block !important; } }
1214 | @media (max-width: 960px) {
1215 | .col-lg-12,
1216 | .col-lg-11,
1217 | .col-lg-10,
1218 | .col-lg-9,
1219 | .col-lg-8,
1220 | .col-lg-7,
1221 | .col-lg-6,
1222 | .col-lg-5,
1223 | .col-lg-4,
1224 | .col-lg-3,
1225 | .col-lg-2,
1226 | .col-lg-1 {
1227 | flex: none; }
1228 |
1229 | .col-lg-12 {
1230 | width: 100%; }
1231 |
1232 | .col-lg-11 {
1233 | width: 91.66666667%; }
1234 |
1235 | .col-lg-10 {
1236 | width: 83.33333333%; }
1237 |
1238 | .col-lg-9 {
1239 | width: 75%; }
1240 |
1241 | .col-lg-8 {
1242 | width: 66.66666667%; }
1243 |
1244 | .col-lg-7 {
1245 | width: 58.33333333%; }
1246 |
1247 | .col-lg-6 {
1248 | width: 50%; }
1249 |
1250 | .col-lg-5 {
1251 | width: 41.66666667%; }
1252 |
1253 | .col-lg-4 {
1254 | width: 33.33333333%; }
1255 |
1256 | .col-lg-3 {
1257 | width: 25%; }
1258 |
1259 | .col-lg-2 {
1260 | width: 16.66666667%; }
1261 |
1262 | .col-lg-1 {
1263 | width: 8.33333333%; }
1264 |
1265 | .hide-lg {
1266 | display: none !important; }
1267 |
1268 | .show-lg {
1269 | display: block !important; } }
1270 | @media (max-width: 840px) {
1271 | .col-md-12,
1272 | .col-md-11,
1273 | .col-md-10,
1274 | .col-md-9,
1275 | .col-md-8,
1276 | .col-md-7,
1277 | .col-md-6,
1278 | .col-md-5,
1279 | .col-md-4,
1280 | .col-md-3,
1281 | .col-md-2,
1282 | .col-md-1 {
1283 | flex: none; }
1284 |
1285 | .col-md-12 {
1286 | width: 100%; }
1287 |
1288 | .col-md-11 {
1289 | width: 91.66666667%; }
1290 |
1291 | .col-md-10 {
1292 | width: 83.33333333%; }
1293 |
1294 | .col-md-9 {
1295 | width: 75%; }
1296 |
1297 | .col-md-8 {
1298 | width: 66.66666667%; }
1299 |
1300 | .col-md-7 {
1301 | width: 58.33333333%; }
1302 |
1303 | .col-md-6 {
1304 | width: 50%; }
1305 |
1306 | .col-md-5 {
1307 | width: 41.66666667%; }
1308 |
1309 | .col-md-4 {
1310 | width: 33.33333333%; }
1311 |
1312 | .col-md-3 {
1313 | width: 25%; }
1314 |
1315 | .col-md-2 {
1316 | width: 16.66666667%; }
1317 |
1318 | .col-md-1 {
1319 | width: 8.33333333%; }
1320 |
1321 | .hide-md {
1322 | display: none !important; }
1323 |
1324 | .show-md {
1325 | display: block !important; } }
1326 | @media (max-width: 600px) {
1327 | .col-sm-12,
1328 | .col-sm-11,
1329 | .col-sm-10,
1330 | .col-sm-9,
1331 | .col-sm-8,
1332 | .col-sm-7,
1333 | .col-sm-6,
1334 | .col-sm-5,
1335 | .col-sm-4,
1336 | .col-sm-3,
1337 | .col-sm-2,
1338 | .col-sm-1 {
1339 | flex: none; }
1340 |
1341 | .col-sm-12 {
1342 | width: 100%; }
1343 |
1344 | .col-sm-11 {
1345 | width: 91.66666667%; }
1346 |
1347 | .col-sm-10 {
1348 | width: 83.33333333%; }
1349 |
1350 | .col-sm-9 {
1351 | width: 75%; }
1352 |
1353 | .col-sm-8 {
1354 | width: 66.66666667%; }
1355 |
1356 | .col-sm-7 {
1357 | width: 58.33333333%; }
1358 |
1359 | .col-sm-6 {
1360 | width: 50%; }
1361 |
1362 | .col-sm-5 {
1363 | width: 41.66666667%; }
1364 |
1365 | .col-sm-4 {
1366 | width: 33.33333333%; }
1367 |
1368 | .col-sm-3 {
1369 | width: 25%; }
1370 |
1371 | .col-sm-2 {
1372 | width: 16.66666667%; }
1373 |
1374 | .col-sm-1 {
1375 | width: 8.33333333%; }
1376 |
1377 | .hide-sm {
1378 | display: none !important; }
1379 |
1380 | .show-sm {
1381 | display: block !important; } }
1382 | @media (max-width: 480px) {
1383 | .col-xs-12,
1384 | .col-xs-11,
1385 | .col-xs-10,
1386 | .col-xs-9,
1387 | .col-xs-8,
1388 | .col-xs-7,
1389 | .col-xs-6,
1390 | .col-xs-5,
1391 | .col-xs-4,
1392 | .col-xs-3,
1393 | .col-xs-2,
1394 | .col-xs-1 {
1395 | flex: none; }
1396 |
1397 | .col-xs-12 {
1398 | width: 100%; }
1399 |
1400 | .col-xs-11 {
1401 | width: 91.66666667%; }
1402 |
1403 | .col-xs-10 {
1404 | width: 83.33333333%; }
1405 |
1406 | .col-xs-9 {
1407 | width: 75%; }
1408 |
1409 | .col-xs-8 {
1410 | width: 66.66666667%; }
1411 |
1412 | .col-xs-7 {
1413 | width: 58.33333333%; }
1414 |
1415 | .col-xs-6 {
1416 | width: 50%; }
1417 |
1418 | .col-xs-5 {
1419 | width: 41.66666667%; }
1420 |
1421 | .col-xs-4 {
1422 | width: 33.33333333%; }
1423 |
1424 | .col-xs-3 {
1425 | width: 25%; }
1426 |
1427 | .col-xs-2 {
1428 | width: 16.66666667%; }
1429 |
1430 | .col-xs-1 {
1431 | width: 8.33333333%; }
1432 |
1433 | .hide-xs {
1434 | display: none !important; }
1435 |
1436 | .show-xs {
1437 | display: block !important; } }
1438 | @keyframes loading {
1439 | 0% {
1440 | transform: rotate(0deg); }
1441 | 100% {
1442 | transform: rotate(360deg); } }
1443 | @keyframes slide-down {
1444 | 0% {
1445 | opacity: 0;
1446 | transform: translateY(-1.6rem); }
1447 | 100% {
1448 | opacity: 1;
1449 | transform: translateY(0); } }
1450 | .text-primary {
1451 | color: #46c2fe; }
1452 |
1453 | a.text-primary:focus, a.text-primary:hover {
1454 | color: #2dbafe; }
1455 |
1456 | .text-secondary {
1457 | color: #d55a43; }
1458 |
1459 | a.text-secondary:focus, a.text-secondary:hover {
1460 | color: #d1482f; }
1461 |
1462 | .text-gray {
1463 | color: #777777; }
1464 |
1465 | a.text-gray:focus, a.text-gray:hover {
1466 | color: #6a6a6a; }
1467 |
1468 | .text-light {
1469 | color: #fff; }
1470 |
1471 | a.text-light:focus, a.text-light:hover {
1472 | color: #f2f2f2; }
1473 |
1474 | .text-success {
1475 | color: #32b643; }
1476 |
1477 | a.text-success:focus, a.text-success:hover {
1478 | color: #2da23c; }
1479 |
1480 | .text-warning {
1481 | color: #ffb700; }
1482 |
1483 | a.text-warning:focus, a.text-warning:hover {
1484 | color: #e6a500; }
1485 |
1486 | .text-error {
1487 | color: #e85600; }
1488 |
1489 | a.text-error:focus, a.text-error:hover {
1490 | color: #cf4d00; }
1491 |
1492 | .bg-primary {
1493 | background: #46c2fe; }
1494 |
1495 | .bg-secondary {
1496 | background: #d86550;
1497 | color: #fff; }
1498 |
1499 | .bg-dark {
1500 | background: #111;
1501 | color: #fff; }
1502 |
1503 | .bg-gray {
1504 | background: #b9b9b9; }
1505 |
1506 | .bg-success {
1507 | background: #32b643;
1508 | color: #fff; }
1509 |
1510 | .bg-warning {
1511 | background: #ffb700;
1512 | color: #fff; }
1513 |
1514 | .bg-error {
1515 | background: #e85600;
1516 | color: #fff; }
1517 |
1518 | .c-hand {
1519 | cursor: pointer; }
1520 |
1521 | .c-move {
1522 | cursor: move; }
1523 |
1524 | .c-zoom-in {
1525 | cursor: zoom-in; }
1526 |
1527 | .c-zoom-out {
1528 | cursor: zoom-out; }
1529 |
1530 | .c-not-allowed {
1531 | cursor: not-allowed; }
1532 |
1533 | .c-auto {
1534 | cursor: auto; }
1535 |
1536 | .d-block {
1537 | display: block; }
1538 |
1539 | .d-inline {
1540 | display: inline; }
1541 |
1542 | .d-inline-block {
1543 | display: inline-block; }
1544 |
1545 | .d-flex {
1546 | display: flex; }
1547 |
1548 | .d-inline-flex {
1549 | display: inline-flex; }
1550 |
1551 | .d-none,
1552 | .d-hide {
1553 | display: none !important; }
1554 |
1555 | .d-visible {
1556 | visibility: visible; }
1557 |
1558 | .d-invisible {
1559 | visibility: hidden; }
1560 |
1561 | .text-hide {
1562 | background: transparent;
1563 | border: 0;
1564 | color: transparent;
1565 | font-size: 0;
1566 | line-height: 0;
1567 | text-shadow: none; }
1568 |
1569 | .text-assistive {
1570 | border: 0;
1571 | clip: rect(0, 0, 0, 0);
1572 | height: 1px;
1573 | margin: -1px;
1574 | overflow: hidden;
1575 | padding: 0;
1576 | position: absolute;
1577 | width: 1px; }
1578 |
1579 | .divider,
1580 | .divider-vert {
1581 | display: block;
1582 | position: relative; }
1583 | .divider[data-content]::after,
1584 | .divider-vert[data-content]::after {
1585 | background: #fff;
1586 | color: #777777;
1587 | content: attr(data-content);
1588 | display: inline-block;
1589 | font-size: 0.7rem;
1590 | padding: 0 0.4rem;
1591 | transform: translateY(-0.65rem); }
1592 |
1593 | .divider {
1594 | border-top: 0.05rem solid #aaaaaa;
1595 | height: 0.05rem;
1596 | margin: 0.4rem 0; }
1597 | .divider[data-content] {
1598 | margin: 0.8rem 0; }
1599 |
1600 | .divider-vert {
1601 | display: block;
1602 | padding: 0.8rem; }
1603 | .divider-vert::before {
1604 | border-left: 0.05rem solid #aaaaaa;
1605 | bottom: 0.4rem;
1606 | content: "";
1607 | display: block;
1608 | left: 50%;
1609 | position: absolute;
1610 | top: 0.4rem;
1611 | transform: translateX(-50%); }
1612 | .divider-vert[data-content]::after {
1613 | left: 50%;
1614 | padding: 0.2rem 0;
1615 | position: absolute;
1616 | top: 50%;
1617 | transform: translate(-50%, -50%); }
1618 |
1619 | .loading {
1620 | color: transparent !important;
1621 | min-height: 0.8rem;
1622 | pointer-events: none;
1623 | position: relative; }
1624 | .loading::after {
1625 | animation: loading 500ms infinite linear;
1626 | border: 0.1rem solid #46c2fe;
1627 | border-radius: 50%;
1628 | border-right-color: transparent;
1629 | border-top-color: transparent;
1630 | content: "";
1631 | display: block;
1632 | height: 0.8rem;
1633 | left: 50%;
1634 | margin-left: -0.4rem;
1635 | margin-top: -0.4rem;
1636 | position: absolute;
1637 | top: 50%;
1638 | width: 0.8rem;
1639 | z-index: 1; }
1640 | .loading.loading-lg {
1641 | min-height: 2rem; }
1642 | .loading.loading-lg::after {
1643 | height: 1.6rem;
1644 | margin-left: -0.8rem;
1645 | margin-top: -0.8rem;
1646 | width: 1.6rem; }
1647 |
1648 | .clearfix::after, .container::after {
1649 | clear: both;
1650 | content: "";
1651 | display: table; }
1652 |
1653 | .float-left {
1654 | float: left !important; }
1655 |
1656 | .float-right {
1657 | float: right !important; }
1658 |
1659 | .relative {
1660 | position: relative; }
1661 |
1662 | .absolute {
1663 | position: absolute; }
1664 |
1665 | .fixed {
1666 | position: fixed; }
1667 |
1668 | .centered {
1669 | display: block;
1670 | float: none;
1671 | margin-left: auto;
1672 | margin-right: auto; }
1673 |
1674 | .flex-centered {
1675 | align-items: center;
1676 | display: flex;
1677 | justify-content: center; }
1678 |
1679 | .m-0 {
1680 | margin: 0; }
1681 |
1682 | .mb-0 {
1683 | margin-bottom: 0; }
1684 |
1685 | .ml-0 {
1686 | margin-left: 0; }
1687 |
1688 | .mr-0 {
1689 | margin-right: 0; }
1690 |
1691 | .mt-0 {
1692 | margin-top: 0; }
1693 |
1694 | .mx-0 {
1695 | margin-left: 0;
1696 | margin-right: 0; }
1697 |
1698 | .my-0 {
1699 | margin-bottom: 0;
1700 | margin-top: 0; }
1701 |
1702 | .m-1 {
1703 | margin: 0.2rem; }
1704 |
1705 | .mb-1 {
1706 | margin-bottom: 0.2rem; }
1707 |
1708 | .ml-1 {
1709 | margin-left: 0.2rem; }
1710 |
1711 | .mr-1 {
1712 | margin-right: 0.2rem; }
1713 |
1714 | .mt-1 {
1715 | margin-top: 0.2rem; }
1716 |
1717 | .mx-1 {
1718 | margin-left: 0.2rem;
1719 | margin-right: 0.2rem; }
1720 |
1721 | .my-1 {
1722 | margin-bottom: 0.2rem;
1723 | margin-top: 0.2rem; }
1724 |
1725 | .m-2 {
1726 | margin: 0.4rem; }
1727 |
1728 | .mb-2 {
1729 | margin-bottom: 0.4rem; }
1730 |
1731 | .ml-2 {
1732 | margin-left: 0.4rem; }
1733 |
1734 | .mr-2 {
1735 | margin-right: 0.4rem; }
1736 |
1737 | .mt-2 {
1738 | margin-top: 0.4rem; }
1739 |
1740 | .mx-2 {
1741 | margin-left: 0.4rem;
1742 | margin-right: 0.4rem; }
1743 |
1744 | .my-2 {
1745 | margin-bottom: 0.4rem;
1746 | margin-top: 0.4rem; }
1747 |
1748 | .p-0 {
1749 | padding: 0; }
1750 |
1751 | .pb-0 {
1752 | padding-bottom: 0; }
1753 |
1754 | .pl-0 {
1755 | padding-left: 0; }
1756 |
1757 | .pr-0 {
1758 | padding-right: 0; }
1759 |
1760 | .pt-0 {
1761 | padding-top: 0; }
1762 |
1763 | .px-0 {
1764 | padding-left: 0;
1765 | padding-right: 0; }
1766 |
1767 | .py-0 {
1768 | padding-bottom: 0;
1769 | padding-top: 0; }
1770 |
1771 | .p-1 {
1772 | padding: 0.2rem; }
1773 |
1774 | .pb-1 {
1775 | padding-bottom: 0.2rem; }
1776 |
1777 | .pl-1 {
1778 | padding-left: 0.2rem; }
1779 |
1780 | .pr-1 {
1781 | padding-right: 0.2rem; }
1782 |
1783 | .pt-1 {
1784 | padding-top: 0.2rem; }
1785 |
1786 | .px-1 {
1787 | padding-left: 0.2rem;
1788 | padding-right: 0.2rem; }
1789 |
1790 | .py-1 {
1791 | padding-bottom: 0.2rem;
1792 | padding-top: 0.2rem; }
1793 |
1794 | .p-2 {
1795 | padding: 0.4rem; }
1796 |
1797 | .pb-2 {
1798 | padding-bottom: 0.4rem; }
1799 |
1800 | .pl-2 {
1801 | padding-left: 0.4rem; }
1802 |
1803 | .pr-2 {
1804 | padding-right: 0.4rem; }
1805 |
1806 | .pt-2 {
1807 | padding-top: 0.4rem; }
1808 |
1809 | .px-2 {
1810 | padding-left: 0.4rem;
1811 | padding-right: 0.4rem; }
1812 |
1813 | .py-2 {
1814 | padding-bottom: 0.4rem;
1815 | padding-top: 0.4rem; }
1816 |
1817 | .rounded {
1818 | border-radius: 0.1rem; }
1819 |
1820 | .circle {
1821 | border-radius: 50%; }
1822 |
1823 | .text-left {
1824 | text-align: left; }
1825 |
1826 | .text-right {
1827 | text-align: right; }
1828 |
1829 | .text-center {
1830 | text-align: center; }
1831 |
1832 | .text-justify {
1833 | text-align: justify; }
1834 |
1835 | .text-lowercase {
1836 | text-transform: lowercase; }
1837 |
1838 | .text-uppercase {
1839 | text-transform: uppercase; }
1840 |
1841 | .text-capitalize {
1842 | text-transform: capitalize; }
1843 |
1844 | .text-normal {
1845 | font-weight: normal; }
1846 |
1847 | .text-bold {
1848 | font-weight: bold; }
1849 |
1850 | .text-italic {
1851 | font-style: italic; }
1852 |
1853 | .text-large {
1854 | font-size: 1.2em; }
1855 |
1856 | .text-ellipsis {
1857 | overflow: hidden;
1858 | text-overflow: ellipsis;
1859 | white-space: nowrap; }
1860 |
1861 | .text-clip {
1862 | overflow: hidden;
1863 | text-overflow: clip;
1864 | white-space: nowrap; }
1865 |
1866 | .text-break {
1867 | hyphens: auto;
1868 | word-break: break-word;
1869 | word-wrap: break-word; }
1870 |
1871 | /*# sourceMappingURL=spectre.css.map */
1872 |
--------------------------------------------------------------------------------