Merge branch 'master' into windows

Conflicts:
	src/components/ContainerSettingsPorts.react.js
	src/components/ContainerSettingsVolumes.react.js
This commit is contained in:
Jeffrey Morgan 2015-06-12 15:48:29 -07:00
commit 5628e91d7c
46 changed files with 1165 additions and 7422 deletions

View File

@ -17,8 +17,7 @@ Before you fil an issue or a pull request, quickly read of the following tips on
### Prerequisites ### Prerequisites
Most of the time, you'll have installed Kitematic before contibuting, but for the - [Node.js 0.10.38](https://nodejs.org/dist/v0.10.38/)
sake of completeness, you can also install [Node.js 0.10.38](https://nodejs.org/dist/v0.10.38/).
### Other Prerequisites (Mac) ### Other Prerequisites (Mac)
- The latest Xcode from the Apple App Store. - The latest Xcode from the Apple App Store.

View File

@ -1,5 +1,4 @@
[![Build Status](https://travis-ci.org/kitematic/kitematic.svg?branch=master)](https://travis-ci.org/kitematic/kitematic) [![Build Status](https://travis-ci.org/kitematic/kitematic.svg?branch=master)](https://travis-ci.org/kitematic/kitematic)
[![Coverage Status](https://coveralls.io/repos/kitematic/kitematic/badge.svg?branch=master)](https://coveralls.io/r/kitematic/kitematic?branch=master)
[![bitHound Score](https://www.bithound.io/github/kitematic/kitematic/badges/score.svg)](https://www.bithound.io/github/kitematic/kitematic) [![bitHound Score](https://www.bithound.io/github/kitematic/kitematic/badges/score.svg)](https://www.bithound.io/github/kitematic/kitematic)
@ -7,6 +6,8 @@
Kitematic is a simple application for managing Docker containers on Mac OS X and Windows (coming soon). Kitematic is a simple application for managing Docker containers on Mac OS X and Windows (coming soon).
![Kitematic Screenshot](https://cloud.githubusercontent.com/assets/3325447/8119979/152f1010-104f-11e5-9298-cd3e92b61ec6.png)
## Installing Kitematic ## Installing Kitematic
[Download the latest version](https://kitematic.com/download) of Kitematic. [Download the latest version](https://kitematic.com/download) of Kitematic.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Binary file not shown.

BIN
fonts/kitematic.eot Normal file

Binary file not shown.

31
fonts/kitematic.svg Normal file
View File

@ -0,0 +1,31 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by Fontastic.me</metadata>
<defs>
<font id="kitematic" horiz-adv-x="512">
<font-face font-family="kitematic" units-per-em="512" ascent="480" descent="-32"/>
<missing-glyph horiz-adv-x="512" />
<glyph unicode="&#97;" d="M256 0c-7 0-12 5-12 12l0 488c0 7 5 12 12 12 7 0 12-5 12-12l0-488c0-7-5-12-12-12z m244 256l-488 0c-7 0-12 5-12 12 0 8 5 12 12 12l488 0c7 0 12-4 12-12 0-7-5-12-12-12z"/>
<glyph unicode="&#101;" d="M411 0l-310 0c-19 0-36 15-36 36l0 238c0 20 16 36 36 36l310 0c19 0 36-16 36-36l0-238c0-21-16-36-36-36z m-310 286c-6 0-12-6-12-12l0-238c0-7 5-12 12-12l310 0c6 0 12 6 12 12l0 238c0 7-5 12-12 12z m286 12l-24 0 0 76c0 55-43 114-107 114-64 0-107-59-107-114l0-76-24 0 0 76c0 75 60 138 131 138 71 0 131-63 131-138z m-131-227c-20 0-36 16-36 36l0 36c-14 11-24 28-24 48 0 33 27 59 60 59 33 0 60-26 60-59 0-20-10-37-24-48l0-36c0-20-16-36-36-36z m0 155c-20 0-36-15-36-35 0-14 7-25 18-31l6-4 0-49c0-7 5-12 12-12 7 0 12 5 12 12l0 49 6 4c11 6 18 17 18 31 0 20-16 35-36 35z"/>
<glyph unicode="&#102;" d="M156 226c-1 0-1 0 0 0-5 0-7 3-10 5l-36 48c-4 4-4 13 2 16 5 4 13 4 17-2l28-37 37 38c5 5 12 5 17 0 5-5 5-12 0-17l-47-47c-2-3-6-4-8-4z m104-71c-6 0-12 5-12 12 0 7 4 12 12 12 25 1 47 10 64 28 14 16 22 37 21 59-2 46-43 82-91 79-25-1-49-12-65-29-16-18-23-43-21-70 0-7-5-11-11-13-7 0-12 5-13 11-2 33 7 63 27 87 21 23 50 37 81 38 62 2 115-44 117-103 1-28-8-55-28-75-21-22-49-35-81-36 1 0 1 0 0 0z m-4-155c-142 0-256 114-256 256 0 142 114 256 256 256 142 0 256-114 256-256 0-142-114-256-256-256z m0 488c-129 0-232-103-232-232 0-129 103-232 232-232 129 0 232 103 232 232 0 129-103 232-232 232z"/>
<glyph unicode="&#103;" d="M256 0c-142 0-256 114-256 256 0 142 114 256 256 256 142 0 256-114 256-256 0-142-114-256-256-256z m0 488c-129 0-232-103-232-232 0-129 103-232 232-232 129 0 232 103 232 232 0 129-103 232-232 232z m137-292l-19 0-14 48-14-48-19 0-20 65-20-65-19 0-14 48-13-48-20 0-20 65-20-65-19 0-14 48-13-48-20 0-27 86 23 0 14-49 13 49 19 0 14-49 15 49 31 0 14-49 13 49 19 0 14-49 15 49 31 0 14-49 13 49 19 0 14-49 15 49 22 0z"/>
<glyph unicode="&#104;" d="M500 0c-3 0-5 1-8 3l-198 198c-4 5-4 12 0 17 5 4 12 4 17 0l198-198c4-5 4-12 0-17-3-2-5-3-9-3z m-320 151c-99 0-180 82-180 181 0 99 81 180 180 180 99 0 181-81 181-180 0-99-82-181-181-181z m0 338c-86 0-157-71-157-157 0-86 71-157 157-157 86 0 157 71 157 157 0 86-71 157-157 157z"/>
<glyph unicode="&#105;" d="M256 0c-142 0-256 114-256 256 0 142 114 256 256 256 142 0 256-114 256-256 0-142-114-256-256-256z m0 488c-129 0-232-103-232-232 0-129 103-232 232-232 129 0 232 103 232 232 0 129-103 232-232 232z m-52-337c-15 0-25 11-25 28l0 142c0 16 9 28 25 28 4 0 9-1 14-4l148-74c9-4 15-13 15-21 0-8-6-17-15-21l-148-74c-6-3-11-4-14-4z m0 174c0 0-2-1-2-4l0-142c0-3 0-4 2-4 0 0 1 0 3 1l148 74-148 74c-2 1-3 1-3 1z"/>
<glyph unicode="&#106;" d="M27 18c-4 0-8 1-11 4-6 6-6 14 0 21l445 444c6 7 15 7 21 0 6-6 6-14 0-20l-445-445c-2-2-6-4-10-4z m457-2c-4 0-7 1-10 4l-445 445c-6 6-6 14 0 20 6 7 14 7 20 0l445-444c6-7 6-15 0-21-2-2-6-4-10-4z"/>
<glyph unicode="&#108;" d="M321 155l-130 0c-21 0-36 15-36 36l0 130c0 21 15 36 36 36l130 0c21 0 36-15 36-36l0-130c0-21-15-36-36-36z m-130 178c-8 0-12-4-12-12l0-130c0-8 4-12 12-12l130 0c8 0 12 4 12 12l0 130c0 8-4 12-12 12z m65-333c-142 0-256 114-256 256 0 142 114 256 256 256 142 0 256-114 256-256 0-142-114-256-256-256z m0 488c-129 0-232-103-232-232 0-129 103-232 232-232 129 0 232 103 232 232 0 129-103 232-232 232z"/>
<glyph unicode="&#109;" d="M315 9c-4 0-7 1-9 3l-302 306c-3 3-4 5-4 9l0 173c0 7 5 12 12 12l173 0c4 0 6-1 9-4l306-302c2-2 5-7 3-12-1-4-3-7-8-9l-129-39-39-129c-2-4-5-7-9-8-1 0-2 0-3 0z m-291 323l284-288 36 116c1 3 3 7 8 8l116 36-288 284-156 0z m95 7c-29 0-53 24-53 54 0 29 24 53 53 53 30 0 54-24 54-53 0-30-24-54-54-54z m0 83c-15 0-29-14-29-29 0-16 14-30 29-30 16 0 30 14 30 30 0 15-14 29-30 29z"/>
<glyph unicode="&#113;" d="M300 287c-4 0-7 1-9 4-5 5-5 12 0 17l200 200c5 5 12 5 17 0 5-5 5-12 0-17l-200-200c-2-3-5-4-8-4z m-275-275c-4 0-6 2-9 4-5 5-5 13 0 18l200 200c5 5 13 5 18 0 5-5 5-13 0-18l-200-200c-3-2-5-4-9-4z m475 338c-8 0-13 5-13 12l0 125-125 0c-7 0-12 5-12 13 0 7 5 12 12 12l138 0c7 0 12-5 12-12l0-138c0-7-5-12-12-12z m-350-350l-138 0c-7 0-12 5-12 12l0 138c0 7 5 12 12 12 8 0 13-5 13-12l0-125 125 0c7 0 12-5 12-13 0-7-5-12-12-12z"/>
<glyph unicode="&#114;" d="M256 23c-2 0-5 1-7 3-10 7-249 181-249 317 0 91 67 146 133 146 43 0 95-26 123-93 28 67 80 93 123 93 66 0 133-55 133-146 0-135-239-309-249-317-2-2-5-3-7-3z m-123 442c-54 0-110-45-110-122 0-111 193-264 233-293 40 29 233 182 233 293 0 77-56 122-110 122-53 0-95-40-111-110-2-5-6-9-12-9-6 0-10 3-12 9-16 70-58 110-111 110z"/>
<glyph unicode="&#115;" d="M29 6c-4 0-6 1-9 4-3 3-5 9-2 14l55 107c-47 42-73 98-73 154 0 122 114 222 256 222 142 0 256-100 256-222 0-123-114-223-256-223-32 0-61 7-83 14l-140-69c-1-1-3-1-4-1z m227 476c-129 0-232-89-232-199 0-51 26-103 71-141 5-4 6-10 3-15l-43-83 111 55c2 1 5 1 9 1 21-7 50-14 81-14 129 0 232 89 232 199 0 108-103 197-232 197z m-115-119l219 0 0-24-219 0z m0-59l219 0 0-24-219 0z m0-60l219 0 0-24-219 0z"/>
<glyph unicode="&#116;" d="M162 214c-13 0-24 5-33 15l-109 108c-13 13-20 30-20 48 0 17 7 34 20 47l61 61c13 13 30 20 48 20 17 0 34-7 47-20l109-108c10-11 15-25 13-41-3-14-10-28-23-40-5-5-12-5-17 0-4 4-4 12 0 16 10 10 15 19 16 28 1 8-1 14-7 20l-109 108c-16 17-45 17-62 0l-60-60c-9-9-13-19-13-31 0-12 4-23 13-31l108-109c6-6 12-8 20-7 9 1 19 7 28 16 4 4 12 4 16 0 5-5 5-12 0-17-13-13-27-20-41-23 0 0-3 0-5 0z m221-214c-17 0-34 7-47 20l-107 109c-22 21-18 54 9 81 5 4 12 4 17 0 5-5 5-12 0-17-14-14-24-33-10-48l107-108c9-8 19-13 31-13 12 0 23 5 31 13l62 62c9 8 13 19 13 31 0 12-4 22-13 31l-108 107c-14 14-35 5-48-10-4-4-12-4-16 0-5 5-5 12 0 17 27 27 59 31 81 10l108-108c13-13 20-29 20-47 0-18-7-35-19-48l-62-62c-14-13-31-20-49-20z m-26 143c-3 0-6 1-8 3l-191 191c-4 5-4 12 0 17 5 4 12 4 17 0l191-191c4-5 4-12 0-17-3-2-5-3-9-3z"/>
<glyph unicode="&#117;" d="M70 186c-39 0-70 32-70 70 0 38 31 70 70 70 38 0 70-32 70-70 0-38-32-70-70-70z m0 117c-26 0-47-21-47-47 0-26 21-47 47-47 25 0 46 21 46 47 0 26-21 47-46 47z m186-117c-38 0-70 32-70 70 0 38 32 70 70 70 38 0 70-32 70-70 0-38-32-70-70-70z m0 117c-26 0-47-21-47-47 0-26 21-47 47-47 26 0 47 21 47 47 0 26-21 47-47 47z m186-117c-38 0-70 32-70 70 0 38 32 70 70 70 39 0 70-32 70-70 0-38-31-70-70-70z m0 117c-25 0-46-21-46-47 0-26 21-47 46-47 26 0 47 21 47 47 0 26-21 47-47 47z"/>
<glyph unicode="&#118;" d="M297 0l-84 0c-7 0-12 5-12 12l0 61c-2 1-5 3-8 3-9 3-20 7-30 11l-42-43c-5-5-13-5-17 0l-60 60c-3 2-4 4-4 8 0 4 1 6 4 9l43 42c-8 15-14 34-18 54l-58 0c-7 0-12 5-12 12l0 67c0 8 5 12 12 12l60 0c3 11 9 26 16 38l-43 43c-5 5-5 12 0 17l60 60c4 5 12 5 17 0l42-43c13 6 27 12 38 16l0 61c0 7 5 12 12 12l84 0c8 0 13-5 13-12l0-60c11-4 25-8 37-16l43 43c5 5 12 5 17 0l60-60c2-2 4-5 4-8 0-4-2-6-4-9l-43-43c6-12 11-24 16-37l60 0c7 0 12-5 12-13l0-67c0-7-5-12-12-12l-59 0c-3-29-9-42-17-55l42-41c5-5 5-12 0-17l-60-60c-5-5-12-5-17 0l-43 43c-10-5-19-9-29-11-2-1-6-3-9-3l0-61c2-8-3-13-11-13z m-70 24l59 0 0 56c0 5 3 9 7 11 3 3 9 5 18 8 11 3 24 8 33 13 5 3 11 3 14-1l41-40 42 42-40 41c-4 3-5 9-1 14l1 1c9 15 15 25 18 63 2 6 6 11 13 11l56 0 0 42-56 0c-7 0-11 4-13 10-3 17-11 34-19 49-3 5-3 11 1 14l40 41-42 42-41-40c-3-4-9-5-14-1-15 8-40 18-48 19-6 2-10 6-10 13l0 56-59 0 0-56c0-7-4-11-10-13-10-2-34-11-48-19-4-3-11-3-14 1l-40 40-43-42 40-41c4-3 5-9 1-14-8-15-17-39-19-49-1-5-6-10-12-10l-56 0 0-42 56 0c6 0 11-5 12-11 4-25 11-50 19-64 3-5 3-11-1-14l-40-41 43-42 40 40c3 4 10 5 14 1 9-5 22-10 33-13 8-3 15-5 19-8 3-2 6-6 6-11z m29 122c-61 0-110 49-110 110 0 61 49 110 110 110 61 0 110-49 110-110 0-61-49-110-110-110z m0 195c-48 0-85-37-85-85 0-48 37-85 85-85 48 0 85 37 85 85 0 48-37 85-85 85z"/>
<glyph unicode="&#99;" d="M252 512c8 0 14 0 21 0 0 0 1 0 1 0 9 0 19-2 28-4 33-7 61-22 85-47 25-28 38-60 37-97-1-19-6-38-16-54-15-30-39-52-69-66-4-2-10-5-15-7 33-76 68-152 101-229-1 0-1 0-2 2-55 30-110 63-165 94-1 1-2 1-3 0-55-31-111-64-166-94 0 0-1-2-1-2 0 0 0 0 0 2 0 1 1 1 1 2 34 74 67 149 99 223 1 2 3 4 4 8-1 0-1 0-1 1-24 11-43 25-60 45-23 29-32 62-27 98 2 24 11 45 26 63 30 38 69 57 116 62 2-1 4 0 6 0z m16-24c-24 0-42-2-58-9-28-11-52-28-67-53-17-26-22-53-14-83 6-25 20-45 39-61 24-19 51-30 82-32 23-2 44 1 64 8 28 10 52 27 67 52 18 27 23 58 13 90-8 25-24 45-45 61-25 18-55 26-81 27z m105-424c-24 55-49 111-73 166-29-6-57-5-86 2-25-56-50-112-75-168 2 0 2 0 2 0 38 22 75 43 113 65 1 1 2 1 3 0 26-16 53-30 79-46 13-4 25-12 37-19z m-113 222c-29 0-52 8-72 25-14 13-24 28-26 47-2 19 1 37 12 53 13 20 31 32 53 38 30 8 59 5 86-12 18-11 30-26 36-46 7-27 2-49-14-70-15-17-35-28-58-33-7-2-14-2-17-2z m-4 24c2 0 4 0 6 0 1 0 2 0 4 0 17 2 34 8 46 21 14 14 19 32 13 51-5 16-14 26-27 34-19 11-41 14-63 9-16-3-30-12-40-25-9-12-13-26-10-40 2-15 9-25 20-34 15-12 33-16 51-16z"/>
<glyph unicode="&#100;" d="M0 274c0 5 0 11 0 16 0 0 0 0 0 2 0 4 1 8 1 13 0 3 1 7 1 12 17 0 34 0 50 0 0 28 0 55 0 83 28 0 56 0 84 0 0 1 0 1 0 2 0 26 0 54 0 80 0 1 0 1 0 2 34 0 70 0 104 0 0 0 0 0 0-1 0-27 0-55 0-82 0 0 0-1 0-1 29 0 56 0 84 0 0-28 0-55 0-83 16-1 31 1 46 7-1 3-2 6-3 9-4 13-5 25-3 39 1 9 3 18 7 26 3 6 7 12 11 17 3 3 5 5 8 8 5-4 9-8 14-12 12-10 21-21 28-35 2-4 3-7 4-11 0 0 0 0 1 0 5 0 11 1 16 1 11 0 21-2 31-8 7-3 13-8 20-11-2-5-6-11-8-16-6-11-13-21-25-28-14-9-30-13-47-14-1 0-2 0-2-1-5-11-10-23-17-33 0 0 0-1-1-2 1 0 1 0 2 0 21 0 43 0 64 0 17 0 32-14 32-31 0-60 0-120 0-179 0-14-10-26-23-30-3-2-5-2-7-2-81 0-164 0-245 0 0 0 0 0-1 0-17 3-29 15-29 32 0 27 0 54 0 82 0 1 0 1 0 2-8 0-15-1-23-1-18 0-38 1-57 7-31 7-57 23-79 46-10 9-17 21-23 32-10 19-15 38-16 59 2 0 1 2 1 4z m350-44c-40 0-79 0-119 0-6 0-12-4-12-12 0-58 0-117 0-175 0-7 5-12 12-12 79 0 159 0 238 0 7 0 12 6 12 12 0 58 0 117 0 175 0 8-5 12-12 12-40 0-79 0-119 0z m50 86c-3-3-6-5-8-6-16-9-33-14-51-15-10-1-19 0-29 0-96 0-192 0-288 0-1 0-1 0-2 0 0-1 0-1 0-1-1-12 0-23 1-34 2-14 6-29 14-42 9-16 22-31 37-42 24-18 52-27 82-28 10-1 21 0 30 0 4 0 8 0 11 1 0 1 0 1 0 3 0 22 0 45 0 70 0 17 14 31 32 31 49 0 99 0 148 0 1 0 2 0 3 1 11 17 19 33 26 52 2 1 2 4 3 4 1 1 3 0 5 0 8-1 16 0 25 1 12 2 23 8 31 17 2 3 5 6 6 10 0 0-1 0-1 1-7 4-14 5-23 6-9 0-17-1-27-4-2-1-5-2-8-2-1 10-2 20-7 31-6 9-12 18-20 25-1-3-3-5-4-7-4-7-5-14-5-21 0-8 0-14 2-20 5-15 11-23 17-31z m-264 0c0 20 0 41 0 62-21 0-42 0-63 0 0-21 0-42 0-62 21 0 42 0 63 0z m21 0c21 0 42 0 62 0 0 20 0 41 0 62-20 0-41 0-62 0 0-21 0-42 0-62z m62 146c-20 0-41 0-62 0 0-21 0-42 0-63 21 0 42 0 62 0 0 21 0 42 0 63z m84-84c-21 0-42 0-63 0 0-21 0-42 0-62 21 0 42 0 63 0 0 20 0 41 0 62z m7-247c-5 6-10 12-15 18-3 5-7 8-10 12-4 4-3 11 1 15 4 4 12 3 15-2 10-12 21-25 31-36 2-2 3-4 6-6-4-5-7-8-10-12-9-10-18-21-27-31-4-5-12-5-16 0-3 4-4 9 0 13 8 9 15 17 22 27 1-2 2 0 3 2z m71-48c-8 0-16 0-26 0-4 0-7 2-9 6-2 4-1 7 1 10 2 3 5 4 8 4 18 0 35 0 53 0 6 0 10-4 10-10 0-6-4-10-10-10-9 0-17 0-27 0z m-235 142c0-20-15-37-34-38-21-1-38 15-39 36-1 20 16 36 35 37 21 2 37-13 38-35z m-36-16c8 0 15 7 15 16 0 8-7 15-15 15-9 0-16-7-16-15 0-10 7-16 16-16z"/>
<glyph unicode="&#107;" d="M256 0c-141 0-256 115-256 256 0 141 115 256 256 256 141 0 256-115 256-256 0-141-115-256-256-256z m0 488c-128 0-232-104-232-232 0-128 104-232 232-232 128 0 232 104 232 232 0 128-104 232-232 232z m98-306l-110 0c-8 0-12 5-12 12 0 7 4 12 12 12l110 0c7 0 12-5 12-12 0-7-5-12-12-12z m-220 1c-2 0-6 1-8 4-5 4-5 12-2 17l54 58-54 59c-5 4-3 12 2 17 4 5 12 3 17-2l68-75-68-76c-3-1-5-2-9-2z"/>
<glyph unicode="&#110;" d="M245 512c7 0 15 0 23 0 0 0 1 0 1 0 11-1 20-1 31-4 36-6 69-19 100-40 37-25 66-57 85-97 15-32 25-65 27-101 1-22 0-45-5-68-7-32-20-63-38-90-25-36-57-64-96-85-32-16-66-25-102-27-23-1-47 0-71 6-51 12-95 37-131 76-25 28-43 57-55 93-7 20-10 42-12 63 0 3 0 5-1 7 0 7 0 15 0 23 0 0 0 1 0 1 1 13 3 26 5 39 11 49 33 92 69 128 21 21 45 38 71 51 31 14 64 23 98 25 0-1 1 0 1 0z m-62-319c-19 19-28 42-28 69 0 27 11 50 30 68 34 33 88 34 126 5 19-16 30-36 33-60 5-30-3-56-23-79 49-29 73-72 72-129 67 46 113 140 88 243-25 103-121 181-235 176-108-5-198-84-219-191-9-52-2-103 24-151 17-31 39-57 68-79-1 56 20 98 64 128z m48-4c-6-2-12-6-18-8-18-7-32-18-44-33-21-27-28-58-24-91 1-6 1-6 7-9 43-22 90-29 137-23 27 4 53 12 77 25 1 0 1 1 2 2 5 22 3 42-4 62-10 29-29 50-57 65-8 3-16 6-24 9-2 1-4 1-7 3 3 1 4 2 6 3 7 6 16 11 22 17 15 16 21 35 19 57-3 17-10 32-22 44-16 15-35 21-57 19-17-2-32-10-44-21-15-17-23-37-19-61 4-22 14-38 33-50 5-1 11-6 17-10z"/>
<glyph unicode="&#98;" d="M4 436c1 0 1 0 3 0 78 0 157 0 236 0 1 0 1 0 2 0 0-9 0-17 0-26-72 0-143 0-215 0 0-126 0-254 0-380 126 0 254 0 380 0 0 67 0 135 0 202 9 0 17 0 26 0 0-1 0-1 0-2 0-75 0-150 0-225 0-1 0-1 0-2-145 0-289 0-432 0 0 144 0 288 0 433z m508-116c0-1 0-2-1-4-3-5-8-9-15-7-6 1-10 6-10 13 0 47 0 95 0 143 0 1 0 1 0 2-2-1-2-1-3-1-80-80-161-161-241-242-7-6-18-5-21 3-3 5-2 11 2 15 0 0 1 1 1 1 81 80 161 160 241 240 0 0 1 1 1 1-1 0-1 0-3 0-47 0-94 0-141 0-7 0-12 3-13 10-3 6 1 14 7 17 0 0 2 0 2 1 60 0 122 0 182 0 4-1 7-3 9-7 2-1 2-4 2-5 1-60 1-121 1-180z m0 179c0 1-1 4-1 5-2 4-6 5-10 7 4 0 8 0 11 0 0-3 0-7 0-12z"/>
<glyph unicode="&#111;" d="M11 436c1 0 1 0 2 0 78 0 155 0 232 0 2 0 2 0 3 0 0-9 0-17 0-26-71 0-142 0-211 0 0-125 0-250 0-375 124 0 249 0 374 0 0 71 0 142 0 212 9 0 17 0 26 0 0-2 0-2 0-3 0-79 0-156 0-235 0 0 0-1 0-1-142 0-285 0-426 0 0 144 0 286 0 428z m501 0c-1-4-4-7-7-9-53-54-107-108-162-162-2-1-4-4-7-4-31-12-64-25-96-37-5-2-10-1-14 3-4 4-5 9-3 15 12 31 25 64 37 95 1 3 3 6 4 7 54 54 108 108 163 163 2 2 5 5 9 6 1 0 2 0 4 0 4-1 6-4 9-6 18-19 37-37 55-55 3-3 5-6 7-10 1-4 1-5 1-6z m-177-142c35 36 71 71 106 106-13 14-27 28-41 41-35-35-70-71-106-106 13-15 28-28 41-41z m126 125c6 6 13 13 19 19-13 14-27 28-40 41-7-6-13-13-20-20 13-13 26-27 41-40z m-150-141c-10 11-21 21-33 33-6-17-13-34-19-52 18 6 35 13 52 19z"/>
</font></defs></svg>

After

Width:  |  Height:  |  Size: 13 KiB

BIN
fonts/kitematic.ttf Normal file

Binary file not shown.

BIN
fonts/kitematic.woff Normal file

Binary file not shown.

View File

@ -1,6 +1,6 @@
{ {
"name": "Kitematic", "name": "Kitematic",
"version": "0.6.3", "version": "0.6.6",
"author": "Kitematic", "author": "Kitematic",
"description": "Simple Docker Container management for Mac OS X.", "description": "Simple Docker Container management for Mac OS X.",
"homepage": "https://kitematic.com/", "homepage": "https://kitematic.com/",

View File

@ -65,10 +65,10 @@ app.on('open-url', function (event, url) {
app.on('ready', function () { app.on('ready', function () {
var mainWindow = new BrowserWindow({ var mainWindow = new BrowserWindow({
width: size.width || 1000, width: size.width || 800,
height: size.height || 780, height: size.height || 600,
'min-width': 1000, 'min-width': 700,
'min-height': 600, 'min-height': 500,
'standard-window': false, 'standard-window': false,
resizable: true, resizable: true,
frame: false, frame: false,

View File

@ -23,8 +23,10 @@ var ContainerDetailsHeader = React.createClass({
state = <span className="status stopped">STOPPED</span>; state = <span className="status stopped">STOPPED</span>;
} }
return ( return (
<div className="details-header"> <div className="header-section">
<h1>{this.props.container.Name}</h1>{state} <div className="text">
{this.props.container.Name}{state}
</div>
</div> </div>
); );
} }

View File

@ -1,4 +1,3 @@
var $ = require('jquery');
var _ = require('underscore'); var _ = require('underscore');
var React = require('react'); var React = require('react');
var exec = require('exec'); var exec = require('exec');
@ -7,10 +6,8 @@ var metrics = require('../utils/MetricsUtil');
var ContainerUtil = require('../utils/ContainerUtil'); var ContainerUtil = require('../utils/ContainerUtil');
var util = require('../utils/Util'); var util = require('../utils/Util');
var machine = require('../utils/DockerMachineUtil'); var machine = require('../utils/DockerMachineUtil');
var RetinaImage = require('react-retina-image');
var classNames = require('classnames'); var classNames = require('classnames');
var resources = require('../utils/ResourcesUtil'); var resources = require('../utils/ResourcesUtil');
var dockerUtil = require('../utils/DockerUtil');
var containerActions = require('../actions/ContainerActions'); var containerActions = require('../actions/ContainerActions');
var dockerMachineUtil = require('../utils/DockerMachineUtil'); var dockerMachineUtil = require('../utils/DockerMachineUtil');
@ -62,12 +59,6 @@ var ContainerDetailsSubheader = React.createClass({
this.context.router.transitionTo('containerHome', {name: this.context.router.getCurrentParams().name}); this.context.router.transitionTo('containerHome', {name: this.context.router.getCurrentParams().name});
} }
}, },
showLogs: function () {
if (!this.disableTab()) {
metrics.track('Viewed Logs');
this.context.router.transitionTo('containerLogs', {name: this.context.router.getCurrentParams().name});
}
},
showSettings: function () { showSettings: function () {
if (!this.disableTab()) { if (!this.disableTab()) {
metrics.track('Viewed Settings'); metrics.track('Viewed Settings');
@ -115,51 +106,7 @@ var ContainerDetailsSubheader = React.createClass({
dockerMachineUtil.dockerTerminal(`docker exec -it ${this.props.container.Name} ${shell}`); dockerMachineUtil.dockerTerminal(`docker exec -it ${this.props.container.Name} ${shell}`);
} }
}, },
handleItemMouseEnterView: function () {
var $action = $(this.getDOMNode()).find('.action .view');
$action.css("visibility", "visible");
},
handleItemMouseLeaveView: function () {
var $action = $(this.getDOMNode()).find('.action .view');
$action.css("visibility", "hidden");
},
handleItemMouseEnterRestart: function () {
var $action = $(this.getDOMNode()).find('.action .restart');
$action.css("visibility", "visible");
},
handleItemMouseLeaveRestart: function () {
var $action = $(this.getDOMNode()).find('.action .restart');
$action.css("visibility", "hidden");
},
handleItemMouseEnterStop: function () {
var $action = $(this.getDOMNode()).find('.action .stop');
$action.css("visibility", "visible");
},
handleItemMouseLeaveStop: function () {
var $action = $(this.getDOMNode()).find('.action .stop');
$action.css("visibility", "hidden");
},
handleItemMouseEnterStart: function () {
var $action = $(this.getDOMNode()).find('.action .start');
$action.css("visibility", "visible");
},
handleItemMouseLeaveStart: function () {
var $action = $(this.getDOMNode()).find('.action .start');
$action.css("visibility", "hidden");
},
handleItemMouseEnterTerminal: function () {
var $action = $(this.getDOMNode()).find('.action .terminal');
$action.css("visibility", "visible");
},
handleItemMouseLeaveTerminal: function () {
var $action = $(this.getDOMNode()).find('.action .terminal');
$action.css("visibility", "hidden");
},
render: function () { render: function () {
var runActionClass = classNames({
action: true,
disabled: this.disableRun()
});
var restartActionClass = classNames({ var restartActionClass = classNames({
action: true, action: true,
disabled: this.disableRestart() disabled: this.disableRestart()
@ -181,33 +128,28 @@ var ContainerDetailsSubheader = React.createClass({
var currentRoute = _.last(currentRoutes); var currentRoute = _.last(currentRoutes);
var tabHomeClasses = classNames({ var tabHomeClasses = classNames({
'tab': true, 'details-tab': true,
'active': currentRoute === 'containerHome', 'active': currentRoute === 'containerHome',
disabled: this.disableTab() disabled: this.disableTab()
}); });
var tabLogsClasses = classNames({
'tab': true,
'active': currentRoute === 'containerLogs',
disabled: this.disableTab()
});
var tabSettingsClasses = classNames({ var tabSettingsClasses = classNames({
'tab': true, 'details-tab': true,
'active': currentRoutes && (currentRoutes.indexOf('containerSettings') >= 0), 'active': currentRoutes && (currentRoutes.indexOf('containerSettings') >= 0),
disabled: this.disableTab() disabled: this.disableTab()
}); });
var startStopToggle; var startStopToggle;
if (this.disableStop()) { if (this.disableStop()) {
startStopToggle = ( startStopToggle = (
<div className={startActionClass} onMouseEnter={this.handleItemMouseEnterStart} onMouseLeave={this.handleItemMouseLeaveStart}> <div className={startActionClass}>
<div className="action-icon" onClick={this.handleStart}><RetinaImage src="button-start.png" /></div> <div className="action-icon start" onClick={this.handleStart}><span className="icon icon-start"></span></div>
<span className="btn-label start">Start</span> <div className="btn-label">START</div>
</div> </div>
); );
} else { } else {
startStopToggle = ( startStopToggle = (
<div className={stopActionClass} onMouseEnter={this.handleItemMouseEnterStop} onMouseLeave={this.handleItemMouseLeaveStop}> <div className={stopActionClass}>
<div className="action-icon" onClick={this.handleStop}><RetinaImage src="button-stop.png" /></div> <div className="action-icon stop" onClick={this.handleStop}><span className="icon icon-stop"></span></div>
<span className="btn-label stop">Stop</span> <div className="btn-label">STOP</div>
</div> </div>
); );
} }
@ -215,22 +157,17 @@ var ContainerDetailsSubheader = React.createClass({
<div className="details-subheader"> <div className="details-subheader">
<div className="details-header-actions"> <div className="details-header-actions">
{startStopToggle} {startStopToggle}
<div className={restartActionClass} onMouseEnter={this.handleItemMouseEnterRestart} onMouseLeave={this.handleItemMouseLeaveRestart}> <div className={restartActionClass}>
<div className="action-icon" onClick={this.handleRestart}><RetinaImage src="button-restart.png"/></div> <div className="action-icon" onClick={this.handleRestart}><span className="icon icon-restart"></span></div>
<span className="btn-label restart">Restart</span> <div className="btn-label">RESTART</div>
</div> </div>
<div className={terminalActionClass} onMouseEnter={this.handleItemMouseEnterTerminal} onMouseLeave={this.handleItemMouseLeaveTerminal}> <div className={terminalActionClass}>
<div className="action-icon" onClick={this.handleTerminal}><RetinaImage src="button-terminal.png"/></div> <div className="action-icon" onClick={this.handleTerminal}><span className="icon icon-docker-exec"></span></div>
<span className="btn-label terminal">Terminal</span> <div className="btn-label">EXEC</div>
</div>
<div className={runActionClass} onMouseEnter={this.handleItemMouseEnterView} onMouseLeave={this.handleItemMouseLeaveView}>
<div className="action-icon" onClick={this.handleRun}><RetinaImage src="button-view.png"/></div>
<span className="btn-label view">View</span>
</div> </div>
</div> </div>
<div className="details-subheader-tabs"> <div className="details-subheader-tabs">
<span className={tabHomeClasses} onClick={this.showHome}>Home</span> <span className={tabHomeClasses} onClick={this.showHome}>Home</span>
<span className={tabLogsClasses} onClick={this.showLogs}>Logs</span>
<span className={tabSettingsClasses} onClick={this.showSettings}>Settings</span> <span className={tabSettingsClasses} onClick={this.showSettings}>Settings</span>
</div> </div>
</div> </div>

View File

@ -27,14 +27,23 @@ var ContainerHome = React.createClass({
}, },
handleResize: function () { handleResize: function () {
$('.left .wrapper').height(window.innerHeight - 240); $('.full .wrapper').height(window.innerHeight - 132);
$('.right .wrapper').height(window.innerHeight / 2 - 100); $('.left .wrapper').height(window.innerHeight - 132);
$('.right .wrapper').height(window.innerHeight / 2 - 55);
}, },
handleErrorClick: function () { handleErrorClick: function () {
shell.openExternal('https://github.com/kitematic/kitematic/issues/new'); shell.openExternal('https://github.com/kitematic/kitematic/issues/new');
}, },
showWeb: function () {
return _.keys(this.props.ports).length > 0;
},
showFolders: function () {
return this.props.container.Volumes && _.keys(this.props.container.Volumes).length > 0 && this.props.container.State.Running;
},
render: function () { render: function () {
if (!this.props.container) { if (!this.props.container) {
return; return;
@ -88,43 +97,42 @@ var ContainerHome = React.createClass({
); );
} }
} else { } else {
if (this.props.defaultPort) { var logWidget = (
<ContainerHomeLogs container={this.props.container}/>
);
var webWidget;
if (this.showWeb()) {
webWidget = (
<ContainerHomePreview ports={this.props.ports} defaultPort={this.props.defaultPort} />
);
}
var folderWidget;
if (this.showFolders()) {
folderWidget = (
<ContainerHomeFolders container={this.props.container} />
);
}
if (logWidget && !webWidget && !folderWidget) {
body = ( body = (
<div className="details-panel home"> <div className="details-panel home">
<div className="content"> <div className="content">
<div className="left"> <div className="full">
<ContainerHomePreview ports={this.props.ports} defaultPort={this.props.defaultPort} /> {logWidget}
</div>
<div className="right">
<ContainerHomeLogs container={this.props.container}/>
<ContainerHomeFolders container={this.props.container} />
</div> </div>
</div> </div>
</div> </div>
); );
} else { } else {
var right;
if (_.keys(this.props.ports) > 0) {
right = (
<div className="right">
<ContainerHomePreview ports={this.props.ports} defaultPort={this.props.defaultPort} />
<ContainerHomeFolders container={this.props.container} />
</div>
);
} else {
right = (
<div className="right">
<ContainerHomeFolders container={this.props.container} />
</div>
);
}
body = ( body = (
<div className="details-panel home"> <div className="details-panel home">
<div className="content"> <div className="content">
<div className="left"> <div className="left">
<ContainerHomeLogs container={this.props.container}/> {logWidget}
</div>
<div className="right">
{webWidget}
{folderWidget}
</div> </div>
{right}
</div> </div>
</div> </div>
); );

View File

@ -58,9 +58,8 @@ var ContainerHomeFolder = React.createClass({
return false; return false;
} }
console.log(this.props.container.Volumes);
var folders = _.map(_.omit(this.props.container.Volumes, (v, k) => k.indexOf('/Users/') !== -1), (val, key) => { var folders = _.map(_.omit(this.props.container.Volumes, (v, k) => k.indexOf('/Users/') !== -1), (val, key) => {
var firstFolder = key.split('/')[1]; var firstFolder = key;
return ( return (
<div key={key} className="folder" onClick={this.handleClickFolder.bind(this, val, key)}> <div key={key} className="folder" onClick={this.handleClickFolder.bind(this, val, key)}>
<RetinaImage src="folder.png" /> <RetinaImage src="folder.png" />
@ -69,19 +68,21 @@ var ContainerHomeFolder = React.createClass({
); );
}); });
if (this.props.container.Volumes && _.keys(this.props.container.Volumes).length > 0 && this.props.container.State.Running) { return (
return ( <div className="folders wrapper">
<div className="folders wrapper"> <div className="widget">
<h4>Edit Files</h4> <div className="top-bar">
<div className="widget"> <div className="text">Volumes</div>
<div className="action" onClick={this.handleClickChangeFolders}>
<span className="icon icon-preferences"></span>
</div>
</div>
<div className="folders-list">
{folders} {folders}
</div> </div>
<div className="subtext" onClick={this.handleClickChangeFolders}>Change Folders</div>
</div> </div>
); </div>
} else { );
return false;
}
} }
}); });

View File

@ -71,12 +71,14 @@ module.exports = React.createClass({
} }
return ( return (
<div className="mini-logs wrapper"> <div className="mini-logs wrapper">
<h4>Logs</h4>
<div className="widget"> <div className="widget">
<div className="top-bar">
<div className="text">Container Logs</div>
</div>
<div className="logs"> <div className="logs">
{logs} {logs}
</div> </div>
<div className="mini-logs-overlay" onClick={this.handleClickLogs}><span className="icon icon-scale-spread-1"></span><div className="text">View Logs</div></div> </div> </div>
</div> </div>
); );
} }

View File

@ -51,38 +51,53 @@ var ContainerHomePreview = React.createClass({
var frame = React.createElement('webview', {className: 'frame', id: 'webview', src: this.props.ports[this.props.defaultPort].url, autosize: 'on'}); var frame = React.createElement('webview', {className: 'frame', id: 'webview', src: this.props.ports[this.props.defaultPort].url, autosize: 'on'});
preview = ( preview = (
<div className="web-preview wrapper"> <div className="web-preview wrapper">
<h4>Web Preview</h4>
<div className="widget"> <div className="widget">
<div className="top-bar">
<div className="text">Web Preview</div>
<div className="action" onClick={this.handleClickPreview}>
<span className="icon icon-open-external"></span>
</div>
<div className="action" onClick={this.handleClickNotShowingCorrectly}>
<span className="icon icon-preferences"></span>
</div>
</div>
{frame} {frame}
<div className="frame-overlay" onClick={this.handleClickPreview}><span className="icon icon-upload-2"></span><div className="text">Open in Browser</div></div>
</div> </div>
<div className="subtext" onClick={this.handleClickNotShowingCorrectly}>Not showing correctly?</div>
</div> </div>
); );
} else { } else {
var ports = _.map(_.pairs(this.props.ports), function (pair) { var ports = _.map(_.pairs(this.props.ports), pair => {
var key = pair[0]; var key = pair[0];
var val = pair[1]; var val = pair[1];
return ( return (
<div key={key} className="table-values"> <tr key={key}>
<span className="value-left">{key}</span><span className="icon icon-arrow-right"></span> <td>{key}</td>
<span className="value-right">{val.display}</span> <td>{val.display}</td>
</div> </tr>
); );
}); });
preview = ( preview = (
<div className="web-preview wrapper"> <div className="web-preview wrapper">
<h4>IP &amp; Ports</h4>
<div className="widget"> <div className="widget">
<p>You can access this container using the following IP address and port:</p> <div className="top-bar">
<div className="table ports"> <div className="text">IP & PORTS</div>
<div className="table-labels"> <div className="action" onClick={this.handleClickNotShowingCorrectly}>
<div className="label-left">DOCKER PORT</div> <span className="icon icon-preferences"></span>
<div className="label-right">MAC PORT</div>
</div> </div>
{ports}
</div> </div>
<p>You can access this container using the following IP address and port:</p>
<table className="table">
<thead>
<tr>
<th>DOCKER PORT</th>
<th>MAC PORT</th>
</tr>
</thead>
<tbody>
{ports}
</tbody>
</table>
</div> </div>
</div> </div>
); );

View File

@ -108,7 +108,7 @@ var ContainerListItem = React.createClass({
</div> </div>
</div> </div>
<div className="action"> <div className="action">
<span className="icon icon-delete-3 btn-delete" onClick={this.handleDeleteContainer}></span> <span className="btn circular" onClick={this.handleDeleteContainer}><span className="icon icon-delete"></span></span>
</div> </div>
</li> </li>
</Router.Link> </Router.Link>

View File

@ -1,3 +1,4 @@
var $ = require('jquery');
var _ = require('underscore'); var _ = require('underscore');
var React = require('react/addons'); var React = require('react/addons');
var Router = require('react-router'); var Router = require('react-router');
@ -11,6 +12,17 @@ var ContainerSettings = React.createClass({
}, },
componentDidMount: function() { componentDidMount: function() {
this.init(); this.init();
this.handleResize();
window.addEventListener('resize', this.handleResize);
},
componentWillUnmount: function() {
window.removeEventListener('resize', this.handleResize);
},
componentDidUpdate: function () {
this.handleResize();
},
handleResize: function () {
$('.settings-panel').height(window.innerHeight - 210);
}, },
init: function () { init: function () {
var currentRoute = _.last(this.context.router.getCurrentRoutes()).name; var currentRoute = _.last(this.context.router.getCurrentRoutes()).name;

View File

@ -30,7 +30,7 @@ var ContainerSettingsGeneral = React.createClass({
}, },
handleNameChange: function (e) { handleNameChange: function (e) {
let name = e.target.value; var name = e.target.value;
if (name === this.state.slugName) { if (name === this.state.slugName) {
return; return;
} }
@ -185,9 +185,9 @@ var ContainerSettingsGeneral = React.createClass({
let [id, key, val] = kvp; let [id, key, val] = kvp;
let icon; let icon;
if (index === this.state.env.length - 1) { if (index === this.state.env.length - 1) {
icon = <a onClick={this.handleAddEnvVar} className="only-icon btn btn-positive small"><span className="icon icon-add-1"></span></a>; icon = <a onClick={this.handleAddEnvVar} className="only-icon btn btn-positive small"><span className="icon icon-add"></span></a>;
} else { } else {
icon = <a onClick={this.handleRemoveEnvVar.bind(this, index)} className="only-icon btn btn-action small"><span className="icon icon-cross"></span></a>; icon = <a onClick={this.handleRemoveEnvVar.bind(this, index)} className="only-icon btn btn-action small"><span className="icon icon-delete"></span></a>;
} }
return ( return (

View File

@ -46,29 +46,33 @@ var ContainerSettingsPorts = React.createClass({
}, },
render: function () { render: function () {
if (!this.props.container) { if (!this.props.container) {
return (<div></div>); return false;
} }
var ports = _.map(_.pairs(this.state.ports), pair => { var ports = _.map(_.pairs(this.state.ports), pair => {
var key = pair[0]; var key = pair[0];
var val = pair[1]; var val = pair[1];
return ( return (
<div key={key} className="table-values"> <tr key={key}>
<span className="value-left">{key}</span><span className="icon icon-arrow-right"></span> <td>{key}</td>
<a className="value-right" onClick={this.handleViewLink.bind(this, val.url)}>{val.display}</a> <td><a onClick={this.handleViewLink.bind(this, val.url)}>{val.display}</a></td>
</div> </tr>
); );
}); });
return ( return (
<div className="settings-panel"> <div className="settings-panel">
<div className="settings-section"> <div className="settings-section">
<h3>Configure Ports</h3> <h3>Configure Ports</h3>
<div className="table ports"> <table className="table ports">
<div className="table-labels"> <thead>
<div className="label-left">DOCKER PORT</div> <tr>
<div className="label-right">LOCAL PORT</div> <th>DOCKER PORT</th>
</div> <th>MAC PORT</th>
{ports} </tr>
</div> </thead>
<tbody>
{ports}
</tbody>
</table>
</div> </div>
</div> </div>
); );

View File

@ -56,43 +56,43 @@ var ContainerSettingsVolumes = React.createClass({
if(util.isWindows()) { if(util.isWindows()) {
homeDir = util.windowsToLinuxPath(homeDir); homeDir = util.windowsToLinuxPath(homeDir);
} }
var volumes = _.map(this.props.container.Volumes, (val, key) => { var volumes = _.map(this.props.container.Volumes, (val, key) => {
if (!val || val.indexOf(homeDir) === -1) { if (!val || val.indexOf(homeDir) === -1) {
val = ( val = (
<span> <span className="value-right">No Folder</span>
<a className="value-right">No Folder</a>
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleChooseVolumeClick.bind(this, key)}>Change</a>
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleRemoveVolumeClick.bind(this, key)}>Remove</a>
</span>
); );
} else { } else {
val = ( val = (
<span> <a className="value-right" onClick={this.handleOpenVolumeClick.bind(this, val)}>{val.replace(process.env.HOME, '~')}</a>
<a className="value-right" onClick={this.handleOpenVolumeClick.bind(this, val)}>{val.replace(process.env.HOME, '~')}</a>
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleChooseVolumeClick.bind(this, key)}>Change</a>
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleRemoveVolumeClick.bind(this, key)}>Remove</a>
</span>
); );
} }
return ( return (
<div key={key} className="table-values"> <tr>
<span className="value-left">{key}</span><span className="icon icon-arrow-right"></span> <td>{key}</td>
{val} <td>{val}</td>
</div> <td>
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleChooseVolumeClick.bind(this, key)}>Change</a>
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleRemoveVolumeClick.bind(this, key)}>Remove</a>
</td>
</tr>
); );
}); });
return ( return (
<div className="settings-panel"> <div className="settings-panel">
<div className="settings-section"> <div className="settings-section">
<h3>Configure Volumes</h3> <h3>Configure Volumes</h3>
<div className="table volumes"> <table className="table volumes">
<div className="table-labels"> <thead>
<div className="label-left">DOCKER FOLDER</div> <tr>
<div className="label-right">LOCAL FOLDER</div> <th>DOCKER FOLDER</th>
</div> <th>MAC FOLDER</th>
{volumes} <th></th>
</div> </tr>
</thead>
<tbody>
{volumes}
</tbody>
</table>
</div> </div>
</div> </div>
); );

View File

@ -6,7 +6,6 @@ var containerStore = require('../stores/ContainerStore');
var ContainerList = require('./ContainerList.react'); var ContainerList = require('./ContainerList.react');
var Header = require('./Header.react'); var Header = require('./Header.react');
var metrics = require('../utils/MetricsUtil'); var metrics = require('../utils/MetricsUtil');
var RetinaImage = require('react-retina-image');
var shell = require('shell'); var shell = require('shell');
var machine = require('../utils/DockerMachineUtil'); var machine = require('../utils/DockerMachineUtil');
@ -162,7 +161,7 @@ var Containers = React.createClass({
<h4>Containers</h4> <h4>Containers</h4>
<div className="create"> <div className="create">
<Router.Link to="new"> <Router.Link to="new">
<span className="btn-new icon icon-add-3"></span> <span className="btn btn-new btn-action has-icon btn-hollow"><span className="icon icon-add"></span>New</span>
</Router.Link> </Router.Link>
</div> </div>
</section> </section>
@ -170,9 +169,9 @@ var Containers = React.createClass({
<ContainerList containers={this.state.sorted} newContainer={this.state.newContainer} /> <ContainerList containers={this.state.sorted} newContainer={this.state.newContainer} />
</section> </section>
<section className="sidebar-buttons"> <section className="sidebar-buttons">
<span className="btn-sidebar btn-terminal" onClick={this.handleClickDockerTerminal} onMouseEnter={this.handleMouseEnterDockerTerminal} onMouseLeave={this.handleMouseLeaveDockerTerminal}><RetinaImage src="whaleicon.png"/> <span className="text">DOCKER CLI</span></span> <span className="btn-sidebar btn-terminal" onClick={this.handleClickDockerTerminal} onMouseEnter={this.handleMouseEnterDockerTerminal} onMouseLeave={this.handleMouseLeaveDockerTerminal}><span className="icon icon-docker-cli"></span><span className="text">DOCKER CLI</span></span>
<span className="btn-sidebar btn-feedback" onClick={this.handleClickReportIssue} onMouseEnter={this.handleMouseEnterDockerTerminal} onMouseLeave={this.handleMouseLeaveDockerTerminal}><RetinaImage src="feedback.png"/></span> <span className="btn-sidebar btn-feedback" onClick={this.handleClickReportIssue} onMouseEnter={this.handleMouseEnterDockerTerminal} onMouseLeave={this.handleMouseLeaveDockerTerminal}><span className="icon icon-feedback"></span></span>
<span className="btn-sidebar" onClick={this.handleClickPreferences} onMouseEnter={this.handleMouseEnterDockerTerminal} onMouseLeave={this.handleMouseLeaveDockerTerminal}><RetinaImage src="preferences.png"/></span> <span className="btn-sidebar btn-preferences" onClick={this.handleClickPreferences} onMouseEnter={this.handleMouseEnterDockerTerminal} onMouseLeave={this.handleMouseLeaveDockerTerminal}><span className="icon icon-preferences"></span></span>
</section> </section>
</div> </div>
<Router.RouteHandler pending={this.state.pending} containers={this.state.containers} container={container}/> <Router.RouteHandler pending={this.state.pending} containers={this.state.containers} container={container}/>

View File

@ -4,6 +4,7 @@ var RetinaImage = require('react-retina-image');
var remote = require('remote'); var remote = require('remote');
var ipc = require('ipc'); var ipc = require('ipc');
var autoUpdater = remote.require('auto-updater'); var autoUpdater = remote.require('auto-updater');
var util = require('../utils/Util');
var metrics = require('../utils/MetricsUtil'); var metrics = require('../utils/MetricsUtil');
var Menu = remote.require('menu'); var Menu = remote.require('menu');
var MenuItem = remote.require('menu-item'); var MenuItem = remote.require('menu-item');
@ -94,8 +95,7 @@ var Header = React.createClass({
}); });
accountActions.verify(); accountActions.verify();
}, },
render: function () { renderWindowButtons: function () {
let updateWidget = this.state.updateAvailable && !this.props.hideLogin ? <a className="btn btn-action small no-drag" onClick={this.handleAutoUpdateClick}>UPDATE NOW</a> : null;
let buttons; let buttons;
if (this.state.fullscreen) { if (this.state.fullscreen) {
buttons = ( buttons = (
@ -106,49 +106,92 @@ var Header = React.createClass({
</div> </div>
); );
} else { } else {
buttons = ( if (false /*util.isWindows()*/) {
<div className="buttons"> buttons = (
<div className="windows-buttons">
<div className="windows-button button-close enabled" onClick={this.handleClose}></div>
<div className="windows-button button-minimize enabled" onClick={this.handleMinimize}></div>
<div className="windows-button button-fullscreen enabled" onClick={this.handleFullscreen}></div>
</div>
);
} else {
buttons = (
<div className="buttons">
<div className="button button-close enabled" onClick={this.handleClose}></div> <div className="button button-close enabled" onClick={this.handleClose}></div>
<div className="button button-minimize enabled" onClick={this.handleMinimize}></div> <div className="button button-minimize enabled" onClick={this.handleMinimize}></div>
<div className="button button-fullscreen enabled" onClick={this.handleFullscreen}></div> <div className="button button-fullscreen enabled" onClick={this.handleFullscreen}></div>
</div> </div>
); );
}
} }
return buttons;
let username; },
if (this.props.hideLogin) { renderDashboardHeader: function () {
username = null;
} else if (this.state.username) {
username = (
<span className="no-drag" onClick={this.handleUserClick}>
<RetinaImage src="user.png"/> {this.state.username} {this.state.verified ? null : '(Unverified)'} <RetinaImage src="userdropdown.png"/>
</span>
);
} else {
username = (
<span className="no-drag" onClick={this.handleLoginClick}>
<RetinaImage src="user.png"/> Log In
</span>
);
}
let headerClasses = classNames({ let headerClasses = classNames({
bordered: !this.props.hideLogin, bordered: !this.props.hideLogin,
header: true, header: true,
'no-drag': true 'no-drag': true
}); });
let username;
if (this.props.hideLogin) {
username = null;
} else if (this.state.username) {
username = (
<div className="login-wrapper">
<div className="login no-drag" onClick={this.handleUserClick}>
<span className="icon icon-user"></span> {this.state.username} {this.state.verified ? null : '(Unverified)'} <RetinaImage src="userdropdown.png"/>
</div>
</div>
);
} else {
username = (
<div className="login-wrapper">
<div className="login no-drag" onClick={this.handleLoginClick}>
<span className="icon icon-user"></span> LOGIN
</div>
</div>
);
}
let updateWidget = this.state.updateAvailable && !this.props.hideLogin ? <a className="btn btn-action small no-drag" onClick={this.handleAutoUpdateClick}>UPDATE NOW</a> : null;
return ( return (
<div className={headerClasses}> <div className={headerClasses}>
{buttons} <div className="left-header">
<div className="updates"> {this.renderWindowButtons()}
{updateWidget}
</div>
<div className="login">
{username} {username}
</div> </div>
<div className="right-header">
<div className="updates">
{updateWidget}
</div>
<div className="logo">
<RetinaImage src="logo.png"/>
</div>
</div>
</div> </div>
); );
},
renderBasicHeader: function () {
let headerClasses = classNames({
bordered: !this.props.hideLogin,
header: true,
'no-drag': true
});
return (
<div className={headerClasses}>
<div className="left-header">
{this.renderWindowButtons()}
</div>
<div className="right-header">
</div>
</div>
);
},
render: function () {
if (this.props.hideLogin) {
return this.renderBasicHeader();
} else {
return this.renderDashboardHeader();
}
} }
}); });

View File

@ -4,8 +4,6 @@ var Router = require('react-router');
var shell = require('shell'); var shell = require('shell');
var RetinaImage = require('react-retina-image'); var RetinaImage = require('react-retina-image');
var metrics = require('../utils/MetricsUtil'); var metrics = require('../utils/MetricsUtil');
var OverlayTrigger = require('react-bootstrap').OverlayTrigger;
var Tooltip = require('react-bootstrap').Tooltip;
var containerActions = require('../actions/ContainerActions'); var containerActions = require('../actions/ContainerActions');
var containerStore = require('../stores/ContainerStore'); var containerStore = require('../stores/ContainerStore');
var tagStore = require('../stores/TagStore'); var tagStore = require('../stores/TagStore');
@ -57,12 +55,22 @@ var ImageCard = React.createClass({
containerActions.run(name, repo, this.state.chosenTag); containerActions.run(name, repo, this.state.chosenTag);
this.transitionTo('containerHome', {name}); this.transitionTo('containerHome', {name});
}, },
handleMenuOverlayClick: function () {
let $menuOverlay = $(this.getDOMNode()).find('.menu-overlay');
$menuOverlay.fadeIn(300);
},
handleCloseMenuOverlay: function () {
var $menuOverlay = $(this.getDOMNode()).find('.menu-overlay');
$menuOverlay.fadeOut(300);
},
handleTagOverlayClick: function () { handleTagOverlayClick: function () {
let $tagOverlay = $(this.getDOMNode()).find('.tag-overlay'); let $tagOverlay = $(this.getDOMNode()).find('.tag-overlay');
$tagOverlay.fadeIn(300); $tagOverlay.fadeIn(300);
tagActions.tags(this.props.image.namespace + '/' + this.props.image.name); tagActions.tags(this.props.image.namespace + '/' + this.props.image.name);
}, },
handleCloseTagOverlay: function () { handleCloseTagOverlay: function () {
let $menuOverlay = $(this.getDOMNode()).find('.menu-overlay');
$menuOverlay.hide();
var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay'); var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay');
$tagOverlay.fadeOut(300); $tagOverlay.fadeOut(300);
}, },
@ -77,23 +85,19 @@ var ImageCard = React.createClass({
}, },
render: function () { render: function () {
var self = this; var self = this;
let name; var name;
if (this.props.image.namespace === 'library') { if (this.props.image.namespace === 'library') {
name = ( name = (
<div> <div>
<div className="namespace official">official</div> <div className="namespace official">official</div>
<OverlayTrigger placement="bottom" overlay={<Tooltip>View on Docker Hub</Tooltip>}> <span className="repo">{this.props.image.name}</span>
<span className="repo" onClick={this.handleRepoClick}>{this.props.image.name}</span>
</OverlayTrigger>
</div> </div>
); );
} else { } else {
name = ( name = (
<div> <div>
<div className="namespace">{this.props.image.namespace}</div> <div className="namespace">{this.props.image.namespace}</div>
<OverlayTrigger placement="bottom" overlay={<Tooltip>View on Docker Hub</Tooltip>}> <span className="repo">{this.props.image.name}</span>
<span className="repo" onClick={this.handleRepoClick}>{this.props.image.name}</span>
</OverlayTrigger>
</div> </div>
); );
} }
@ -114,9 +118,9 @@ var ImageCard = React.createClass({
} }
var tags; var tags;
if (self.state.loading) { if (self.state.loading) {
tags = <RetinaImage className="tags-loading" src="loading-white.png"/>; tags = <RetinaImage className="tags-loading" src="loading.png"/>;
} else if (self.state.tags.length === 0) { } else if (self.state.tags.length === 0) {
tags = <span>No Tags</span>; tags = <div className="no-tags">No Tags</div>;
} else { } else {
var tagDisplay = self.state.tags.map(function (t) { var tagDisplay = self.state.tags.map(function (t) {
if (t === self.state.chosenTag) { if (t === self.state.chosenTag) {
@ -134,43 +138,58 @@ var ImageCard = React.createClass({
var badge = null; var badge = null;
if (this.props.image.namespace === 'library') { if (this.props.image.namespace === 'library') {
badge = ( badge = (
<RetinaImage src="official.png"/> <span className="icon icon-badge-official"></span>
); );
} else if (this.props.image.is_private) { } else if (this.props.image.is_private) {
badge = ( badge = (
<RetinaImage src="private.png"/> <span className="icon icon-badge-private"></span>
); );
} }
return ( return (
<div className="image-item"> <div className="image-item">
<div className="tag-overlay" onClick={self.handleCloseTagOverlay}> <div className="overlay menu-overlay">
<div className="menu-item" onClick={this.handleTagOverlayClick.bind(this, this.props.image.name)}>
<span className="icon icon-tag"></span><span className="text">SELECTED TAG: <span className="selected-tag">{this.state.chosenTag}</span></span>
</div>
<div className="menu-item" onClick={this.handleRepoClick}>
<span className="icon icon-open-external"></span><span className="text">VIEW ON DOCKER HUB</span>
</div>
<div className="close-overlay">
<a className="btn btn-action circular" onClick={self.handleCloseMenuOverlay}><span className="icon icon-delete"></span></a>
</div>
</div>
<div className="overlay tag-overlay">
<p>Please select an image tag.</p> <p>Please select an image tag.</p>
{tags} {tags}
<div className="close-overlay" onClick={self.handleCloseTagOverlay}>
<a className="btn btn-action circular"><span className="icon icon-delete"></span></a>
</div>
</div> </div>
<div className="logo" style={logoStyle}> <div className="logo" style={logoStyle}>
<RetinaImage src={imgsrc}/> <RetinaImage src={imgsrc}/>
</div> </div>
<div className="card"> <div className="card">
<div className="badges"> <div className="info">
{badge} <div className="badges">
</div> {badge}
<div className="name"> </div>
{name} <div className="name">
</div> {name}
<div className="description"> </div>
{description} <div className="description">
{description}
</div>
</div> </div>
<div className="actions"> <div className="actions">
<div className="stars"> <div className="favorites">
<span className="icon icon-star-9"></span> <span className="icon icon-favorite"></span>
<span className="text">{this.props.image.star_count}</span> <span className="text">{this.props.image.star_count}</span>
</div> </div>
<div className="tags"> <div className="more-menu" onClick={self.handleMenuOverlayClick}>
<span className="icon icon-bookmark-2"></span> <span className="icon icon-more"></span>
<span className="text" onClick={self.handleTagOverlayClick.bind(self, this.props.image.name)} data-name={this.props.image.name}>{this.state.chosenTag}</span>
</div> </div>
<div className="action"> <div className="action" onClick={self.handleClick}>
<a className="btn btn-action btn-positive" onClick={self.handleClick}>Create</a> CREATE
</div> </div>
</div> </div>
</div> </div>

View File

@ -207,7 +207,7 @@ module.exports = React.createClass({
let magnifierClasses = classNames({ let magnifierClasses = classNames({
hidden: this.state.loading, hidden: this.state.loading,
icon: true, icon: true,
'icon-magnifier': true, 'icon-search': true,
'search-icon': true 'search-icon': true
}); });
@ -215,24 +215,21 @@ module.exports = React.createClass({
<div className="details"> <div className="details">
<div className="new-container"> <div className="new-container">
<div className="new-container-header"> <div className="new-container-header">
<div className="text">
Select a Docker image to create a container.
</div>
<div className="search"> <div className="search">
<div className="search-bar"> <div className="search-bar">
<input type="search" ref="searchInput" className="form-control" placeholder="Search Docker Hub for an image" onChange={this.handleChange}/> <input type="search" ref="searchInput" className="form-control" placeholder="Search image on Docker Hub" onChange={this.handleChange}/>
<div className={magnifierClasses}></div> <div className={magnifierClasses}></div>
<div className={loadingClasses}><div></div></div> <div className={loadingClasses}><div></div></div>
</div> </div>
</div> </div>
</div>
<div className="results">
<div className="results-filters"> <div className="results-filters">
<span className="results-filter results-filter-title">FILTER BY</span> <span className="results-filter results-filter-title">FILTER BY</span>
<span className={`results-filter results-all tab ${filter === 'all' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'all')}>All</span> <span className={`results-filter results-all tab ${filter === 'all' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'all')}>All</span>
<span className={`results-filter results-recommended tab ${filter === 'recommended' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'recommended')}>Recommended</span> <span className={`results-filter results-recommended tab ${filter === 'recommended' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'recommended')}>Recommended</span>
<span className={`results-filter results-userrepos tab ${filter === 'userrepos' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'userrepos')}>My Repositories</span> <span className={`results-filter results-userrepos tab ${filter === 'userrepos' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'userrepos')}>My Repos</span>
</div> </div>
</div>
<div className="results">
{results} {results}
</div> </div>
</div> </div>

View File

@ -42,25 +42,30 @@ var MenuTemplate = function () {
click: function () { click: function () {
metrics.track('Installed Docker Commands'); metrics.track('Installed Docker Commands');
if (!setupUtil.shouldUpdateBinaries()) { if (!setupUtil.shouldUpdateBinaries()) {
dialog.showMessageBox({
message: 'Docker binaries are already installed in /usr/local/bin',
buttons: ['OK']
});
return; return;
} }
let copy = setupUtil.needsBinaryFix() ? let copy = setupUtil.needsBinaryFix() ?
util.exec(setupUtil.copyBinariesCmd() + ' && ' + setupUtil.fixBinariesCmd()) : util.exec(setupUtil.macSudoCmd(setupUtil.copyBinariesCmd() + ' && ' + setupUtil.fixBinariesCmd())) :
util.exec(setupUtil.copyBinariesCmd()); util.exec(setupUtil.copyBinariesCmd());
copy.then(() => { copy.then(() => {
dialog.showMessageBox({ dialog.showMessageBox({
message: 'Docker binaries have been copied to /usr/local/bin', message: 'Docker binaries have been installed under /usr/local/bin',
buttons: ['OK'] buttons: ['OK']
}); });
}).catch(() => {}); }).catch((err) => {
console.log(err);
});
}, },
}, },
{ {
type: 'separator' type: 'separator'
}, }, {
{
label: 'Hide Kitematic', label: 'Hide Kitematic',
accelerator: util.CommandOrCtrl() + '+H', accelerator: util.CommandOrCtrl() + '+H',
selector: 'hide:' selector: 'hide:'

View File

@ -49,6 +49,8 @@ var _steps = [{
} catch (err) { } catch (err) {
throw null; throw null;
} }
} else if (!util.isWindows() && !virtualBox.active()) {
yield util.exec(setupUtil.macSudoCmd(util.escapePath('/Library/Application Support/VirtualBox/LaunchDaemons/VirtualBoxStartup.sh') + ' restart'));
} }
}) })
}, { }, {
@ -147,7 +149,7 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), {
var vboxNeedsInstall = !virtualBox.installed(); var vboxNeedsInstall = !virtualBox.installed();
required.download = vboxNeedsInstall && (!fs.existsSync(vboxfile) || setupUtil.checksum(vboxfile) !== virtualBox.checksum()); required.download = vboxNeedsInstall && (!fs.existsSync(vboxfile) || setupUtil.checksum(vboxfile) !== virtualBox.checksum());
required.install = vboxNeedsInstall; required.install = vboxNeedsInstall || !virtualBox.active();
required.init = required.install || !(yield machine.exists()) || (yield machine.state()) !== 'Running' || !isoversion || util.compareVersions(isoversion, packagejson['docker-version']) < 0; required.init = required.install || !(yield machine.exists()) || (yield machine.state()) !== 'Running' || !isoversion || util.compareVersions(isoversion, packagejson['docker-version']) < 0;
var exists = yield machine.exists(); var exists = yield machine.exists();

View File

@ -62,7 +62,7 @@ module.exports = {
let data = JSON.parse(body); let data = JSON.parse(body);
// If the JWT has expired, then log in again to get a new JWT // If the JWT has expired, then log in again to get a new JWT
if (data && data.detail === 'Signature has expired.') { if (data && data.detail && data.detail.indexOf('expired') !== -1) {
let config = this.config(); let config = this.config();
if (!this.config()) { if (!this.config()) {
this.logout(); this.logout();

View File

@ -5,7 +5,6 @@ var util = require('../utils/Util');
var hubUtil = require('../utils/HubUtil'); var hubUtil = require('../utils/HubUtil');
var repositoryServerActions = require('../actions/RepositoryServerActions'); var repositoryServerActions = require('../actions/RepositoryServerActions');
var tagServerActions = require('../actions/TagServerActions'); var tagServerActions = require('../actions/TagServerActions');
var Promise = require('bluebird');
let REGHUB2_ENDPOINT = process.env.REGHUB2_ENDPOINT || 'https://registry.hub.docker.com/v2'; let REGHUB2_ENDPOINT = process.env.REGHUB2_ENDPOINT || 'https://registry.hub.docker.com/v2';
let searchReq = null; let searchReq = null;
@ -62,7 +61,7 @@ module.exports = {
let data = JSON.parse(body); let data = JSON.parse(body);
let repos = data.repos; let repos = data.repos;
async.map(repos, (repo, cb) => { async.map(repos, (repo, cb) => {
let name = repo.repo; var name = repo.repo;
if (util.isOfficialRepo(name)) { if (util.isOfficialRepo(name)) {
name = 'library/' + name; name = 'library/' + name;
} }

View File

@ -59,7 +59,7 @@ var SetupUtil = {
} }
}, },
macSudoCmd: function (cmd) { macSudoCmd: function (cmd) {
return `${util.escapePath(resources.macsudo())} -p "Kitematic requires administrative privileges to install." sh -c \"${cmd}\"`; return `${util.escapePath(resources.macsudo())} -p "Kitematic requires administrative privileges to install and/or start VirtualBox." sh -c \"${cmd}\"`;
}, },
simulateProgress(estimateSeconds, progress) { simulateProgress(estimateSeconds, progress) {
var times = _.range(0, estimateSeconds * 1000, 200); var times = _.range(0, estimateSeconds * 1000, 200);

View File

@ -15,7 +15,7 @@ module.exports = {
fn(args, options, (stderr, stdout, code) => { fn(args, options, (stderr, stdout, code) => {
if (code) { if (code) {
var cmd = Array.isArray(args) ? args.join(' ') : args; var cmd = Array.isArray(args) ? args.join(' ') : args;
reject(new Error(cmd + ' returned non zero exit code. Stderr: ' + stderr)); reject(new Error(cmd + ' returned non zero exit code.\n===== Stderr =====\n ' + stderr + '\n===== Stdout =====\n' + stdout));
} else { } else {
resolve(stdout); resolve(stdout);
} }

View File

@ -23,9 +23,12 @@ var VirtualBox = {
if(util.isWindows()) { if(util.isWindows()) {
return fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe') && fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VirtualBox.exe'); return fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe') && fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VirtualBox.exe');
} else { } else {
return fs.existsSync('/usr/bin/VBoxManage') && fs.existsSync('/Applications/VirtualBox.app'); return fs.existsSync('/usr/bin/VBoxManage') && fs.existsSync('/Applications/VirtualBox.app') && fs.existsSync('/Applications/VirtualBox.app/Contents/MacOS/VBoxManage');
} }
}, },
active: function () {
return fs.existsSync('/dev/vboxnetctl');
},
version: function () { version: function () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
util.exec([this.command(), '-v']).then(stdout => { util.exec([this.command(), '-v']).then(stdout => {

View File

@ -5,32 +5,31 @@
display: flex; display: flex;
flex: 1 auto; flex: 1 auto;
flex-direction: row; flex-direction: row;
padding: 10px 27px; padding: 1rem;
.left { .left {
width: 60%; width: 100%;
flex-direction: column; flex-direction: column;
margin-right: 30px; margin-right: 1rem;
} }
.right { .right {
width: 40%; width: 40%;
min-width: 200px;
max-width: 600px;
flex-direction: column; flex-direction: column;
} }
.subtext { .full {
text-align: right; width: 100%;
color: @gray-lighter;
margin-top: 2px;
transition: all 0.25s;
&:hover {
color: darken(@gray-lightest, 10%);
}
} }
.web-preview { .web-preview {
margin-bottom: 50px; margin-bottom: 1rem;
.widget { .widget {
.widget-style(); .widget-style();
.top-bar {
.widget-bar-style();
}
background-color: white; background-color: white;
width: 100%; width: 100%;
height: 95%; height: 100%;
p { p {
font-size: 13px; font-size: 13px;
color: @gray-normal; color: @gray-normal;
@ -38,63 +37,41 @@
padding-bottom: 0px; padding-bottom: 0px;
} }
.table { .table {
padding-left: 20px; color: @gray-normal;
padding-right: 20px; font-size: 12px;
.icon-arrow-right { margin-left: 0.2rem;
color: #BBB; max-width: 300px;
font-size: 13px; tr {
margin: 0px 8px; &:first-child {
position: relative; td {
top: 2px; border: none;
flex: 0 auto;
min-width: 13px;
}
&.ports {
.table-labels {
margin-top: 20px;
flex: 1 auto;
display: flex;
font-size: 10px;
color: @gray-light;
.label-left {
flex: 0 auto;
min-width: 60px;
margin-right: 30px;
text-align: right;
}
.label-right {
flex: 1 auto;
display: inline-block;
} }
} }
.table-values { td {
flex: 1 auto; border-color: @color-divider;
display: flex; &.right {
flex-direction: row;
margin: 8px 0;
font-size: 12px;
.value-left {
text-align: right; text-align: right;
min-width: 70px;
flex: 0 auto;
padding: 0px;
}
.value-right {
flex: 1 auto;
-webkit-user-select: text;
max-width: 170px;
padding: 0px;
} }
} }
} }
th {
font-size: 10px;
color: @gray-lighter;
font-weight: 500;
border: none;
&.right {
text-align: right;
}
}
} }
.frame { .frame {
padding-top: 8rem;
border: 0; border: 0;
position: absolute; position: absolute;
left: -100%; left: -100%;
top: -100%; top: -100%;
height: 400%; height: 400%;
width: 401%; width: 400%;
transform: scale(0.5); transform: scale(0.5);
} }
.frame-overlay { .frame-overlay {
@ -125,24 +102,32 @@
} }
} }
.mini-logs { .mini-logs {
margin-bottom: 50px;
.widget { .widget {
.widget-style(); .widget-style();
background-color: @gray-darkest; .top-bar {
color: @gray-lightest; .widget-bar-style();
background-color: darken(@gray-darkest, 3%);
border-bottom: 1px solid rgba(255,255,255,0.1);
.text {
color: white;
}
}
height: 100%; height: 100%;
padding: 10px;
font-family: @font-code;
font-size: 7px;
white-space: pre;
.logs { .logs {
overflow: hidden; background-color: @gray-darkest;
color: @gray-lightest;
font-family: @font-code;
font-size: 10px;
white-space: pre-wrap;
-webkit-user-select: text;
padding: 1.2rem 1.2rem 5rem 1.2rem;
overflow: auto;
height: 100%; height: 100%;
} }
p { p {
margin-bottom: 0px; margin-bottom: 0px;
} }
.mini-logs-overlay { /*.mini-logs-overlay {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -167,29 +152,40 @@
background-color: @gray-darkest; background-color: @gray-darkest;
opacity: 0.75; opacity: 0.75;
} }
} }*/
} }
} }
.folders { .folders {
.widget { .widget {
.widget-style(); .widget-style();
padding: 10px 5px;
background-color: white; background-color: white;
display: flex; .top-bar {
.folder { .widget-bar-style();
width: 110px; }
padding: 5px; .folders-list {
&:hover { .folder {
background-color: darken(@color-background, 2%); display: flex;
border-radius: 10px; padding: 1rem;
} border-bottom: 1px solid @color-divider;
img { &:last-child {
display: block; border-bottom: none;
margin: 0 auto; }
} &:hover {
.text { background-color: @color-box-button;
margin-top: 4px; }
text-align: center; img {
width: 32px;
min-width: 32px;
height: 25px;
}
.text {
margin-top: 0.3rem;
margin-left: 1rem;
width: 180px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
} }
} }
} }

View File

@ -1,38 +1,38 @@
.details-panel .settings { .details-panel .settings {
display: flex; display: flex;
flex: 1 auto; flex: 1 auto;
flex-direction: row; flex-direction: column;
background-color: white;
margin: 1rem;
margin-bottom: 0;
border: 1px solid @color-divider;
border-radius: @border-radius;
.settings-menu { .settings-menu {
min-width: 180px; display: flex;
flex: 0 auto; flex: 0 auto;
height: 40px;
border-bottom: 1px solid @color-divider;
ul { ul {
position: fixed;
margin: 0;
padding: 0;
padding-top: 14px;
display: flex; display: flex;
flex-direction: column; flex-direction: row;
width: 100%;
justify-content: flex-end;
a { a {
min-width: 160px;
margin-left: 12px;
color: @gray-normal; color: @gray-normal;
flex-shrink: 0;
cursor: default; cursor: default;
outline: none; outline: none;
margin-bottom: 10px;
&.active { &.active {
li { li {
color: white; font-weight: 500;
border-radius: 40px; border-bottom: 3px solid @brand-primary;
.brand-gradient(); color: @gray-darkest;
} }
} }
&:hover { &:hover {
text-decoration: none; text-decoration: none;
li { li {
cursor: default; cursor: default;
border-radius: 40px; color: @gray-darkest;
background-color: @gray-lightest;
} }
} }
&:focus { &:focus {
@ -40,24 +40,32 @@
} }
} }
li { li {
transition: all 140ms;
vertical-align: middle; vertical-align: middle;
padding: 5px 12px; padding: 0.9rem 1.6rem;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
} }
} }
} }
.settings-panel { .settings-panel {
padding-left: 20px; padding: 2rem;
flex: 1 auto; flex: 1 auto;
overflow-x: hidden; overflow-x: hidden;
h3 {
margin-top: 0;
color: @gray-darker;
font-weight: 400;
font-size: 18px;
}
.settings-section { .settings-section {
margin-bottom: 40px; margin-bottom: 4rem;
} }
.container-name { .container-name {
margin-bottom: 20px; margin-bottom: 2rem;
input { input {
width: 40%; width: 100%;
max-width: 500px;
} }
p { p {
font-weight: 300; font-weight: 300;
@ -71,8 +79,9 @@
} }
.env-vars-labels { .env-vars-labels {
width: 100%; width: 100%;
font-size: 12px; font-size: 14px;
color: @gray-light; color: @gray-lighter;
font-weight: 500;
margin-left: 5px; margin-left: 5px;
margin-bottom: 5px; margin-bottom: 5px;
margin-top: 20px; margin-top: 20px;
@ -102,98 +111,32 @@
} }
} }
.table { .table {
margin-bottom: 0;
.icon-arrow-right {
color: #BBB;
font-size: 20px;
margin: 0px 10px;
flex: 0 auto;
min-width: 13px;
}
&.ports { &.ports {
.table-labels { max-width: 500px;
margin-top: 20px;
flex: 1 auto;
display: flex;
font-size: 12px;
color: @gray-light;
.label-left {
flex: 0 auto;
min-width: 85px;
margin-right: 30px;
text-align: right;
}
.label-right {
flex: 1 auto;
display: inline-block;
margin-left: 10px;
width: 40%;
}
}
.table-values {
flex: 1 auto;
display: flex;
flex-direction: row;
margin: 8px 0;
.value-left {
text-align: right;
min-width: 85px;
flex: 0 auto;
padding: 0px;
}
.value-right {
flex: 1 auto;
-webkit-user-select: text;
max-width: 170px;
padding: 0px;
}
label {
margin-left: 8px;
margin-top: 1px;
font-weight: 400;
font-size: 13px;
}
input[type="checked"] {
}
}
} }
&.volumes { &.volumes {
.table-labels { max-width: 100%;
margin-top: 20px; }
flex: 1 auto; color: @gray-normal;
display: flex; tr {
font-size: 12px; &:first-child {
color: @gray-light; td {
.label-left { border: none;
flex: 0 auto;
margin-right: 30px;
}
.label-right {
flex: 1 auto;
display: inline-block;
margin-left: 10px;
} }
} }
.table-values { td {
flex: 1 auto; border-color: @color-divider;
display: flex;
flex-direction: row;
margin: 8px 0;
.value-left {
flex: 0 auto;
padding: 0px;
}
.value-right {
flex: 1 auto;
-webkit-user-select: text;
padding: 0px;
}
.btn {
margin-left: 10px;
}
} }
} }
th {
font-size: 14px;
color: @gray-lighter;
font-weight: 500;
border: none;
}
.btn {
margin-right: 0.5rem;
}
} }
} }
} }

View File

@ -1,10 +1,10 @@
.header { .header {
min-height: 50px; min-height: 40px;
-webkit-app-region: drag; -webkit-app-region: drag;
-webkit-user-select: none; -webkit-user-select: none;
&.bordered { &.bordered {
border-bottom: 1px solid #E7E7E7; border-bottom: 1px solid @color-divider;
} }
display: flex; display: flex;
@ -12,31 +12,44 @@
.no-drag { .no-drag {
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
} }
.left-header {
display: flex;
min-width: @sidebar-width + 1px;
}
.updates { .updates {
flex: 1 auto; flex: 1 auto;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
margin-right: 20px; margin-right: 20px;
}
img { .logo {
margin: 0 14px; position: absolute;
height: 16px; right: 1rem;
width: 20px; top: 0.8rem;
} }
.login-wrapper {
flex: 1 auto;
display: flex;
justify-content: flex-end;
} }
.login { .login {
flex: 0 auto; flex: 0 auto;
display: flex; display: flex;
color: #88919C;
align-items: center; align-items: center;
justify-content: flex-end; border-left: 1px solid @color-divider;
margin-right: 13px; border-right: 1px solid @color-divider;
padding: 0 1rem 0 1rem;
.box-button();
&:active { &:active {
img, span { img, span {
-webkit-filter: brightness(0.8); -webkit-filter: brightness(0.9);
} }
} }
@ -45,10 +58,17 @@
} }
} }
.windows-buttons {
display: flex;
align-items: center;
justify-content: center;
}
.buttons { .buttons {
display: flex; display: flex;
margin-left: 14px; margin: 0 1.5rem;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
&:hover { &:hover {

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +1,28 @@
/* Sidebar */ /* Sidebar */
.sidebar { .sidebar {
padding-top: 10px;
background-color: white; background-color: white;
margin: 0; margin: 0;
border-right: 1px solid @color-divider; border-right: 1px solid @color-divider;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-width: 260px; min-width: @sidebar-width;
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
.sidebar-header { .sidebar-header {
flex: 0 auto; flex: 0 auto;
min-width: 240px; min-width: @sidebar-width;
min-height: 47px; min-height: 44px;
display: flex; display: flex;
border-bottom: 1px solid transparent; border-bottom: 1px solid transparent;
transition: border-bottom 0.25s; transition: border-bottom 0.25s;
&.sep { &.sep {
border-bottom: 1px solid #EEE; border-bottom: 1px solid @color-divider;
box-shadow: 0px 2px 3px 0px rgba(0,0,0,0.03); box-shadow: 0px 2px 3px 0px rgba(0,0,0,0.03);
} }
h4 { h4 {
align-self: flex-start; align-self: flex-start;
padding-left: 26px; padding: 0.4rem 0 0 1.4rem;
margin: 14px 0 0;
display: inline-block; display: inline-block;
position: relative; position: relative;
} }
@ -32,27 +30,17 @@
display: flex; display: flex;
flex: 1 auto; flex: 1 auto;
justify-content: flex-end; justify-content: flex-end;
margin-right: 20px; margin: 0.8rem 0.8rem 0 0;
margin-top: 3px;
a { a {
display: block; display: block;
text-decoration: none; text-decoration: none;
cursor: default; cursor: default;
&:focus {
outline: 0;
}
&.active { &.active {
.btn-new { .btn-new {
opacity: 0.3; opacity: 0.3;
&:hover {
color: @brand-action;
}
}
}
.btn-new {
display: block;
font-size: 24px;
color: @brand-action;
transition: all 0.25s;
&:hover {
color: darken(@brand-action, 15%);
} }
} }
} }
@ -66,7 +54,7 @@
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
box-sizing: border-box; box-sizing: border-box;
max-width: 260px; max-width: @sidebar-width;
ul { ul {
flex: 1 auto; flex: 1 auto;
margin: 0; margin: 0;
@ -88,15 +76,20 @@
text-decoration: none; text-decoration: none;
cursor: default; cursor: default;
} }
&:first-child {
li {
//border-top: 1px solid @color-divider;
}
}
&:focus { &:focus {
text-decoration: none; text-decoration: none;
} }
&.active { &.active {
background: @brand-action; background: @brand-action;
li { li {
height: 57px; height: 45px;
border-bottom: none; border-bottom: none;
.brand-gradient(); background-color: @brand-primary;
.name { .name {
color: white; color: white;
} }
@ -104,31 +97,33 @@
color: white; color: white;
opacity: 0.8; opacity: 0.8;
} }
.btn-delete { .action {
font-size: 24px; .btn {
color: white; border: 1px solid white;
position: relative; .icon {
z-index: 9999; color: white;
}
}
} }
.state-new { .state-new {
.at2x('container-white.png', 20px, 20px); .at2x('container-white.png', @container-state-size, @container-state-size);
} }
.state-running { .state-running {
.at2x('running-white.png', 20px, 20px); .at2x('running-white.png', @container-state-size, @container-state-size);
.runningwave { .runningwave {
.at2x('runningwave-white.png', 20px, 20px); .at2x('runningwave-white.png', @container-state-size, @container-state-size);
} }
} }
.state-paused { .state-paused {
.at2x('wavy-white.png', 20px, 20px); .at2x('wavy-white.png', @container-state-size, @container-state-size);
} }
.state-stopped { .state-stopped {
.at2x('stopped-white.png', 20px, 20px); .at2x('stopped-white.png', @container-state-size, @container-state-size);
} }
.state-downloading { .state-downloading {
.at2x('downloading-white.png', 20px, 20px); .at2x('downloading-white.png', @container-state-size, @container-state-size);
.downloading-arrow { .downloading-arrow {
.at2x('downloading-arrow-white.png', 20px, 20px); .at2x('downloading-arrow-white.png', @container-state-size, @container-state-size);
} }
} }
} }
@ -136,28 +131,33 @@
} }
li { li {
vertical-align: middle; vertical-align: middle;
padding: 10px 16px 10px 26px; padding: 0.7rem 1rem 0.7rem 1.4rem;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
height: 57px; height: 45px;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
&:hover {
background-color: @color-box-button;
}
.info { .info {
font-size: 13px; margin-left: 1rem;
margin-left: 16px;
.name { .name {
text-overflow: ellipsis; text-overflow: ellipsis;
max-width: 140px; max-width: @sidebar-text-overflow-width;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
font-size: 14px; font-size: 12px;
font-weight: 400; font-weight: 400;
color: @gray-darkest; color: @gray-darkest;
} }
.image { .image {
margin-top: -0.1rem;
color: @gray-light; color: @gray-light;
font-size: 10px; font-size: 10px;
font-weight: 400; font-weight: 400;
text-overflow: ellipsis; text-overflow: ellipsis;
max-width: 140px; max-width: @sidebar-text-overflow-width;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
} }
@ -166,42 +166,41 @@
display: none; display: none;
flex: 1; flex: 1;
position: relative; position: relative;
top: 5px; top: 0.2rem;
text-align: right; text-align: right;
margin-right: 4px; .btn {
.btn-delete { border: 1px solid @gray-lighter;
font-size: 24px; .icon {
color: @gray-lighter; color: @gray-lighter;
position: relative; }
z-index: 9999;
} }
} }
.state { .state {
margin-top: 9px; margin-top: 0.6rem;
display: inline-block; display: inline-block;
position: relative; position: relative;
min-width: 20px; min-width: @container-state-size;
height: 20px; height: @container-state-size;
} }
.state-error { .state-error {
.at2x('error.png', 20px, 20px); .at2x('error.png', @container-state-size, @container-state-size);
} }
.state-stopped { .state-stopped {
.at2x('stopped.png', 20px, 20px); .at2x('stopped.png', @container-state-size, @container-state-size);
} }
.state-paused { .state-paused {
.at2x('paused.png', 20px, 20px); .at2x('paused.png', @container-state-size, @container-state-size);
} }
.state-new { .state-new {
.at2x('container.png', 20px, 20px); .at2x('container.png', @container-state-size, @container-state-size);
} }
.state-downloading { .state-downloading {
.at2x('downloading.png', 20px, 20px); .at2x('downloading.png', @container-state-size, @container-state-size);
overflow: hidden; overflow: hidden;
.downloading-arrow { .downloading-arrow {
width: 20px; width: @container-state-size;
height: 20px; height: @container-state-size;
.at2x('downloading-arrow.png', 20px, 20px); .at2x('downloading-arrow.png', @container-state-size, @container-state-size);
position: absolute; position: absolute;
-webkit-animation-name: translatedownload; -webkit-animation-name: translatedownload;
-webkit-animation-duration: 1.8s; -webkit-animation-duration: 1.8s;
@ -210,14 +209,14 @@
} }
} }
.state-running { .state-running {
.at2x('running.png', 20px, 20px); .at2x('running.png', @container-state-size, @container-state-size);
overflow: hidden; overflow: hidden;
.runningwave { .runningwave {
position: absolute; position: absolute;
width: 40px; width: @container-state-size * 2;
height: 20px; height: @container-state-size;
left: -20px; left: -20px;
.at2x('runningwave.png', 20px, 20px); .at2x('runningwave.png', @container-state-size, @container-state-size);
-webkit-animation-name: translatewave; -webkit-animation-name: translatewave;
-webkit-animation-duration: 7.0s; -webkit-animation-duration: 7.0s;
-webkit-animation-iteration-count: infinite; -webkit-animation-iteration-count: infinite;
@ -226,9 +225,9 @@
} }
.state-restarting { .state-restarting {
display: inline-block; display: inline-block;
width: 20px; width: @container-state-size;
height: 20px; height: @container-state-size;
.at2x('restarting.png', 20px, 20px); .at2x('restarting.png', @container-state-size, @container-state-size);
background-repeat: repeat-x; background-repeat: repeat-x;
-webkit-animation-delay: -1s; -webkit-animation-delay: -1s;
-webkit-animation-name: rotate; -webkit-animation-name: rotate;
@ -244,8 +243,8 @@
/* Sidebar Buttons */ /* Sidebar Buttons */
.sidebar-buttons { .sidebar-buttons {
border-top: 1px solid #F0F4F8; border-top: 1px solid @color-divider;
min-height: 48px; min-height: 40px;
flex: 0 auto; flex: 0 auto;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -258,10 +257,7 @@
height: 18px; height: 18px;
} }
.btn-sidebar { .btn-sidebar {
font-size: 13px; .box-button();
font-weight: 500;
color: @brand-primary;
flex: 0 auto; flex: 0 auto;
display: flex; display: flex;
align-items: center; align-items: center;
@ -270,20 +266,26 @@
&:active { &:active {
img, .text { img, .text {
-webkit-filter: brightness(0.7); -webkit-filter: brightness(0.9);
} }
} }
} }
.btn-terminal { .btn-terminal {
flex: 1 auto; flex: 1 auto;
border-right: 1px solid #F0F4F8; border-right: 1px solid @color-divider;
img { color: @brand-primary;
margin-right: 10px;
}
} }
.btn-feedback { .btn-feedback {
border-right: 1px solid #F0F4F8; border-right: 1px solid @color-divider;
.icon {
margin-right: 0;
}
}
.btn-preferences {
.icon {
margin-right: 0;
}
} }
.btn { .btn {
position: relative; position: relative;

View File

@ -28,6 +28,9 @@ html, body {
-webkit-user-drag: none; -webkit-user-drag: none;
font-family: @font-regular; font-family: @font-regular;
cursor: default; cursor: default;
-webkit-font-smoothing: subpixel-antialiased;
text-rendering: optimizelegibility;
//-webkit-font-smoothing: antialiased;
img { img {
pointer-events: none; pointer-events: none;
} }

View File

@ -23,15 +23,62 @@
} }
.widget-style() { .widget-style() {
border-radius: 4px; border-radius: @border-radius;
border: 1px solid @gray-lighter; border: 1px solid @color-divider;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
} }
.widget-bar-style() {
height: 40px;
background-color: white;
border-bottom: 1px solid @color-divider;
display: flex;
align-items: flex-start;
position: relative;
z-index: 99999;
.text {
flex: 1 auto;
padding: 1.1rem;
text-transform: uppercase;
font-size: 12px;
font-weight: 500;
color: @gray-light;
}
.action {
flex: 0 auto;
height: 40px;
width: 40px;
align-self: flex-end;
border-left: 1px solid @color-divider;
padding-top: 0.8rem;
padding-left: 0.9rem;
.box-button();
}
}
.fade-in() { .fade-in() {
opacity: 0; opacity: 0;
-webkit-animation: fadein ease-in 1; -webkit-animation: fadein ease-in 1;
-webkit-animation-fill-mode: forwards; -webkit-animation-fill-mode: forwards;
-webkit-animation-duration: 0.2s; -webkit-animation-duration: 0.2s;
} }
.box-button {
transition: all 100ms;
color: @gray-light;
font-size: 10px;
font-weight: 500;
.icon {
font-size: 2rem;
margin-right: 0.7rem;
margin-top: 0.5rem;
}
&:hover {
background-color: @color-box-button;
}
&:active {
box-shadow: inset 0 0 1px @color-divider;
background-color: darken(@color-box-button, 1%);
}
}

View File

@ -31,7 +31,6 @@
display: flex; display: flex;
flex: 1 auto; flex: 1 auto;
flex-direction: column; flex-direction: column;
padding: 25px 0 0;
.spinner { .spinner {
display: inline-block; display: inline-block;
@ -43,35 +42,6 @@
flex: 1 auto; flex: 1 auto;
color: @gray-normal; color: @gray-normal;
.results-filters {
flex: 0 auto;
flex-shrink: 0;
display: flex;
flex-direction: row;
justify-content: flex-end;
font-size: 13px;
margin: 0 10px;
margin-bottom: 10px;
.results-filter {
text-align: center;
margin: 0 10px;
min-width: 40px;
&.tab {
&:hover {
border-radius: 40px;
background-color: @gray-lightest;
}
}
}
.results-filter-title {
color: @gray-lighter;
font-weight: 500;
padding-top: 6px;
}
}
.no-results { .no-results {
flex: 1 auto; flex: 1 auto;
display: flex; display: flex;
@ -115,45 +85,49 @@
} }
} }
.new-container-header { .new-container-header {
margin: 0 20px 8px; background-color: white;
height: 45px;
border-bottom: 1px solid @color-divider;
display: flex; display: flex;
flex: 0 auto; flex: 0 auto;
flex-shrink: 0; flex-shrink: 0;
.text {
flex: 1 auto;
width: 50%;
font-size: 14px;
color: @gray-normal;
}
.search { .search {
flex: 1 auto; flex: 1 auto;
.search-bar { .search-bar {
top: -7px;
position: relative; position: relative;
.loading { .loading {
position: absolute; position: absolute;
top: 7px; top: 13px;
left: 10px; left: 13px;
} }
.search-icon { .search-icon {
font-size: 18px; font-size: 20px;
color: @gray-lighter; color: @gray-lighter;
position: absolute; position: absolute;
top: 5px; top: 10px;
left: 10px; left: 13px;
} }
input { input {
border-radius: 20px; transition: all 140ms;
font-size: 12px; width: 90%;
height: 30px; max-width: 500px;
padding: 4px 8px 4px 35px; border-radius: 0;
position: relative;
top: -1px;
left: -1px;
font-size: 14px;
height: 46px;
padding-left: 4.4rem;
color: @gray-darkest; color: @gray-darkest;
margin-bottom: 3px;
border-color: @gray-lighter;
box-shadow: none; box-shadow: none;
border: 1px solid @color-divider;
//border-right: 1px solid transparent;
&:hover {
border: 1px solid @gray-lighter;
}
&:focus { &:focus {
box-shadow: none; box-shadow: none;
border-color: @brand-primary; border: 1px solid @brand-primary;
} }
&::-webkit-input-placeholder { &::-webkit-input-placeholder {
color: @gray-lighter; color: @gray-lighter;
@ -162,42 +136,94 @@
} }
} }
} }
.results-filters {
margin: 1.2rem 1.2rem 0 0;
.results-filter-title {
font-size: 12px;
color: @gray-lighter;
font-weight: 500;
margin-right: 0.7rem;
}
}
} }
} }
.result-grids { .result-grids {
overflow: auto; overflow: auto;
margin: 0 0 0 20px; margin: 0 0 0 1.3rem;
.result-grid { .result-grid {
display: flex;
align-content: stretch;
display: flex; display: flex;
flex-flow: row wrap; flex-flow: row wrap;
flex-wrap: wrap;
justify-content: flex-start; justify-content: flex-start;
margin-top: 10px; margin-top: 10px;
.image-item { .image-item {
display: flex; display: flex;
flex: 1 0;
position: relative; position: relative;
width: 320px; min-width: @image-card-width;
height: 166px; max-width: @image-card-width;
border-radius: 4px; height: @image-card-height;
border-radius: @border-radius;
background-color: white; background-color: white;
margin-right: 20px; margin-right: 1rem;
margin-bottom: 20px; margin-bottom: 1rem;
.tag-overlay { .overlay {
z-index: 999; display: flex;
background-color: rgba(0,0,0,0.8); flex: 1 0;
border-radius: 4px; background-color: white;
width: 320px; border-radius: @border-radius;
height: 166px; min-width: @image-card-width;
max-width: @image-card-width;
height: @image-card-height;
position: absolute; position: absolute;
color: white; color: @gray-normal;
font-size: 13px;
display: none; display: none;
padding: 10px; border: 1px solid @color-divider;
}
.menu-overlay {
z-index: 999;
.menu-item {
padding: 0.8rem 1rem;
border-bottom: 1px solid @color-divider;
height: 40px;
.box-button();
.selected-tag {
color: @brand-primary;
margin-left: 0.3rem;
}
.icon {
font-size: 18px;
}
.text {
position: relative;
top: -0.4rem;
margin-left: 0.3rem;
}
}
.close-overlay {
position: absolute;
bottom: 1rem;
right: 1rem;
}
}
.tag-overlay {
z-index: 1000;
.close-overlay {
position: absolute;
top: 0.6rem;
right: 0.7rem;
}
p { p {
color: white; color: @gray-normal;
padding-bottom: 7px; padding: 0.8rem 1rem;
border-bottom: 1px solid rgba(255,255,255,0.2); margin-bottom: 0;
border-bottom: 1px solid @color-divider;
} }
.tag-list { .tag-list {
display: flex; display: flex;
@ -205,133 +231,149 @@
align-items: flex-start; align-items: flex-start;
align-content: flex-start; align-content: flex-start;
flex-flow: row wrap; flex-flow: row wrap;
height: 100px; height: 90px;
overflow: auto; overflow: auto;
padding: 0.5rem;
.tag { .tag {
font-size: 10px;
padding: 0.3rem 0.6rem;
display: inline-block; display: inline-block;
flex: 0 auto; flex: 0 auto;
margin-right: 2px; border-radius: @border-radius;
padding: 3px 5px; margin-right: 0.3rem;
margin-bottom: 0.3rem;
border: 1px solid transparent;
&.active { &.active {
background-color: rgba(255,255,255,0.2); background-color: @brand-primary;
border-radius: 20px; color: white;
&:hover {
background-color: @brand-primary;
color: white;
border: 1px solid transparent;
}
} }
&:hover { &:hover {
background-color: rgba(255,255,255,0.2); background-color: @color-box-button;
border-radius: 20px; border: 1px solid @color-divider;
} }
} }
} }
.tags-loading { .tags-loading {
position: relative; position: relative;
left: 42%; left: 45%;
top: 20%;
text-align: center; text-align: center;
margin: 14px auto; margin-top: 3rem;
-webkit-animation-name: spin; -webkit-animation-name: spin;
-webkit-animation-duration: 1.8s; -webkit-animation-duration: 1.8s;
-webkit-animation-iteration-count: infinite; -webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear; -webkit-animation-timing-function: linear;
} }
.no-tags {
color: @gray-lighter;
text-align: center;
margin-top: 3rem;
}
} }
.logo { .logo {
flex: 1 auto; width: 60px;
min-width: 90px; min-width: 60px;
max-width: 60px;
background-color: @brand-action; background-color: @brand-action;
border-top-left-radius: 4px; border-top-left-radius: @border-radius;
border-bottom-left-radius: 4px; border-bottom-left-radius: @border-radius;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
box-shadow: inset 0px 0px 0px 1px rgba(0,0,0,0.2); box-shadow: inset 0px 0px 0px 1px rgba(0,0,0,0.2);
img { img {
margin-top: 15px; width: 35px;
height: auto;
margin-top: 1.2rem;
} }
} }
.card { .card {
padding: 10px 20px 10px 20px;
position: relative; position: relative;
border: 1px solid @gray-lighter; border: 1px solid darken(@gray-lightest, 5%);
border-left: 0; border-left: 0;
border-top-right-radius: 4px; border-top-right-radius: @border-radius;
border-bottom-right-radius: 4px; border-bottom-right-radius: @border-radius;
.badges { .info {
position: absolute; padding: 0.7rem 1rem 0.7rem 1.2rem;
right: 15px; .badges {
top: 8px; color: @brand-primary;
} position: absolute;
.name { right: 0.6rem;
font-size: 18px; top: 0.7rem;
color: @gray-darkest; }
margin-bottom: 0px; .name {
position: relative; color: @gray-darkest;
width: 190px; position: relative;
.namespace { width: 190px;
font-size: 11px; .namespace {
color: @gray-lighter; font-size: 10px;
margin-bottom: -3px; color: @gray-normal;
&.official { &.official {
color: @brand-action; color: @brand-action;
}
}
.repo {
font-size: 13px;
font-weight: 500;
display: inline-block;
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
} }
.repo { .description {
display: inline-block; font-size: 10px;
max-width: 190px; color: @gray-light;
overflow: hidden; padding-right: 1rem;
height: 44px;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
display: -webkit-box;
word-wrap: break-word;
} }
} }
.description {
font-size: 12px;
color: @gray-normal;
height: 50px;
width: 190px;
text-overflow: ellipsis;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
display: -webkit-box;
word-wrap: break-word;
}
.actions { .actions {
width: 190px; display: flex;
position: absolute; flex-direaction: column;
bottom: 8px; justify-content: flex-end;
.stars { height: 33px;
height: 15px; border-top: 1px solid @color-divider;
.favorites {
flex: 1 auto;
font-size: 10px; font-size: 10px;
color: @gray-darker; color: @gray-normal;
border-right: 1px solid @gray-lightest; border-right: 1px solid @color-divider;
padding-right: 10px; padding: 1.1rem 1.2rem;
.icon { .icon {
position: relative; position: relative;
font-size: 16px; font-size: 11px;
margin-right: 3px; margin-right: 0.5rem;
top: -1px; color: @gray-normal;
color: @gray-darkest;
} }
.text { .text {
position: relative; position: relative;
top: -6px; top: -0.2rem;
} }
} }
.tags { .tags {
height: 15px; flex: 1 auto;
font-size: 10px; font-size: 10px;
color: @gray-darker; color: @gray-darker;
padding-left: 10px; padding-left: 1rem;
.icon { .key {
position: relative; position: relative;
font-size: 12px; font-weight: 400;
margin-right: 2px; color: @gray-light;
top: 2px;
color: @gray-darkest;
} }
.text { .text {
position: relative; position: relative;
top: -2px; color: @brand-action;
padding: 3px 5px;
text-decoration: underline;
&:hover { &:hover {
background-color: @brand-action; background-color: @brand-action;
color: white; color: white;
@ -339,17 +381,31 @@
} }
} }
} }
.more-menu {
flex: 0 auto;
width: 39px;
padding: 0.4rem 1rem;
font-size: 20px;
.box-button();
}
.action { .action {
flex: 1 auto; flex: 0 auto;
.btn { position: relative;
text-align: right; top: -1px;
position: relative; right: -1px;
float: right; height: 34px;
top: -7px; .box-button();
padding: 0.9rem 1rem;
color: @brand-primary;
border: 1px solid @brand-primary;
border-bottom-right-radius: @border-radius;
transition: all 140ms;
&:hover {
background-color: @brand-action;
color: white;
border: 1px solid darken(@brand-primary, 10%);
} }
} }
display: flex;
flex-direaction: row;
} }
} }
} }

View File

@ -3,135 +3,173 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
flex: 1; flex: 1 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.header-section {
position: absolute;
top: 10px;
.text {
font-size: 14px;
color: @gray-darker;
font-weight: 500;
margin-left: 1.5rem;
.status {
padding: 0.3rem;
border-radius: @border-radius;
font-size: 10px;
font-weight: 500;
position: relative;
left: 1rem;
color: white;
border: 1px solid white;
&.running {
border-color: @brand-positive;
color: @brand-positive;
}
&.paused {
border-color: @gray-lighter;
color: @gray-lighter;
}
&.stopped {
border-color: @gray-lighter;
color: @gray-lighter;
}
&.downloading {
border-color: @brand-action;
color: @brand-action;
}
}
}
}
.details-subheader { .details-subheader {
flex: 0 auto; flex: 0 0;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
position: relative; position: relative;
border-bottom: 1px solid @color-divider; border-bottom: 1px solid @color-divider;
border-radius: @border-radius;
background-color: white; background-color: white;
height: 51px; top: -1px;
padding: 6px 10px 10px 24px; font-size: 10px;
font-size: 13px; padding-left: 0.3rem;
color: @gray-normal;
.details-header-actions { .details-header-actions {
flex: 1 auto; flex: 1 auto;
text-align: left; text-align: left;
margin-top: -12px; position: relative;
.action { .action {
display: inline-block; display: inline-block;
position: relative; width: 54px;
img { padding-top: 0.5rem;
width: 35px; height: 60px;
height: 35px; .box-button();
} &:hover {
&.disabled { background-color: transparent;
opacity: 0.3; color: @gray-darkest;
&:active {
img, .btn-label {
-webkit-filter: brightness(1);
}
}
} }
&:active { &:active {
img, .btn-label { color: @brand-action;
-webkit-filter: brightness(0.8); box-shadow: none;
} background-color: transparent;
}
//border-right: 1px solid @color-divider;
&.disabled {
background-color: white;
color: fade(@gray-light, 20%);
} }
.action-icon { .action-icon {
color: @gray-normal; text-align: center;
margin-right: 15px; //height: 44px;
.icon {
font-size: 30px;
margin-left: 0.6rem;
}
} }
.btn-label { .btn-label {
position: absolute; text-align: center;
color: @brand-action;
font-size: 9px; font-size: 9px;
width: 200px; position: relative;
top: 38px; top: -0.5rem;
&.view {
left: 8px;
}
&.restart {
left: 3px;
}
&.stop {
left: 8px;
}
&.start {
left: 8px;
}
&.terminal {
left: 1px;
}
visibility: hidden;
} }
} }
} }
.details-subheader-tabs { .details-subheader-tabs {
flex: 1 auto; display: flex;
text-align: right; justify-content: flex-end;
margin-right: 3px; align-items: flex-end;
margin-top: 3px;
}
}
.details-header {
flex: 0 auto;
display: flex;
flex-direction: row;
padding: 18px 24px 24px 24px;
position: relative;
background-color: white;
h1 {
margin: 0;
font-size: 20px;
font-weight: 400;
margin: 0;
color: @gray-darkest;
}
.status {
font-size: 10px;
font-weight: 500;
position: relative; position: relative;
top: 8px; right: -1px;
left: 14px; top: 1px;
&.running { flex: 1 0;
color: @brand-positive; text-align: right;
} .details-tab {
&.paused { transition: background-color 140ms;
color: @gray-lighter; font-size: 14px;
} font-weight: 500;
&.stopped { padding: 1.5rem 2rem;
color: @gray-lighter; border-top: 1px solid @color-divider;
} border-left: 1px solid @color-divider;
&.downloading { border-bottom: 1px solid transparent;
color: @brand-action; color: @gray-light;
&:hover {
background-color: @color-box-button;
}
&:last-child {
border-right: 1px solid @color-divider;
}
//border-radius: @border-radius;
&.active {
background-color: @color-background;
border-bottom: 1px solid @color-background;
border-top: 3px solid @brand-primary;
color: @gray-darker;
}
&:active {
box-shadow: inset 0 0 1px @color-divider;
background-color: darken(@color-box-button, 1%);
}
&.disabled {
color: @gray-lightest;
&:hover {
background-color: white;
}
&:active {
background-color: white;
}
&.active {
&:hover {
background-color: @color-background;
}
}
}
} }
} }
} }
.tab { .tab {
margin-left: 16px; font-size: 12px;
padding: 6px 10px;
font-weight: 400; font-weight: 400;
display: inline-block; display: inline-block;
margin: 0 0.6rem;
transition: all 0.3s;
color: @gray-light;
border-bottom: 3px solid transparent;
text-align: center;
min-width: 20px;
padding-bottom: 1rem;
&.active { &.active {
border-radius: 40px; font-weight: 500;
color: white; color: @gray-darkest;
.brand-gradient(); border-bottom: 3px solid @brand-primary;
} }
&.disabled { &.disabled {
opacity: 0.5; opacity: 0.5;
&:hover { &:hover {
border-radius: 40px; color: @gray-light;
background-color: transparent;
} }
} }
&:hover { &:hover {
border-radius: 40px; color: @gray-darkest;
background-color: @gray-lightest;
} }
} }
@ -148,8 +186,7 @@
} }
} }
.details-panel { .details-panel {
flex: 1; flex: 1 auto;
overflow: auto; overflow: auto;
background-color: white;
} }
} }

View File

@ -10,13 +10,15 @@
flex-direction: row; flex-direction: row;
flex: 1 auto; flex: 1 auto;
padding: 2rem;
.image { .image {
display: flex; display: flex;
flex: 1 auto; flex: 1 auto;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
padding-right: 40px; padding-right: 10rem;
img { img {
width: 399px; width: 399px;
@ -38,8 +40,8 @@
flex: 1 auto; flex: 1 auto;
align-items: flex-end; align-items: flex-end;
justify-content: center; justify-content: center;
padding-right: 60px; padding-right: 5rem;
padding-left: 80px; padding-left: 15rem;
flex-direction: column; flex-direction: column;
img { img {
@ -96,14 +98,10 @@
button[type="submit"] { button[type="submit"] {
flex: 0 auto; flex: 0 auto;
display: block; display: block;
.btn-filled-styles(@brand-action);
font-size: 18px; font-size: 18px;
padding: 10px 20px; padding: 10px 20px;
color: white;
background-color: @brand-action;
border: 0; border: 0;
&:hover {
background-color: darken(@brand-action, 5%);
}
} }
} }

View File

@ -6,7 +6,7 @@
@import "bootstrap/mixins.less"; @import "bootstrap/mixins.less";
h2 { h2 {
font-size: 18px; font-size: 16px;
color: @gray-normal; color: @gray-normal;
} }
@ -22,10 +22,14 @@ h4 {
} }
a { a {
transition: color 0.25s; color: @brand-action;
transition: color 150ms;
//cursor: pointer;
&:hover { &:hover {
text-decoration: none; text-decoration: none;
color: darken(@brand-action, 10%);
} }
outline: 0 !important;
} }
input[type="text"] { input[type="text"] {
@ -41,8 +45,8 @@ input[type="text"] {
border-bottom: 1px solid @brand-action; border-bottom: 1px solid @brand-action;
} }
&::-webkit-input-placeholder { &::-webkit-input-placeholder {
color: #DDD; color: @gray-lighter;
font-weight: 300; font-weight: 400;
} }
} }
} }
@ -62,34 +66,33 @@ input[type="text"] {
} }
// Mixin for generating new styles // Mixin for generating new styles
.btn-styles(@btn-color: @gray-normal) { .btn-hollow-styles(@btn-color: @gray-normal) {
transition: all 0.25s;
.reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners
border-color: @btn-color; transition: all 100ms;
color: @btn-color; background: transparent;
border: 1px solid @btn-color;
color: @btn-color;
font-weight: 500;
&:hover, &:hover {
&:focus { background-color: fade(@btn-color, 2%);
border-color: darken(@btn-color, 15%); border-color: darken(@btn-color, 5%);
color: darken(@btn-color, 15%); color: darken(@btn-color, 5%);
cursor: default; cursor: default;
box-shadow: none; box-shadow: none;
background: none;
} }
&:focus,
&.focus {
color: @btn-color;
outline: none;
}
&:active { &:active {
background-color: lighten(@btn-color, 45%); background-color: fade(@btn-color, 3%);
border-color: darken(@btn-color, 15%); border-color: darken(@btn-color, 5%);
color: darken(@btn-color, 15%); box-shadow: 0 0 15px fade(@btn-color, 25%);
box-shadow: none; }
}
&.active {
background-color: @btn-color;
color: white;
box-shadow: none;
box-shadow: none;
}
&:disabled, &:disabled,
&[disabled] { &[disabled] {
@ -104,6 +107,45 @@ input[type="text"] {
} }
} }
.btn-filled-styles(@btn-color: @gray-normal) {
.reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners
transition: all 100ms;
background: @btn-color;
border: none;
color: white;
font-weight: 500;
&:hover {
background-color: darken(@btn-color, 4%);
color: white;
cursor: default;
box-shadow: none;
}
&:focus,
&.focus {
color: white;
outline: none;
}
&:active {
background-color: darken(@btn-color, 5%);
box-shadow: 0 0 15px fade(@btn-color, 25%);
}
&:disabled,
&[disabled] {
opacity: 0.5;
background: @btn-color;
&.active {
background: @btn-color;
color: white;
box-shadow: none;
box-shadow: none;
}
}
}
.btn-group { .btn-group {
&.tabs { &.tabs {
.btn { .btn {
@ -125,16 +167,40 @@ input[type="text"] {
// Common styles // Common styles
.btn { .btn {
font-size: 13px;
background-color: transparent; background-color: transparent;
color: @gray-normal; color: @gray-normal;
border: 1px solid @gray-normal; border: 1px solid @gray-normal;
border-radius: 40px; border-radius: @border-radius;
box-shadow: none; box-shadow: none;
font-weight: 400;
text-shadow: none; text-shadow: none;
padding: 5px 14px 5px 14px;
cursor: default; cursor: default;
font-size: 12px;
padding: 0.5rem 1rem;
text-transform: uppercase;
&.has-icon {
.icon {
font-size: 0.9rem;
margin-right: 0.4rem;
&::before {
-webkit-font-smoothing: subpixel-antialiased;
}
}
}
&.circular {
border-radius: 100%;
width: 25px;
height: 25px;
padding: 0;
padding-top: 4px;
.icon {
font-size: 10px;
&::before {
-webkit-font-smoothing: subpixel-antialiased;
}
}
}
&.small { &.small {
font-size: 11px; font-size: 11px;
@ -180,36 +246,35 @@ input[type="text"] {
&.only-icon { &.only-icon {
padding: 6px 7px 6px 7px; padding: 6px 7px 6px 7px;
border-radius: 100%;
&.small { &.small {
width: 22px; width: 22px;
padding: 4px 5px 4px 5px; padding: 3px 5px 4px 5px;
.icon {
&::before {
-webkit-font-smoothing: subpixel-antialiased;
}
}
} }
} }
} }
// Apply the mixin to the buttons // Apply the mixin to the buttons
.btn-action { .btn-action {
.btn-styles(@brand-action); .btn-hollow-styles(@brand-action);
&.btn-hollow {
.btn-hollow-styles(@brand-action);
}
} }
.btn-positive { .btn-positive {
.btn-styles(@brand-positive); .btn-hollow-styles(@brand-positive);
&:hover,
&:focus {
border-color: darken(@brand-positive, 7%);
color: darken(@brand-positive, 7%);
}
&:active {
background-color: lighten(@brand-positive, 53%);
border-color: darken(@brand-positive, 7%);
color: darken(@brand-positive, 7%);
}
} }
.btn-default { .btn-styles(@btn-default-bg); } .btn-default { .btn-filled-styles(@btn-default-bg); }
.btn-primary { .btn-styles(@btn-primary-bg); } .btn-primary { .btn-filled-styles(@btn-primary-bg); }
.btn-success { .btn-styles(@btn-success-bg); } .btn-success { .btn-filled-styles(@btn-success-bg); }
.btn-info { .btn-styles(@btn-info-bg); } .btn-info { .btn-filled-styles(@btn-info-bg); }
.btn-warning { .btn-styles(@btn-warning-bg); } .btn-warning { .btn-filled-styles(@btn-warning-bg); }
.btn-danger { .btn-styles(@btn-danger-bg); } .btn-danger { .btn-filled-styles(@btn-danger-bg); }
.tooltip { .tooltip {
.tooltip-inner { .tooltip-inner {

View File

@ -1,5 +1,5 @@
@brand-primary: #24B8EB; @brand-primary: #22b8eb;
@brand-action: #24B8EB; @brand-action: @brand-primary;
@brand-positive: #15CC35; @brand-positive: #15CC35;
@brand-negative: #FF5F52; @brand-negative: #FF5F52;
@ -13,14 +13,24 @@
@traffic-light-gray-border: #D3D3D3; @traffic-light-gray-border: #D3D3D3;
@gray-darkest: #233137; @gray-darkest: #233137;
@gray-darker: #556473; @gray-darker: #3f5167;
@gray-normal: #7A8491; @gray-normal: #556473;
@gray-light: #9AA7BB; @gray-light: #7a8491;
@gray-lighter: #C4CDDA; @gray-lighter: #c4cdda;
@gray-lightest: #E1E8EF; @gray-lightest: #e6edf4;
@color-divider: @gray-lightest; @color-divider: @gray-lightest;
@color-background: #FCFCFC; @color-box-button: lighten(@gray-lightest, 5%);
@color-background: lighten(@gray-lightest, 4.5%);
@font-regular: "Helvetica Neue", Segoe UI, Arial, "Lucida Grande", sans-serif; @font-regular: "Helvetica Neue", Segoe UI, Arial, "Lucida Grande", sans-serif;
@font-code: Menlo, Consolas; @font-code: Menlo, Consolas;
@border-radius: 0.2rem;
@sidebar-width: 220px;
@sidebar-text-overflow-width: 140px;
@container-state-size: 20px;
@image-card-width: 260px;
@image-card-height: 130px;