diff options
339 files changed, 4392 insertions, 2893 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c26e7f0aeba..edb1069e54f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.14-chrome-63.0-node-8.x-yarn-1.2-postgresql-9.6" +image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.6-golang-1.9-git-2.14-chrome-63.0-node-8.x-yarn-1.2-postgresql-9.6" .dedicated-runner: &dedicated-runner retry: 1 diff --git a/.ruby-version b/.ruby-version index cc6c9a491e0..e75da3e63d6 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.3.5 +2.3.6 diff --git a/CHANGELOG.md b/CHANGELOG.md index fd7825b5f82..a9539bbed87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,155 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 10.3.0 (2017-12-22) + +### Security (1 change, 1 of them is from the community) + +- Upgrade jQuery to 2.2.4. !15570 (Takuya Noguchi) + +### Fixed (55 changes, 8 of them are from the community) + +- Fail jobs if its dependency is missing. !14009 +- Fix errors when selecting numeric-only labels in the labels autocomplete selector. !14607 (haseebeqx) +- Fix pipeline status transition for single manual job. This would also fix pipeline duration becuse it is depending on status transition. !15251 +- Fix acceptance of username for Mattermost service update. !15275 +- Set the default gitlab-shell timeout to 3 hours. !15292 +- Make sure a user can add projects to subgroups they have access to. !15294 +- OAuth identity lookups case-insensitive. !15312 +- Fix filter by my reaction is not working. !15345 (Hiroyuki Sato) +- Avoid deactivation when pipeline schedules execute a branch includes `[ci skip]` comment. !15405 +- Add recaptcha modal to issue updates detected as spam. !15408 +- Fix item name and namespace text overflow in Projects dropdown. !15451 +- Removed unused rake task, 'rake gitlab:sidekiq:drop_post_receive'. !15493 +- Fix commits page throwing 500 when the multi-file editor was enabled. !15502 +- Fix Issue comment submit button being disabled when pasting content from another GFM note. !15530 +- Reenable Prometheus metrics, add more control over Prometheus method instrumentation. !15558 +- Fix broadcast message not showing up on login page. !15578 +- Initializes the branches dropdown when the 'Start new pipeline' failed due to validation errors. !15588 (Christiaan Van den Poel) +- Fix merge requests where the source or target branch name matches a tag name. !15591 +- Create a fork network for forks with a deleted source. !15595 +- Fix search results when a filename would contain a special character. !15606 (haseebeqx) +- Strip leading & trailing whitespaces in CI/CD secret variable keys. !15615 +- Correctly link to a forked project from the new fork page. !15653 +- Fix the fork project functionality for projects with hashed storage. !15671 +- Added default order to UsersFinder. !15679 +- Fix graph notes number duplication. !15696 (Vladislav Kaverin) +- Fix updateEndpoint undefined error for issue_show app root. !15698 +- Change boards page boards_data absolute urls to paths. !15703 +- Using appropiate services in the API for managing forks. !15709 +- Confirming email with invalid token should no longer generate an error. !15726 +- fix #39233 - 500 in merge request. !15774 (Martin Nowak) +- Use Markdown styling for new project guidelines. !15785 (Markus Koller) +- Fix error during schema dump. !15866 +- Fix broken illustration images for monitoring page empty states. !15889 +- Make sure user email is read only when synced with LDAP. !15915 +- Fixed outdated browser flash positioning. +- Fix gitlab:import:repos Rake task moving repositories into the wrong location. +- Gracefully handle case when repository's root ref does not exist. +- Fix GitHub importer using removed interface. +- Align retry button with job title with new grid size. +- Fixed admin welcome screen new group path. +- Fix related branches/Merge requests failing to load when the hostname setting is changed. +- Init zen mode in snippets pages. +- Remove extra margin from wordmark in header. +- Fixed long commit links not wrapping correctly. +- Fixed deploy keys remove button loading state not resetting. +- Use app host instead of asset host when rendering image blob or diff. +- Hide log size for mobile screens. +- Fix sending notification emails to users with the mention level set who were mentioned in an issue or merge request description. +- Changed validation error message on wrong milestone dates. (Xurxo Méndez Pérez) +- Fix access to the final page of todos. +- Fixed new group milestone breadcrumbs. +- Fix image diff notification email from showing wrong content. +- Fixed merge request lock icon size. +- Make sure head pippeline always corresponds with the head sha of an MR. +- Prevent 500 error when inspecting job after trigger was removed. + +### Changed (14 changes, 2 of them are from the community) + +- Only owner or master can erase jobs. !15216 +- Allow password authentication to be disabled entirely. !15223 (Markus Koller) +- Add the option to automatically run a pipeline after updating AutoDevOps settings. !15380 +- Add total_time_spent to the `changes` hash in issuable Webhook payloads. !15381 +- Monitor NFS shards for circuitbreaker in a separate process. !15426 +- Add inline editing to issues on mobile. !15438 +- Add custom brand text on new project pages. !15541 (Markus Koller) +- Show only group name by default and put full namespace in tooltip in Groups tree. !15650 +- Use custom user agent header in all GCP API requests. !15705 +- Changed the deploy markers on the prometheus dashboard to be more verbose. !38032 +- Animate contextual sidebar on collapse/expand. +- Update emojis. Add :gay_pride_flag: and :speech_left:. Remove extraneous comma in :cartwheel_tone4:. +- When a custom header logo is present, don't show GitLab type logo. +- Improved diff changed files dropdown design. + +### Performance (19 changes) + +- Add timeouts for Gitaly calls. !15047 +- Performance issues when loading large number of wiki pages. !15276 +- Add performance logging to UpdateMergeRequestsWorker. !15360 +- Keep track of all circuitbreaker keys in a set. !15613 +- Improve the performance for counting commits. !15628 +- Reduce requests for project forks on show page of projects that have forks. !15663 +- Perform SQL matching of Build&Runner tags to greatly speed-up job picking. +- Only load branch names for protected branch checks. +- Optimize API /groups/:id/projects by preloading associations. +- Remove allocation tracking code from InfluxDB sampler for performance. +- Throttle the number of UPDATEs triggered by touch. +- Make finding most recent merge request diffs more efficient. +- Fetch blobs in bulk when generating diffs. +- Cache commits for MergeRequest diffs. +- Use fuzzy search with minimum length of 3 characters where appropriate. +- Add axios to common file. +- Remove template selector from global namespace. +- check the import_status field before doing SQL operations to check the import url. +- Stop sending milestone and labels data over the wire for MR widget requests. + +### Added (22 changes, 15 of them are from the community) + +- Limit autocomplete menu to applied labels. !11110 (Vitaliy @blackst0ne Klachkov) +- Make diff notes created on a commit in a merge request to persist a rebase. !12148 +- Allow creation of merge request from email. !13817 (janp) +- Add an ability to use a custom branch name on creation from issues. !13884 (Vitaliy @blackst0ne Klachkov) +- Add anonymous rate limit per IP, and authenticated (web or API) rate limits per user. !14708 +- Create a new form to add Existing Kubernetes Cluster. !14805 +- Add support of Mermaid (generation of diagrams and flowcharts from text). !15107 (Vitaliy @blackst0ne Klachkov) +- Add total time spent to milestones. !15116 (George Andrinopoulos) +- Add /groups/:id/subgroups endpoint to API. !15142 (marbemac) +- Add administrative endpoint to list all pages domains. !15160 (Travis Miller) +- Adds Rubocop rule for line break after guard clause. !15188 (Jacopo Beschi @jacopo-beschi) +- Add edit button to mobile file view. !15199 (Travis Miller) +- Add dropdown sort to group milestones. !15230 (George Andrinopoulos) +- added support for ordering and sorting in notes api. !15342 (haseebeqx) +- Hashed Storage migration script now supports migrating project attachments. !15352 +- New API endpoint - list jobs for a specified runner. !15432 +- Add new API endpoint - get a namespace by ID. !15442 +- Disables autocomplete in filtered searc. !15477 (Jacopo Beschi @jacopo-beschi) +- Update empty state page of merge request 'changes' tab. !15611 (Vitaliy @blackst0ne Klachkov) +- Allow git pull/push on group/user/project redirects. !15670 +- show status of gitlab reference links in wiki. !15694 (haseebeqx) +- Add email confirmation parameters for user creation and update via API. (Daniel Juarez) + +### Other (17 changes, 7 of them are from the community) + +- Enable UnnecessaryMantissa in scss-lint. !15255 (Takuya Noguchi) +- Add untracked files to uploads table. !15270 +- Move update_project_counter_caches? out of issue and merge request. !15300 (George Andrinopoulos) +- Removed tooltip from clone dropdown. !15334 +- Clean up empty fork networks. !15373 +- Create issuable destroy service. !15604 (George Andrinopoulos) +- Upgrade seed-fu to 2.3.7. !15607 (Takuya Noguchi) +- Rename GKE as Kubernetes Engine. !15608 (Takuya Noguchi) +- Prefer ci_config_path validation for leading slashes instead of sanitizing the input. !15672 (Christiaan Van den Poel) +- Fix typo in docs about Elasticsearch. !15699 (Takuya Noguchi) +- Add internationalization support for the prometheus integration. !33338 +- Export text utils functions as es6 module and add tests. +- Stop reloading the page when using pagination and tabs - use API calls - in Pipelines table. +- Clean up schema of the "issues" table. +- Clarify wording of protected branch settings for the default branch. +- Update svg external depencency. +- Clean up schema of the "merge_requests" table. + + ## 10.2.5 (2017-12-15) ### Fixed (8 changes) @@ -1 +1 @@ -10.3.0-pre +10.4.0-pre diff --git a/app/assets/images/icons.json b/app/assets/images/icons.json index 68d6528758b..38c1faccbf1 100644 --- a/app/assets/images/icons.json +++ b/app/assets/images/icons.json @@ -1 +1 @@ -{"iconCount":181,"spriteSize":81482,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","bold","book","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","image-comment-light","import","issue-block","issue-child","issue-close","issue-duplicate","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","spinner","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","user","users","volume-up","warning","work"]}
\ No newline at end of file +{"iconCount":186,"spriteSize":84748,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","bold","book","bookmark","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder-o","folder-open","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","image-comment-light","import","issue-block","issue-child","issue-close","issue-duplicate","issue-external","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","podcast","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","spinner","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","user","users","volume-up","warning","work"]}
\ No newline at end of file diff --git a/app/assets/images/icons.svg b/app/assets/images/icons.svg index fd8f7862911..42f5377a10e 100644 --- a/app/assets/images/icons.svg +++ b/app/assets/images/icons.svg @@ -1 +1 @@ -<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 16 16" id="abuse" xmlns="http://www.w3.org/2000/svg"><path d="M11.408.328l4.029 3.222A1.5 1.5 0 0 1 16 4.72v6.555a1.5 1.5 0 0 1-.563 1.171l-4.026 3.224a1.5 1.5 0 0 1-.937.329H5.529a1.5 1.5 0 0 1-.937-.328L.563 12.45A1.5 1.5 0 0 1 0 11.28V4.724a1.5 1.5 0 0 1 .563-1.171L4.589.329A1.5 1.5 0 0 1 5.526 0h4.945c.34 0 .67.116.937.328zM10.296 2H5.702L2 4.964v6.074L5.704 14h4.594L14 11.036V4.962L10.296 2zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="account" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.195 9.965l-.568-.875a.25.25 0 0 1 .015-.294l.405-.5a.25.25 0 0 1 .283-.075l.938.36c.257-.183.543-.325.851-.42l.322-.988A.25.25 0 0 1 11.679 7h.642a.25.25 0 0 1 .238.173l.322.988c.308.095.594.237.851.42l.938-.36a.25.25 0 0 1 .283.076l.405.5a.25.25 0 0 1 .015.293l-.568.875c.113.297.18.616.193.95l.898.54a.25.25 0 0 1 .115.27l-.144.626a.25.25 0 0 1-.222.193l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.281a.25.25 0 0 1-.29-.05l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.904a.25.25 0 0 1-.289.051l-.577-.281a.25.25 0 0 1-.138-.26l.165-1.18a3.015 3.015 0 0 1-.512-.607l-1.115-.098a.25.25 0 0 1-.222-.193l-.144-.626a.25.25 0 0 1 .115-.27l.898-.54c.013-.334.08-.653.193-.95zM6.789 8.023A12.845 12.845 0 0 0 6 8c-5.036 0-6 2.74-6 4.48C0 14.22.076 15 6 15c.553 0 1.055-.006 1.51-.02A5.977 5.977 0 0 1 6 11c0-1.083.287-2.1.79-2.977zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM12 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="admin" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.162 2.5a3.5 3.5 0 0 1-3.163 5.479L6.08 14.766a1.5 1.5 0 0 1-2.598-1.5L7.4 6.479A3.5 3.5 0 0 1 10.564 1L8.9 3.88l2.599 1.5 1.663-2.88zm-8.63 11.949a.5.5 0 1 0 .5-.866.5.5 0 0 0-.5.866z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.414 7.95l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 1 0 1.414-1.415L10.414 7.95zm-7 0l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 0 0 1.414-1.415L3.414 7.95z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.536 7.95L1.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 1 1-1.414-1.415L5.536 7.95zm7 0L8.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.414-1.415l4.243-4.242z"/></symbol><symbol viewBox="0 0 16 16" id="angle-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 10.243l-4.95-4.95a1 1 0 0 0-1.414 1.414l5.657 5.657a.997.997 0 0 0 1.414 0l5.657-5.657a1 1 0 0 0-1.414-1.414L8 10.243z"/></symbol><symbol viewBox="0 0 16 16" id="angle-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.757 8l4.95-4.95a1 1 0 1 0-1.414-1.414L3.636 7.293a.997.997 0 0 0 0 1.414l5.657 5.657a1 1 0 0 0 1.414-1.414L5.757 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.243 8l-4.95-4.95a1 1 0 0 1 1.414-1.414l5.657 5.657a.997.997 0 0 1 0 1.414l-5.657 5.657a1 1 0 0 1-1.414-1.414L10.243 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 6.757l-4.95 4.95a1 1 0 1 1-1.414-1.414l5.657-5.657a.997.997 0 0 1 1.414 0l5.657 5.657a1 1 0 0 1-1.414 1.414L8 6.757z"/></symbol><symbol viewBox="0 0 16 16" id="appearance" xmlns="http://www.w3.org/2000/svg"><path d="M11.161 12.456l.232.121c.1.053.175.094.249.137.53.318.844.75.857 1.402.012 1.397-1.116 1.756-3.12 1.858a23.85 23.85 0 0 1-1.38.026A8 8 0 0 1 0 8a8 8 0 0 1 8-8c4.417 0 7.998 3.582 7.998 7.977.06 2.621-1.312 3.586-4.48 3.648-.602.008-1.068.043-1.4.104.228.192.598.47 1.043.727zm-3.287-.943c-.019-1.495 1.228-1.856 3.611-1.888C13.67 9.582 14.028 9.33 13.998 8A6 6 0 1 0 8 14c.603 0 .91-.004 1.277-.023a9.7 9.7 0 0 0 .478-.035c-1.172-.738-1.868-1.47-1.88-2.43zM6 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-2-3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM4 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="applications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 1v2h2V1H7zm0 5h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm0 1v2h2V7h-2zM1 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm0 1v2h2v-2H1zm6-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm6 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="approval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.536 10.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 1 1 9.12 9.243l1.415 1.414zM7.632 8.109A2 2 0 0 0 7 11.364l2.121 2.121a1.996 1.996 0 0 0 2.807.021C11.686 14.554 10.627 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.6 0 1.142.038 1.632.109zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-down" xmlns="http://www.w3.org/2000/svg"><path d="M10.472 7.282a.862.862 0 0 1 1.26-.006c.357.364.357.958 0 1.285L8.627 11.73A.886.886 0 0 1 8 12a.849.849 0 0 1-.627-.27L4.275 8.561a.904.904 0 0 1-.013-1.285.861.861 0 0 1 1.26-.007l2.486 2.527z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 6H2a2 2 0 1 0 0 4h7v2.586a1 1 0 0 0 1.707.707l4.586-4.586a1 1 0 0 0 0-1.414l-4.586-4.586A1 1 0 0 0 9 3.414V6z"/></symbol><symbol viewBox="0 0 16 16" id="assignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 5V4a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V7h-1a1 1 0 0 1 0-2h1zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="bold" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4 12.5v-9A1.5 1.5 0 0 1 5.5 2h2.104c2.182 0 3.879.681 3.879 2.982 0 1.067-.517 2.227-1.374 2.595v.073C11.176 7.963 12 8.865 12 10.466 12 12.914 10.19 14 7.911 14H5.5A1.5 1.5 0 0 1 4 12.5zm2.376-5.696H7.49c1.164 0 1.665-.552 1.665-1.417 0-.94-.534-1.289-1.649-1.289h-1.13v2.706zm0 5.098h1.341c1.293 0 1.956-.515 1.956-1.62 0-1.049-.647-1.472-1.956-1.472H6.376v3.092z"/></symbol><symbol viewBox="0 0 16 16" id="book" xmlns="http://www.w3.org/2000/svg"><path d="M7 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2v4.191a.5.5 0 0 1-.724.447l-1.052-.526a.5.5 0 0 0-.448 0l-1.052.526A.5.5 0 0 1 7 6.191V2zM5 0h6a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="branch" xmlns="http://www.w3.org/2000/svg"><path d="M6 11.978v.29a2 2 0 1 1-2 0V3.732a2 2 0 1 1 2 0v3.849c.592-.491 1.31-.854 2.15-1.081 1.308-.353 1.875-.882 1.893-1.743a2 2 0 1 1 2.002-.051C12.053 6.54 10.857 7.84 8.67 8.43 7.056 8.867 6.195 9.98 6 11.978zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 15a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="bullhorn" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.143 10H7V4H3a3 3 0 1 0 0 6h.143l.734 5.141a1 1 0 0 0 .99.859h1.556a.5.5 0 0 0 .495-.57L6.143 10zM8 4c1.034.02 2.039-.274 3.014-.883.727-.455 1.836-1.334 3.328-2.637A1 1 0 0 1 16 1.233v10.764a1 1 0 0 1-1.595.803c-1.658-1.227-2.788-1.992-3.392-2.294-.781-.39-1.785-.559-3.013-.506V4z"/></symbol><symbol viewBox="0 0 16 16" id="calendar" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 2h2a2 2 0 0 1 2 2H0a2 2 0 0 1 2-2h2V1a1 1 0 1 1 2 0v1h4V1a1 1 0 1 1 2 0v1zM0 4h16v9a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4zm2 2.5V13a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6.5a.5.5 0 0 0-.5-.5h-11a.5.5 0 0 0-.5.5zM5 8h2a1 1 0 1 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="cancel" xmlns="http://www.w3.org/2000/svg"><path d="M3.11 4.523a6 6 0 0 0 8.367 8.367L3.109 4.524zM4.522 3.11l8.368 8.368A6 6 0 0 0 4.524 3.11zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 16 16" id="chart" xmlns="http://www.w3.org/2000/svg"><path d="M15 14a1 1 0 0 1 0 2H2a2 2 0 0 1-2-2V1a1 1 0 1 1 2 0v13h13zM3.142 8.735l2.502-2.561a.5.5 0 0 1 .714-.003L8 7.833l3.592-4.553a.5.5 0 0 1 .796.015l2.516 3.454a.5.5 0 0 1 .096.295V12.5a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5V9.085a.5.5 0 0 1 .142-.35z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.078 8.2l3.535-3.536a2 2 0 0 1 2.828 2.828l-4.949 4.95c-.39.39-.902.586-1.414.586a1.994 1.994 0 0 1-1.414-.586l-4.95-4.95a2 2 0 1 1 2.828-2.828l3.536 3.535z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.977 7.998l3.535-3.535a2 2 0 1 0-2.828-2.828l-4.95 4.949c-.39.39-.586.902-.586 1.414 0 .512.196 1.024.586 1.414l4.95 4.95a2 2 0 1 0 2.828-2.828L7.977 7.998z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.22 7.998L4.683 4.463a2 2 0 0 1 2.828-2.828l4.95 4.949c.39.39.586.902.586 1.414a1.99 1.99 0 0 1-.586 1.414l-4.95 4.95a2 2 0 0 1-2.828-2.828l3.535-3.536z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.778 8.957l3.535 3.535a2 2 0 1 0 2.828-2.828l-4.949-4.95a1.994 1.994 0 0 0-1.414-.586c-.512 0-1.024.196-1.414.586l-4.95 4.95a2 2 0 1 0 2.828 2.828l3.536-3.535z"/></symbol><symbol viewBox="0 0 16 16" id="clock" xmlns="http://www.w3.org/2000/svg"><path d="M9 7h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V5a1 1 0 1 1 2 0v2zm-1 9A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.414 8l4.95-4.95a1 1 0 0 0-1.414-1.414L8 6.586l-4.95-4.95A1 1 0 0 0 1.636 3.05L6.586 8l-4.95 4.95a1 1 0 1 0 1.414 1.414L8 9.414l4.95 4.95a1 1 0 1 0 1.414-1.414L9.414 8z"/></symbol><symbol viewBox="0 0 16 16" id="code" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.871 8.243a.997.997 0 0 0-.293-.707L12.75 4.707a1 1 0 0 0-1.414 1.414l2.12 2.122-2.12 2.121a1 1 0 0 0 1.414 1.414l2.828-2.828a.997.997 0 0 0 .293-.707zm-13.243 0L4.75 6.12a1 1 0 1 0-1.414-1.414L.507 7.536a.997.997 0 0 0 0 1.414l2.829 2.828a1 1 0 1 0 1.414-1.414L2.628 8.243zm6.407-4.107a1 1 0 0 1 .707 1.225L8.19 11.157a1 1 0 1 1-1.931-.518L7.81 4.843a1 1 0 0 1 1.224-.707z"/></symbol><symbol viewBox="0 0 9 13" id="collapse"><path d="M.084.25C.01.18-.015.12.008.071.031.024.093 0 .194 0h8.521c.1 0 .162.024.185.072.023.048-.002.107-.075.177l-4.11 3.935a.372.372 0 0 1-.11.072h-.301a.508.508 0 0 1-.11-.072L.084.249zM.377 6.88a.364.364 0 0 1-.26-.105.334.334 0 0 1-.11-.25v-.709c0-.096.036-.179.11-.249a.364.364 0 0 1 .26-.105h8.15c.101 0 .188.035.261.105.074.07.11.153.11.25v.709c0 .096-.036.179-.11.249a.364.364 0 0 1-.26.105H.377zM.084 12.132c-.074.07-.099.129-.076.177.023.048.085.072.186.072h8.521c.1 0 .162-.024.185-.072.023-.048-.002-.107-.075-.177l-4.11-3.935a.372.372 0 0 0-.11-.072h-.301a.508.508 0 0 0-.11.072l-4.11 3.935z"/></symbol><symbol viewBox="0 0 16 16" id="comment" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comment-dots" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586zM5 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="comment-next" xmlns="http://www.w3.org/2000/svg"><path d="M8 5V4a.5.5 0 0 1 .8-.4l2.667 2a.5.5 0 0 1 0 .8L8.8 8.4A.5.5 0 0 1 8 8V7H6a1 1 0 1 1 0-2h2zM1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comments" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.75 10L0 13V3a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3.75zM13 5h1a2 2 0 0 1 2 2v8l-2.667-2H8a2 2 0 0 1-2-2h4a3 3 0 0 0 3-3V5z"/></symbol><symbol viewBox="0 0 16 16" id="commit" xmlns="http://www.w3.org/2000/svg"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3.876-1.008a4.002 4.002 0 0 1-7.752 0A1.01 1.01 0 0 1 4 9H1a1 1 0 1 1 0-2h3c.042 0 .083.003.124.008a4.002 4.002 0 0 1 7.752 0A1.01 1.01 0 0 1 12 7h3a1 1 0 0 1 0 2h-3a1.01 1.01 0 0 1-.124-.008z"/></symbol><symbol viewBox="0 0 16 16" id="credit-card" xmlns="http://www.w3.org/2000/svg"><path d="M14 5a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1h12zm0 3H2v3a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V8zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm6.5 8h3a.5.5 0 1 1 0 1h-3a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="cut" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="2" y="7" fill-rule="evenodd" rx="1"/></symbol><symbol viewBox="0 0 16 16" id="dashboard" xmlns="http://www.w3.org/2000/svg"><path d="M7.709 10.021l.696-2.6a.5.5 0 0 1 .966.26l-.657 2.45A2 2 0 0 1 10 12H6a2 2 0 0 1 1.709-1.979zM0 8.9a8 8 0 0 1 15.998 0H16v3.6a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5V8.9zM14 9A6 6 0 1 0 2 9v3.5a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 .5-.5V9zM3.5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm9 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-7-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm5 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1z"/></symbol><symbol viewBox="0 0 16 16" id="disk" xmlns="http://www.w3.org/2000/svg"><path d="M16 11.764V3a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v8.764A2.989 2.989 0 0 1 2 11V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v8c.768 0 1.47.289 2 .764zM2 12h12a2 2 0 1 1 0 4H2a2 2 0 1 1 0-4zm10 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="doc_code" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zm1.036 7.607a.498.498 0 0 1-.147.354l-1.414 1.414a.5.5 0 0 1-.707-.707l1.06-1.06-1.06-1.061a.5.5 0 0 1 .707-.707l1.414 1.414a.498.498 0 0 1 .147.353zm-4.822 0l1.06 1.061a.5.5 0 0 1-.706.707l-1.414-1.414a.498.498 0 0 1 0-.707l1.414-1.414a.5.5 0 1 1 .707.707l-1.06 1.06zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="doc_image" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM7.333 9.667l1.313-1.313a.5.5 0 0 1 .708 0L12 11H4l2.188-1.75a.5.5 0 0 1 .624 0l.521.417zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 8a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4 11h8v.7a.3.3 0 0 1-.3.3H4.3a.3.3 0 0 1-.3-.3V11z"/></symbol><symbol viewBox="0 0 16 16" id="doc_text" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 11h5a.5.5 0 1 1 0 1h-5a.5.5 0 1 1 0-1zm0-2h5a.5.5 0 1 1 0 1h-5a.5.5 0 0 1 0-1zm0-2h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/></symbol><symbol viewBox="0 0 105 26" id="double-headed-arrow" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.018 11.089L15.138.614c1.23-.911 3.086-.795 4.147.26.461.46.715 1.045.715 1.651v20.95C20 24.869 18.684 26 17.06 26a3.238 3.238 0 0 1-1.921-.614L1.019 14.911C-.212 14-.347 12.405.714 11.35c.094-.094.195-.18.303-.261zm102.964 0c.108.08.21.167.303.26 1.061 1.056.925 2.65-.303 3.562l-14.12 10.475A3.238 3.238 0 0 1 87.94 26C86.316 26 85 24.87 85 23.475V2.525c0-.606.254-1.192.715-1.65 1.061-1.056 2.917-1.172 4.146-.26l14.12 10.474zM35 17a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="download" xmlns="http://www.w3.org/2000/svg"><path d="M9 12h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0l-2-2.667A.5.5 0 0 1 6 12h1V8a1 1 0 1 1 2 0v4zM4 9a1 1 0 1 1 0 2 4 4 0 0 1-1.971-7.481 4 4 0 0 1 6.633-2.505 3.999 3.999 0 0 1 3.82 2.014A4 4 0 0 1 12 11a1 1 0 0 1 0-2 2 2 0 1 0 0-4h-1a2 2 0 0 0-3.112-1.662A2 2 0 1 0 4.268 5H4a2 2 0 1 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M14 10h-3a1 1 0 0 1-1-1V6H8.527A.527.527 0 0 0 8 6.527V13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-3zm-4-7H8.527c-.18 0-.355.013-.527.04V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2v2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3zM8.527 4h2.323a.5.5 0 0 1 .35.143l4.65 4.551a.5.5 0 0 1 .15.357V13a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V6.527A2.527 2.527 0 0 1 8.527 4z"/></symbol><symbol viewBox="0 0 16 16" id="earth" xmlns="http://www.w3.org/2000/svg"><path d="M8.7 2.04l-.082.177c.283.223.422.413.417.571-.008.237-.311.057-.444.274-.133.218.038.542-.112.637-.15.096-.398-.386-.479-.46-.054-.049-.166-.257-.336-.625l-.216-.225a.844.844 0 0 0-.418-.035c-.177.038-.075.1-.035.132.04.032.32.037.452.2.132.164.03.224-.05.298-.054.05-.157.062-.31.035H5.952l-.402.398.03.325.229.455.324-.463c.008-.206.058-.342.15-.41.14-.1.342-.15.534-.085.191.066-.057.218.011.271.068.053.204-.098.313-.02.11.08.07.155.104.322.036.167.254.114.398.328.144.215.19.29.147.483-.043.195-.168.26-.305.232-.138-.028-.107-.246-.275-.348-.168-.102-.266-.114-.386-.054-.12.06-.016.129.023.235.04.106.274.321.224.43-.05.107-.108.116-.42 0-.21-.077-.414-.007-.615.212l-.76.722c-.153.715-.3 1.13-.44 1.243-.211.17-.177-.483-.483-.656-.306-.174-.494-.047-.8-.07-.307-.023-.42.65-.38.873a.434.434 0 0 0 .221.321c.236-.141.39-.184.465-.128.11.084-.144.267-.074.425.07.158.314.069.386.283.073.213.084.48-.05.706-.135.227-.275.178-.4.053-.127-.126-.033-.375-.255-.704-.223-.329-.381-.337-.63-.787-.158-.287-.35-.743-.575-1.366a6 6 0 0 0 3.21 7.198l.001-.075c0-.577-.004-.944-.012-1.102-.011-.236-.95-.945-1.104-1.2-.154-.256-.34-.595-.355-.746-.016-.151.185-.232.344-.325.16-.093-.11-.367.028-.626.137-.258.395-.438.496-.356.101.081.058.228.267.333.209.104.077-.213.456-.178.38.035.143.201.252.216.11.016.113-.127.299-.143.186-.015.282.445.471.622.19.178.452.008.611.043.159.034.267.09.402.255.136.166-.03.352.073.557.103.205 1.07.22 1.433.255.364.034.371.011.371.324s-.166.314-.453.507c-.286.193-.166.462-.38.762-.212.3-.316.062-.622.14-.306.077-.413.382-.452.568-.039.186-.386.094-.877.232-.29.082-.429.144-.569.204a6.002 6.002 0 0 0 7.682-4.3c-.094-.384-.18-.63-.258-.74-.213-.297-.36.21-.924.49-.564.278-.57-.288-.81-.49-.16-.133-.212-.44-.158-.92-.005-.478.02-.828.077-1.049.057-.221.126-.543.207-.965.351-.373.606-.572.764-.595.237-.034.336.374.658.3a.315.315 0 0 0 .035-.01 5.993 5.993 0 0 0-.475-.824l-.309-.043a.646.646 0 0 0-.332-.117c-.205-.02-.025.128-.089.24-.064.112-.235.724-.437.685-.201-.039-.204-.374-.17-.668.036-.294-.077-.35-.2-.412-.124-.062-.325-.213-.556-.295-.232-.082-.123-.175-.093-.274.03-.1.208-.015.193-.058-.014-.044-.313-.135-.266-.167.03-.02.2-.02.506.003l.216-.012.293-.163a.58.58 0 0 0-.376-.22c-.233-.036-.513-.034-.73-.142-.205-.103-.458-.36-.643-.638A5.965 5.965 0 0 0 8.7 2.04zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 1600 1600" id="ellipsis_v" xmlns="http://www.w3.org/2000/svg"><path d="M1088 1248v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V224q0-40 28-68t68-28h192q40 0 68 28t28 68z"/></symbol><symbol viewBox="0 0 18 18" id="emoji_slightly_smiling_face" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369.721.721 0 0 1 .568.047.715.715 0 0 1 .37.445 2.91 2.91 0 0 0 1.084 1.518A2.93 2.93 0 0 0 9 12.75a2.93 2.93 0 0 0 1.775-.58 2.913 2.913 0 0 0 1.084-1.518.711.711 0 0 1 .375-.445.737.737 0 0 1 .575-.047c.195.063.34.186.433.37.094.183.11.372.047.568zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smile" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568zM14 6.37c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm-6.5 0c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm9 2.63a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smiley" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568h.001zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6c.92.397 1.91.6 2.912.598a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39c.397-.92.6-1.91.598-2.912zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z"/></symbol><symbol viewBox="0 0 16 16" id="epic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.985 8.044l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637A2 2 0 0 0 1.618 9h11.661a2 2 0 0 0 1.706-.956zm0 3l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637a2 2 0 0 0 .576.084h11.661a2 2 0 0 0 1.706-.956zM3.618 2h10.995a1 1 0 0 1 .948 1.316l-1.333 4a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l2-4A1 1 0 0 1 3.618 2zm-.382 4h9.322l.667-2H4.236l-1 2z"/></symbol><symbol viewBox="0 0 16 16" id="external-link" xmlns="http://www.w3.org/2000/svg"><path d="M13.121 4.177l-4.95 4.95a1 1 0 1 1-1.414-1.414l4.95-4.95-1.386-1.386a.5.5 0 0 1 .299-.85l4.709-.524a.5.5 0 0 1 .552.552l-.523 4.71a.5.5 0 0 1-.851.297l-1.386-1.385zM12 8.884a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3v-8a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-4z"/></symbol><symbol viewBox="0 0 16 16" id="eye" xmlns="http://www.w3.org/2000/svg"><path d="M8 14C4.816 14 2.253 12.284.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2s5.747 1.716 7.607 5.019a2 2 0 0 1 0 1.962C13.747 12.284 11.184 14 8 14zm0-2c2.41 0 4.338-1.29 5.864-4C12.338 5.29 10.411 4 8 4 5.59 4 3.662 5.29 2.136 8 3.662 10.71 5.589 12 8 12zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm1-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="eye-slash" xmlns="http://www.w3.org/2000/svg"><path d="M13.618 2.62L1.62 14.619a1 1 0 0 1-.985-1.668l1.525-1.526C1.516 10.742.926 9.927.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2c1.074 0 2.076.195 3.006.58l.944-.944a1 1 0 0 1 1.668.985zM8.068 11a3 3 0 0 0 2.931-2.932l-2.931 2.931zm-3.02-2.462a3 3 0 0 1 3.49-3.49l.884-.884A6.044 6.044 0 0 0 8 4C5.59 4 3.662 5.29 2.136 8c.445.79.924 1.46 1.439 2.011l1.473-1.473zm.421 5.06l1.658-1.658c.283.04.575.06.873.06 2.41 0 4.338-1.29 5.864-4a11.023 11.023 0 0 0-1.133-1.664l1.418-1.418a12.799 12.799 0 0 1 1.458 2.1 2 2 0 0 1 0 1.963C13.747 12.284 11.184 14 8 14a7.883 7.883 0 0 1-2.53-.402z"/></symbol><symbol viewBox="0 0 16 16" id="file-addition" xmlns="http://www.w3.org/2000/svg"><path d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3z"/></symbol><symbol viewBox="0 0 16 16" id="file-deletion" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm2 6h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="file-modified" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm5 4a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/></symbol><symbol viewBox="0 0 16 16" id="filter" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 6v9l-3.724-1.862A.5.5 0 0 1 6 12.691V6L1.854 1.854A.5.5 0 0 1 2.207 1h11.586a.5.5 0 0 1 .353.854L10 6z"/></symbol><symbol viewBox="0 0 16 16" id="folder" xmlns="http://www.w3.org/2000/svg"><path d="M7.228 5l-.475-1.335A1 1 0 0 0 5.81 3H2v9a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H7.228zM13 3a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="fork" xmlns="http://www.w3.org/2000/svg"><path d="M9 12.268a2 2 0 1 1-2 0V8.874A4.002 4.002 0 0 1 4 5V3.732a2 2 0 1 1 2 0V5a2 2 0 1 0 4 0V3.732a2 2 0 1 1 2 0V5a4.002 4.002 0 0 1-3 3.874v3.394zM11 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm3 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="geo-nodes" xmlns="http://www.w3.org/2000/svg"><path d="M9.7 13.1l-.2.2c-.7.8-2 .9-2.8.1-.1 0-.1-.1-.1-.1l-.2-.2c-2 .2-3.4.7-3.4 1.4 0 .8 2.2 1.5 5 1.5s5-.7 5-1.5c0-.7-1.4-1.2-3.3-1.4M7.3 12.7c.4.4 1 .3 1.4-.1C11.6 9.5 13 7 13 5.3 13 2.4 10.8 0 8 0S3 2.4 3 5.3C3 7 4.4 9.5 7.3 12.7M8 2c1.6 0 3 1.4 3 3.3 0 1-1 2.8-3 5.2-2-2.4-3-4.2-3-5.2C5 3.4 6.4 2 8 2"/><circle cx="8" cy="5" r="1"/></symbol><symbol viewBox="0 0 16 16" id="git-merge" xmlns="http://www.w3.org/2000/svg"><path d="M11 12.268V5a1 1 0 0 0-1-1v1a.5.5 0 0 1-.8.4l-2.667-2a.5.5 0 0 1 0-.8L9.2.6a.5.5 0 0 1 .8.4v1a3 3 0 0 1 3 3v7.268a2 2 0 1 1-2 0zm-6 0a2 2 0 1 1-2 0V4.732a2 2 0 1 1 2 0v7.536zM4 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm8 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="group" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.048 11.997C-.377 11.975.013 11.782.013 10.56.013 9.235.653 8 4 8c.444 0 .84.022 1.194.062.164.435.426.82.76 1.132-1.786.389-2.721 1.353-2.906 2.803zm2.94-7.222a2.993 2.993 0 0 0-.976 1.95 2 2 0 1 1 .975-1.95zm6.964 7.222c-.185-1.45-1.12-2.414-2.906-2.803.334-.311.596-.697.76-1.132C11.16 8.022 11.556 8 12 8c3.346 0 3.987 1.235 3.987 2.56 0 1.222.39 1.415-3.035 1.437zm-1.964-5.272a2.993 2.993 0 0 0-.976-1.95 2 2 0 1 1 .976 1.95zM8 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 5c-2.177 0-3.987-.115-3.987-1.44S4.653 10 8 10c3.346 0 3.987 1.235 3.987 2.56S10.177 14 8 14z"/></symbol><symbol viewBox="0 0 16 16" id="history" xmlns="http://www.w3.org/2000/svg"><path d="M2.868 3.24a7 7 0 1 1-.043 9.475 1 1 0 0 1 1.478-1.348 5 5 0 1 0 .124-6.865l.796.645a.5.5 0 0 1-.193.873l-3.232.814a.5.5 0 0 1-.622-.504L1.3 3a.5.5 0 0 1 .814-.37l.754.61zM9 8h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V6a1 1 0 1 1 2 0v2z"/></symbol><symbol viewBox="0 0 16 16" id="home" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177a.505.505 0 0 1-.038.044l.038-.044zm-.787 0l.038.043a.5.5 0 0 1-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="hook" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1h4zm0 1H6v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V4zM7 8a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3v4a2 2 0 1 0 4 0h-.44a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H15a4 4 0 0 1-7 2.646A4 4 0 0 1 1 12H.56a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H3a2 2 0 1 0 4 0V8z"/></symbol><symbol viewBox="0 0 16 16" id="hourglass" xmlns="http://www.w3.org/2000/svg"><path d="M10.331 4.889A2.988 2.988 0 0 0 11 3V2H5v1c0 .362.064.709.182 1.03l5.15.859zM3 14v-1c0-1.78.93-3.342 2.33-4.228.447-.327.67-.582.67-.764 0-.19-.242-.46-.725-.815A4.996 4.996 0 0 1 3 3V2H2a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2h-1v1a4.997 4.997 0 0 1-2.39 4.266c-.407.3-.61.545-.61.734 0 .19.203.434.61.734A4.997 4.997 0 0 1 13 13v1h1a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2h1zm8 0v-1a3 3 0 0 0-6 0v1h6z"/></symbol><symbol viewBox="0 0 38 38" id="image-comment-dark" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#1F78D1"/><path fill="#FFF" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></symbol><symbol viewBox="0 0 38 38" id="image-comment-light" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#FFF"/><path fill="#1F78D1" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></symbol><symbol viewBox="0 0 16 16" id="import" xmlns="http://www.w3.org/2000/svg"><path d="M9 8h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0L5.6 8.8A.5.5 0 0 1 6 8h1V1a1 1 0 1 1 2 0v7zM0 8a1 1 0 1 1 2 0 6 6 0 1 0 12 0 1 1 0 0 1 2 0A8 8 0 1 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-block" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.803 8a5.97 5.97 0 0 0-.462 1H4.5a.5.5 0 0 1 0-1h1.303zM4.5 5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1zm7.5.083a6.04 6.04 0 0 0-2 0V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.083a5.96 5.96 0 0 0 .72 2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v2.083zm1.121 3.796zM11 16a5 5 0 1 1 0-10 5 5 0 0 1 0 10zm-1.293-2.292a3 3 0 0 0 4.001-4.001l-4.001 4zm-1.415-1.415l4.001-4a3 3 0 0 0-4.001 4.001z"/></symbol><symbol viewBox="0 0 16 16" id="issue-child" xmlns="http://www.w3.org/2000/svg"><path d="M11 8H5v1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2V7a.997.997 0 0 1 1-1h3V4H4.5a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9v2h3a.997.997 0 0 1 1 1v2h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h1V8zm-9 3v2h3v-2H2zm9 0v2h3v-2h-3z"/></symbol><symbol viewBox="0 0 16 16" id="issue-close" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M10.874 2H12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-2c-.918 0-1.74-.413-2.29-1.063a3.987 3.987 0 0 0 1.988-.984A1 1 0 0 0 10 14h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-1V3c0-.345-.044-.68-.126-1zM4 0h3a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-new" xmlns="http://www.w3.org/2000/svg"><path d="M10 2V1a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V4H9a1 1 0 1 1 0-2h1zm0 6a1 1 0 0 1 2 0v5a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h1a1 1 0 1 1 0 2H5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm0-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open-m" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-parent" xmlns="http://www.w3.org/2000/svg"><path d="M11 11H5v1h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H3v-2a.997.997 0 0 1 1-1h3V7H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H9v2h3a.997.997 0 0 1 1 1v2h2.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H11v-1zM6 3v2h4V3H6z"/></symbol><symbol viewBox="0 0 16 16" id="issues" xmlns="http://www.w3.org/2000/svg"><path d="M10.458 15.012l.311.055a3 3 0 0 0 3.476-2.433l1.389-7.879A3 3 0 0 0 13.2 1.28L11.23.933a3.002 3.002 0 0 0-.824-.031c.364.59.58 1.28.593 2.02l1.854.328a1 1 0 0 1 .811 1.158l-1.389 7.879a1 1 0 0 1-1.158.81l-.118-.02a3.98 3.98 0 0 1-.541 1.935zM3 0h4a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="italic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.5 12l2-8H6a1 1 0 1 1 0-2h6a1 1 0 0 1 0 2h-1.5l-2 8H10a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2h1.5z"/></symbol><symbol viewBox="0 0 16 16" id="key" xmlns="http://www.w3.org/2000/svg"><path d="M7.575 6.689a4.002 4.002 0 0 1 6.274-4.86 4 4 0 0 1-4.86 6.274l-2.21 2.21.706.708a1 1 0 1 1-1.414 1.414l-.707-.707-.707.707.707.707a1 1 0 1 1-1.414 1.414l-.707-.707a1 1 0 0 1-1.414-1.414l5.746-5.746zm2.032-.618a2 2 0 1 0 2.828-2.828A2 2 0 0 0 9.607 6.07z"/></symbol><symbol viewBox="0 0 16 16" id="key-2" xmlns="http://www.w3.org/2000/svg"><path d="M5.172 14.157l-.344.344-2.485.133a.462.462 0 0 1-.497-.503l.14-2.24a.599.599 0 0 1 .177-.382l5.155-5.155a4 4 0 1 1 2.828 2.828l-1.439 1.44-1.06-.354-.708.707.354 1.06-.707.708-1.06-.354-.708.707.354 1.06zm6.01-8.839a1 1 0 1 0 1.414-1.414 1 1 0 0 0-1.414 1.414z"/></symbol><symbol viewBox="0 0 16 16" id="label" xmlns="http://www.w3.org/2000/svg"><path d="M11.782 14.718a3 3 0 0 1-4.242 0L1.652 8.829a2 2 0 0 1-.565-1.702l.54-3.703a2 2 0 0 1 1.69-1.69l3.703-.54a2 2 0 0 1 1.703.564l5.888 5.888a3 3 0 0 1 0 4.243l-2.829 2.829zm1.415-5.657L7.309 3.173l-3.703.54-.54 3.702 5.888 5.888a1 1 0 0 0 1.414 0l2.829-2.828a1 1 0 0 0 0-1.414zM5.732 5.525A1 1 0 1 1 7.146 6.94a1 1 0 0 1-1.414-1.414z"/></symbol><symbol viewBox="0 0 16 16" id="labels" xmlns="http://www.w3.org/2000/svg"><path d="M9.424 2.254l2.08-.905a1 1 0 0 1 1.206.326l3.013 4.12a1 1 0 0 1 .16.849l-1.947 7.264a3 3 0 0 1-3.675 2.122l-.5-.135a3.999 3.999 0 0 0 1.082-1.782 1 1 0 0 0 1.16-.722l1.823-6.802-2.258-3.087-.687.299a2 2 0 0 0-.628-.88l-.829-.667zM.377 3.7L4.4.498a1 1 0 0 1 1.25.003L9.627 3.7a1 1 0 0 1 .373.78V13a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4.482A1 1 0 0 1 .377 3.7zM2 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V4.958L5.02 2.561 2 4.964V13zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="leave" xmlns="http://www.w3.org/2000/svg"><path d="M11 7V5.883a.5.5 0 0 1 .757-.429l3.528 2.117a.5.5 0 0 1 0 .858l-3.528 2.117a.5.5 0 0 1-.757-.43V9H7a1 1 0 1 1 0-2h4zm-2 6.256a1 1 0 0 1 2 0A2.744 2.744 0 0 1 8.256 16H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h5.19A2.81 2.81 0 0 1 11 2.81a1 1 0 0 1-2 0A.81.81 0 0 0 8.19 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h5.256c.41 0 .744-.333.744-.744z"/></symbol><symbol viewBox="0 0 16 16" id="level-up" xmlns="http://www.w3.org/2000/svg"><path fill="#2E2E2E" fill-rule="evenodd" d="M7 6h3.489a.5.5 0 0 0 .373-.832L6.374.117a.5.5 0 0 0-.748 0l-4.488 5.05A.5.5 0 0 0 1.51 6H5v7a3 3 0 0 0 3 3h6a1 1 0 0 0 0-2H8a1 1 0 0 1-1-1V6z"/></symbol><symbol viewBox="0 0 16 16" id="license" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12.56 8.9l2.66 4.606a.3.3 0 0 1-.243.45l-1.678.094a.1.1 0 0 0-.078.044l-.953 1.432a.3.3 0 0 1-.51-.016L9.097 10.9a5.994 5.994 0 0 0 3.464-2zm-5.23 2.063L4.707 15.51a.3.3 0 0 1-.51.016l-.953-1.432a.1.1 0 0 0-.078-.044l-1.678-.094a.3.3 0 0 1-.243-.45l2.48-4.297a5.983 5.983 0 0 0 3.607 1.754zM8 10A5 5 0 1 1 8 0a5 5 0 0 1 0 10zm0-2a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="link" xmlns="http://www.w3.org/2000/svg"><path d="M6.986 3.35l2.12-2.122a4 4 0 0 1 5.657 5.657l-2.828 2.829a4 4 0 0 1-5.657 0 1 1 0 0 1 1.414-1.415 2 2 0 0 0 2.829 0l2.828-2.828a2 2 0 1 0-2.828-2.828l-1.001 1a5.018 5.018 0 0 0-2.534-.294zm2.12 9.192l-2.12 2.121a4 4 0 1 1-5.658-5.656l2.829-2.829a4 4 0 0 1 5.657 0 1 1 0 1 1-1.415 1.414 2 2 0 0 0-2.828 0l-2.828 2.829a2 2 0 1 0 2.828 2.828l1.001-1.001a5.018 5.018 0 0 0 2.534.294z"/></symbol><symbol viewBox="0 0 16 16" id="list-bulleted" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-7h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 5h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm-4 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-2h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="list-numbered" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 2h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2zM1.156 5v-.828h.816V2.204h-.72v-.636c.432-.084.708-.192.996-.372h.756v2.976h.684V5H1.156zm-.18 5v-.588c.9-.828 1.596-1.464 1.596-1.98 0-.342-.192-.504-.468-.504-.252 0-.444.18-.624.36l-.552-.552c.396-.42.756-.612 1.32-.612.768 0 1.308.492 1.308 1.248 0 .612-.576 1.284-1.092 1.812.192-.024.468-.048.636-.048h.636V10H.976zm1.26 5.072c-.618 0-1.068-.204-1.356-.54l.468-.648c.234.216.51.36.78.36.336 0 .552-.12.552-.36 0-.288-.15-.456-.948-.456v-.72c.636 0 .828-.168.828-.432 0-.228-.138-.348-.396-.348-.252 0-.432.108-.672.312l-.516-.624c.372-.312.768-.492 1.236-.492.84 0 1.38.384 1.38 1.074 0 .366-.204.642-.612.822v.024c.432.132.732.432.732.912 0 .72-.684 1.116-1.476 1.116z"/></symbol><symbol viewBox="0 0 16 16" id="location" xmlns="http://www.w3.org/2000/svg"><path d="M8.755 15.144a1 1 0 0 1-1.51 0C3.748 11.114 2 8.065 2 6a6 6 0 1 1 12 0c0 2.065-1.748 5.113-5.245 9.144zM12 6a4 4 0 1 0-8 0c0 1.314 1.312 3.71 4 6.944C10.688 9.71 12 7.314 12 6zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="location-dot" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.314 13.087C4.382 13.295 3 13.85 3 14.5c0 .828 2.239 1.5 5 1.5s5-.672 5-1.5c0-.65-1.382-1.205-3.314-1.413l-.202.225a2 2 0 0 1-2.968 0l-.202-.225zm2.428-.445a1 1 0 0 1-1.484 0C4.419 9.5 3 7.037 3 5.252 3 2.353 5.239 0 8 0s5 2.352 5 5.253c0 1.784-1.42 4.247-4.258 7.389zM11 5.252C11 3.436 9.634 2 8 2S5 3.435 5 5.253c0 1.027.974 2.824 3 5.203 2.026-2.38 3-4.176 3-5.203zM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="lock" xmlns="http://www.w3.org/2000/svg"><path d="M10 5V4h2v1a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V8a3 3 0 0 1 3-3V4h2v1h4zM4 7a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H4zm0-3a4 4 0 1 1 8 0h-2a2 2 0 1 0-4 0H4z"/></symbol><symbol viewBox="0 0 16 16" id="lock-open" xmlns="http://www.w3.org/2000/svg"><path d="M4.044 4a4 4 0 0 1 6.99-2.658 1 1 0 1 1-1.495 1.33A2 2 0 0 0 6.044 4a.998.998 0 0 1-.07.367v.701H12a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3v-5a3 3 0 0 1 2.974-3V4h.07zM4 7.07a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="log" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4zm1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-5h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm0 3h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm-3 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-2h3a1 1 0 0 1 0 2H8a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="mail" xmlns="http://www.w3.org/2000/svg"><path d="M14 5.6L9.338 9.796a2 2 0 0 1-2.676 0L2 5.6V11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5.6zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm.212 2L8 8.31 12.788 4H3.212z"/></symbol><symbol viewBox="0 0 16 16" id="menu" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.143 2h13.714C15.488 2 16 2.448 16 3s-.512 1-1.143 1H1.143C.512 4 0 3.552 0 3s.512-1 1.143-1zm0 5h13.714C15.488 7 16 7.448 16 8s-.512 1-1.143 1H1.143C.512 9 0 8.552 0 8s.512-1 1.143-1zm0 5h13.714c.631 0 1.143.448 1.143 1s-.512 1-1.143 1H1.143C.512 14 0 13.552 0 13s.512-1 1.143-1z"/></symbol><symbol viewBox="0 0 16 16" id="merge-request-close" xmlns="http://www.w3.org/2000/svg"><path d="M9.414 8l1.414 1.414a1 1 0 1 1-1.414 1.414L8 9.414l-1.414 1.414a1 1 0 1 1-1.414-1.414L6.586 8 5.172 6.586a1 1 0 1 1 1.414-1.414L8 6.586l1.414-1.414a1 1 0 1 1 1.414 1.414L9.414 8zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="messages" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-.98-1.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM4.464 2.464L5.88 3.88a3 3 0 0 0 0 4.242L4.464 9.536a5 5 0 0 1 0-7.072zm7.072 7.072L10.12 8.12a3 3 0 0 0 0-4.242l1.415-1.415a5 5 0 0 1 0 7.072zM2.343.343l1.414 1.414a6 6 0 0 0 0 8.486l-1.414 1.414a8 8 0 0 1 0-11.314zm11.314 11.314l-1.414-1.414a6 6 0 0 0 0-8.486L13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="mobile-issue-close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.657 10.728L2.12 7.192A1 1 0 1 0 .707 8.607l4.243 4.242a.997.997 0 0 0 1.414 0l8.485-8.485a1 1 0 1 0-1.414-1.414l-7.778 7.778z"/></symbol><symbol viewBox="0 0 16 16" id="monitor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 13v1h3a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2h3v-1H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3h-3zM3 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm5.723 6.416l-2.66-1.773-1.71 1.71a.5.5 0 1 1-.707-.707l2-2a.5.5 0 0 1 .631-.062l2.66 1.773 2.71-2.71a.5.5 0 0 1 .707.707l-3 3a.5.5 0 0 1-.631.062z"/></symbol><symbol viewBox="0 0 16 16" id="more" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="notifications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 14H2.435a2 2 0 0 1-1.761-2.947c.962-1.788 1.521-3.065 1.68-3.832.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024c3.755.528 4.375 4.27 4.761 6.043.188.86.742 2.188 1.661 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0zm5.805-6.468c-.325-1.492-.37-1.674-.61-2.288C10.6 3.716 9.742 3 8.07 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.208 1.012-.827 2.424-1.877 4.375H13.64c-.993-1.937-1.6-3.396-1.835-4.468z"/></symbol><symbol viewBox="0 0 16 16" id="notifications-off" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.26 5.089c.243.757.382 1.478.5 2.017.187.86.74 2.188 1.66 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0H4.35l2-2h7.29c-.993-1.937-1.6-3.396-1.835-4.468-.07-.326-.129-.59-.178-.81l1.634-1.633zM10.943 1.75l-1.48 1.48C9.07 3.076 8.612 3 8.069 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.065.317-.17.673-.317 1.073L.45 12.242a1.99 1.99 0 0 1 .224-1.19c.962-1.787 1.521-3.064 1.68-3.831.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024 4.867 4.867 0 0 1 1.944.688zm2.932-.105a1 1 0 0 1 0 1.415L2.561 14.374a1 1 0 1 1-1.415-1.414L12.46 1.646a1 1 0 0 1 1.414 0z"/></symbol><symbol viewBox="0 0 16 16" id="overview" xmlns="http://www.w3.org/2000/svg"><path d="M2 0h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2h-3zM2 9h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3h-3z"/></symbol><symbol viewBox="0 0 16 16" id="pencil" xmlns="http://www.w3.org/2000/svg"><path d="M13.02 1.293l1.414 1.414a1 1 0 0 1 0 1.414L4.119 14.436a1 1 0 0 1-.704.293l-2.407.008L1 12.316a1 1 0 0 1 .293-.71L11.605 1.292a1 1 0 0 1 1.414 0zm-1.416 1.415l-.707.707L12.31 4.83l.707-.707-1.414-1.415zM3.411 13.73l1.123-1.122H3.12v-1.415L2 12.312l.005 1.422 1.406-.005z"/></symbol><symbol viewBox="0 0 16 16" id="pencil-square" xmlns="http://www.w3.org/2000/svg"><path d="M12 9a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V9zm.778-7.179l1.414 1.415-6.476 6.476a1 1 0 0 1-.498.27l-1.51.325.323-1.512a1 1 0 0 1 .27-.497l6.477-6.477zM15.607.407a1 1 0 0 1 0 1.414l-.708.707-1.414-1.414.707-.707a1 1 0 0 1 1.415 0z"/></symbol><symbol viewBox="0 0 16 16" id="pipeline" xmlns="http://www.w3.org/2000/svg"><path d="M8.969 7.25a2 2 0 1 1-1.938 0A1.002 1.002 0 0 1 7 7V5.083a.2.2 0 0 1 .06-.142l.877-.87a.1.1 0 0 1 .141 0l.864.87A.2.2 0 0 1 9 5.083V7c0 .086-.01.17-.031.25zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm4.5-4a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zM8 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="play" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.765 15.835c-.545.321-1.258.159-1.593-.363A1.075 1.075 0 0 1 1 14.89V1.11C1 .496 1.518 0 2.158 0c.214 0 .424.057.607.165l11.684 6.89c.544.321.714 1.005.38 1.526a1.135 1.135 0 0 1-.38.364l-11.684 6.89z"/></symbol><symbol viewBox="0 0 16 16" id="plus" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V1a1 1 0 1 1 2 0v6h6a1 1 0 0 1 0 2H9v6a1 1 0 0 1-2 0V9H1a1 1 0 1 1 0-2h6z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 7V4a1 1 0 1 0-2 0v3H4a1 1 0 1 0 0 2h3v3a1 1 0 0 0 2 0V9h3a1 1 0 0 0 0-2H9zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square-o" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="preferences" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 12h10a1 1 0 0 1 0 2H5a1 1 0 0 1-2 0v-2a1 1 0 0 1 2 0zm-3 0H1a1 1 0 0 0 0 2h1v-2zm11-5h2a1 1 0 0 1 0 2h-2a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0zm-3 0H1a1 1 0 1 0 0 2h9V7zM6 2h9a1 1 0 0 1 0 2H6a1 1 0 1 1-2 0V2a1 1 0 1 1 2 0zM3 2H1a1 1 0 1 0 0 2h2V2z"/></symbol><symbol viewBox="0 0 16 16" id="profile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-4.274-3.404C4.412 9.709 5.694 9 8 9c2.313 0 3.595.7 4.28 1.586A4.997 4.997 0 0 1 8 13a4.997 4.997 0 0 1-4.274-2.404zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="project" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177l-.038.044a.505.505 0 0 0 .038-.044zm-.787 0a.5.5 0 0 0 .038.043l-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="push-rules" xmlns="http://www.w3.org/2000/svg"><path d="M6.268 9a2 2 0 0 1 3.464 0H11a1 1 0 0 1 0 2H9.732a2 2 0 0 1-3.464 0H5a1 1 0 0 1 0-2h1.268zM7 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1v3.515a.3.3 0 0 1-.434.268l-1.432-.716a.3.3 0 0 0-.268 0l-1.432.716A.3.3 0 0 1 7 5.515V2zM4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm4 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="question" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm-1.46-5.602h2.233a3.97 3.97 0 0 1 .051-.558c.029-.17.073-.326.133-.469.06-.143.14-.28.242-.41.102-.13.228-.263.38-.399.26-.24.504-.467.733-.683a5.03 5.03 0 0 0 .598-.668c.17-.23.302-.477.399-.742a2.66 2.66 0 0 0 .144-.907c0-.505-.083-.95-.25-1.335a2.55 2.55 0 0 0-.723-.97 3.2 3.2 0 0 0-1.152-.589 5.441 5.441 0 0 0-1.531-.2c-.516 0-.998.063-1.445.188a3.19 3.19 0 0 0-1.168.59c-.331.268-.594.61-.79 1.027-.195.417-.295.917-.3 1.5h2.64c.006-.224.04-.416.102-.578.062-.161.142-.293.238-.394a.921.921 0 0 1 .332-.227 1.04 1.04 0 0 1 .39-.074c.34 0 .593.095.763.285.169.19.254.488.254.895 0 .328-.106.63-.317.906-.21.276-.499.565-.863.867-.214.182-.39.374-.531.574-.141.2-.253.42-.336.657a3.656 3.656 0 0 0-.176.777 7.89 7.89 0 0 0-.05.937zm-.321 2.375c0 .188.035.362.105.524.07.161.17.3.301.418.13.117.284.21.46.277.178.068.376.102.595.102.218 0 .416-.034.593-.102.178-.068.331-.16.461-.277a1.2 1.2 0 0 0 .301-.418c.07-.162.106-.336.106-.524a1.3 1.3 0 0 0-.106-.523 1.2 1.2 0 0 0-.3-.418 1.461 1.461 0 0 0-.462-.277 1.651 1.651 0 0 0-.593-.102c-.22 0-.417.034-.594.102a1.46 1.46 0 0 0-.461.277 1.2 1.2 0 0 0-.3.418 1.284 1.284 0 0 0-.106.523z"/></symbol><symbol viewBox="0 0 16 16" id="question-o" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-.778-4.151c0-.301.014-.575.044-.82a3.2 3.2 0 0 1 .154-.68c.073-.208.17-.4.294-.575.123-.176.278-.343.465-.503a4.81 4.81 0 0 0 .755-.758c.185-.242.277-.506.277-.793 0-.356-.074-.617-.222-.783-.148-.166-.37-.25-.667-.25a.92.92 0 0 0-.342.065.806.806 0 0 0-.29.199 1.04 1.04 0 0 0-.209.345 1.5 1.5 0 0 0-.088.506H5.082c.005-.51.092-.948.263-1.313.171-.364.401-.664.69-.899.29-.234.63-.406 1.023-.516a4.66 4.66 0 0 1 1.264-.164c.497 0 .944.058 1.34.174.397.117.733.289 1.008.517.276.227.487.51.633.847.146.337.218.727.218 1.17 0 .295-.042.56-.126.792a2.52 2.52 0 0 1-.349.65 4.4 4.4 0 0 1-.523.584c-.2.19-.414.389-.642.598a2.73 2.73 0 0 0-.332.349c-.089.114-.16.233-.212.359a1.868 1.868 0 0 0-.116.41 3.39 3.39 0 0 0-.044.489H7.222zm-.28 2.078c0-.164.03-.317.092-.458a1.05 1.05 0 0 1 .263-.366c.114-.103.248-.183.403-.243a1.45 1.45 0 0 1 .52-.089c.191 0 .364.03.52.09.154.059.289.14.403.242.114.103.201.224.263.366.061.141.092.294.092.458 0 .164-.03.316-.092.458a1.05 1.05 0 0 1-.263.365 1.278 1.278 0 0 1-.404.243 1.43 1.43 0 0 1-.52.089c-.19 0-.364-.03-.519-.089-.155-.06-.29-.14-.403-.243a1.05 1.05 0 0 1-.263-.365 1.135 1.135 0 0 1-.093-.458z"/></symbol><symbol viewBox="0 0 16 16" id="quote" xmlns="http://www.w3.org/2000/svg"><path d="M15 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9h-2a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1zM7 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="redo" xmlns="http://www.w3.org/2000/svg"><path d="M4.666 4.423a5 5 0 1 1-.203 6.944 1 1 0 1 0-1.478 1.347 7 7 0 1 0 .12-9.556L1.842 2.137a.5.5 0 0 0-.815.385L1 7.26a.5.5 0 0 0 .607.492l4.629-1.013a.5.5 0 0 0 .207-.877L4.666 4.423z"/></symbol><symbol viewBox="0 0 16 16" id="remove" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2v10a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V3zm3-2a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1H5zM4 3v10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3H4zm2.5 2a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm3 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 16 16" id="repeat" xmlns="http://www.w3.org/2000/svg"><path d="M11.494 4.423a5 5 0 1 0 .203 6.944 1 1 0 1 1 1.478 1.347 7 7 0 1 1-.12-9.556l1.262-1.021a.5.5 0 0 1 .815.385l.028 4.738a.5.5 0 0 1-.607.492L9.924 6.739a.5.5 0 0 1-.207-.877l1.777-1.439z"/></symbol><symbol viewBox="0 0 16 16" id="retry" xmlns="http://www.w3.org/2000/svg"><path d="M4.114 6.958a4 4 0 0 0 5.283 4.775 1 1 0 1 1 .712 1.87A6 6 0 0 1 2.182 6.44l-.741-.2a.5.5 0 0 1-.12-.915l2.195-1.268a.5.5 0 0 1 .683.183l1.268 2.196a.5.5 0 0 1-.563.733l-.79-.212zm7.777 2.084a4 4 0 0 0-5.284-4.775 1 1 0 0 1-.712-1.87 6 6 0 0 1 7.927 7.162l.742.2a.5.5 0 0 1 .12.915l-2.196 1.268a.5.5 0 0 1-.683-.183l-1.267-2.196a.5.5 0 0 1 .562-.733l.79.212z"/></symbol><symbol viewBox="0 0 16 16" id="scale" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.99 9a.792.792 0 0 0-.078-.231L13 7l-.912 1.769a.791.791 0 0 0-.077.231h1.978zm-10 0a.792.792 0 0 0-.078-.231L3 7l-.912 1.769A.791.791 0 0 0 2.011 9h1.978zM2 0h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm3 14h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zM8 4a1 1 0 0 1 1 1v9H7V5a1 1 0 0 1 1-1zm-4.53-.714l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 3 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158L2.53 3.286a.53.53 0 0 1 .94 0zm10 0l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 13 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158l2.266-4.735a.53.53 0 0 1 .94 0z"/></symbol><symbol viewBox="0 0 16 16" id="screen-full" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 14v-2a1 1 0 0 1 2 0v3a.997.997 0 0 1-1 1h-3a1 1 0 0 1 0-2h2zM2 14v-2a1 1 0 0 0-2 0v3a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2H2zM15.707.293A.997.997 0 0 1 16 1v3a1 1 0 0 1-2 0V2h-2a1 1 0 0 1 0-2h3c.276 0 .526.112.707.293zM2 2v2a1 1 0 1 1-2 0V1a.997.997 0 0 1 1-1h3a1 1 0 1 1 0 2H2zm4 4h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="screen-normal" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 3V1a1 1 0 1 1 2 0v3a.997.997 0 0 1-1 1H1a1 1 0 1 1 0-2h2zm10 0h2a1 1 0 0 1 0 2h-3a.997.997 0 0 1-1-1V1a1 1 0 0 1 2 0v2zM3 13H1a1 1 0 0 1 0-2h3a.997.997 0 0 1 1 1v3a1 1 0 0 1-2 0v-2zm10 0v2a1 1 0 0 1-2 0v-3a.997.997 0 0 1 1-1h3a1 1 0 0 1 0 2h-2zM6.5 7h3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 12 16" id="scroll_down" xmlns="http://www.w3.org/2000/svg"><path class="ewfirst-triangle" d="M1.048 14.155a.508.508 0 0 0-.32.105c-.091.07-.136.154-.136.25v.71c0 .095.045.178.135.249.09.07.197.105.321.105h10.043a.51.51 0 0 0 .321-.105c.09-.07.136-.154.136-.25v-.71c0-.095-.045-.178-.136-.249a.508.508 0 0 0-.32-.105"/><path class="ewsecond-triangle" d="M.687 8.027c-.09-.087-.122-.16-.093-.22.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 12.91a.458.458 0 0 1-.136.089h-.37a.626.626 0 0 1-.136-.09"/><path class="ewthird-triangle" d="M.687 1.027C.597.94.565.867.594.807c.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 5.91a.458.458 0 0 1-.136.09h-.37a.626.626 0 0 1-.136-.09"/></symbol><symbol viewBox="0 0 12 16" id="scroll_up" xmlns="http://www.w3.org/2000/svg"><path d="M1.048 1.845a.508.508 0 0 1-.32-.105c-.091-.07-.136-.154-.136-.25V.78c0-.095.045-.178.135-.249a.508.508 0 0 1 .321-.105h10.043a.51.51 0 0 1 .321.105c.09.07.136.154.136.25v.71c0 .095-.045.178-.136.249a.508.508 0 0 1-.32.105M.687 7.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 3.09A.458.458 0 0 0 6.257 3h-.37a.626.626 0 0 0-.136.09M.687 14.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 10.09a.458.458 0 0 0-.136-.09h-.37a.626.626 0 0 0-.136.09"/></symbol><symbol viewBox="0 0 16 16" id="search" xmlns="http://www.w3.org/2000/svg"><path d="M8.853 8.854a3.5 3.5 0 1 0-4.95-4.95 3.5 3.5 0 0 0 4.95 4.95zm.207 2.328a5.5 5.5 0 1 1 2.121-2.121l3.329 3.328a1.5 1.5 0 0 1-2.121 2.121L9.06 11.182z"/></symbol><symbol viewBox="0 0 16 16" id="settings" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.415 5.803L1.317 4.084A.5.5 0 0 1 1.35 3.5l.805-.994a.5.5 0 0 1 .564-.153l1.878.704a5.975 5.975 0 0 1 1.65-.797L6.885.342A.5.5 0 0 1 7.36 0h1.28a.5.5 0 0 1 .474.342l.639 1.918a5.97 5.97 0 0 1 1.65.797l1.877-.704a.5.5 0 0 1 .565.153l.805.994a.5.5 0 0 1 .032.584l-1.097 1.719c.217.551.354 1.143.399 1.76l1.731 1.058a.5.5 0 0 1 .227.54l-.288 1.246a.5.5 0 0 1-.44.385l-2.008.19a6.026 6.026 0 0 1-1.142 1.431l.265 1.995a.5.5 0 0 1-.277.516l-1.15.56a.5.5 0 0 1-.576-.1l-1.424-1.452a6.047 6.047 0 0 1-1.804 0l-1.425 1.453a.5.5 0 0 1-.576.1l-1.15-.561a.5.5 0 0 1-.276-.516l.265-1.995a6.026 6.026 0 0 1-1.143-1.43l-2.008-.191a.5.5 0 0 1-.44-.385L.058 9.16a.5.5 0 0 1 .226-.539l1.732-1.058a5.968 5.968 0 0 1 .399-1.76zM8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="shield" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v7.186a3 3 0 0 1-1.426 2.554l-4 2.465a3 3 0 0 1-3.148 0l-4-2.465A3 3 0 0 1 1 10.186V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v7.186a1 1 0 0 0 .475.852l4 2.464a1 1 0 0 0 1.05 0l4-2.464a1 1 0 0 0 .475-.852V3a1 1 0 0 0-1-1H4zm0 1.5a.5.5 0 0 1 .5-.5h4v8.837a.5.5 0 0 1-.753.431l-3.5-2.052A.5.5 0 0 1 4 9.785V3.5z"/></symbol><symbol viewBox="0 0 16 16" id="slight-frown" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-2.163-3.275a2.499 2.499 0 0 1 4.343.03.5.5 0 0 1-.871.49 1.5 1.5 0 0 0-2.607-.018.5.5 0 1 1-.865-.502zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="slight-smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-5.163 2.254a.5.5 0 1 1 .865-.502 1.499 1.499 0 0 0 2.607-.018.5.5 0 1 1 .871.49 2.499 2.499 0 0 1-4.343.03z"/></symbol><symbol viewBox="0 0 16 16" id="smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM6.18 6.27a.5.5 0 0 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zm6 0a.5.5 0 1 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zM5 9a3 3 0 0 0 6 0H5z"/></symbol><symbol viewBox="0 0 16 16" id="smiley" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM5 9h6a3 3 0 0 1-6 0z"/></symbol><symbol viewBox="0 0 16 16" id="snippet" xmlns="http://www.w3.org/2000/svg"><path d="M10.67 9.31a3.001 3.001 0 0 1 2.062 5.546 3 3 0 0 1-3.771-4.559 1.007 1.007 0 0 1-.095-.137l-4.5-7.794a1 1 0 0 1 1.732-1l4.5 7.794c.028.05.052.1.071.15zm-3.283.35l-.289.5c-.028.05-.06.095-.095.137a3.001 3.001 0 0 1-3.77 4.56A3 3 0 0 1 5.294 9.31c.02-.051.043-.102.071-.15l.866-1.5 1.155 2zm2.31-4l-1.156-2 1.325-2.294a1 1 0 0 1 1.732 1L9.696 5.66zm-5.465 7.464a1 1 0 1 0 1-1.732 1 1 0 0 0-1 1.732zm7.5 0a1 1 0 1 0-1-1.732 1 1 0 0 0 1 1.732z"/></symbol><symbol viewBox="0 0 16 16" id="spam" xmlns="http://www.w3.org/2000/svg"><path d="M8.75.433l5.428 3.134a1.5 1.5 0 0 1 .75 1.299v6.268a1.5 1.5 0 0 1-.75 1.299L8.75 15.567a1.5 1.5 0 0 1-1.5 0l-5.428-3.134a1.5 1.5 0 0 1-.75-1.299V4.866a1.5 1.5 0 0 1 .75-1.299L7.25.433a1.5 1.5 0 0 1 1.5 0zM3.072 5.155v5.69L8 13.691l4.928-2.846v-5.69L8 2.309 3.072 5.155zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 14 14" id="spinner" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="7" cy="7" r="6" stroke="#000" stroke-opacity=".1" stroke-width="2"/><path fill="#000" fill-opacity=".1" fill-rule="nonzero" d="M7 0a7 7 0 0 1 7 7h-2a5 5 0 0 0-5-5V0z"/></g></symbol><symbol viewBox="0 0 16 16" id="star" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.609 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 16 16" id="star-o" xmlns="http://www.w3.org/2000/svg"><path d="M10.975 10.99a3 3 0 0 1 .655-2.083l1.54-1.916-2.219-.576a3 3 0 0 1-1.825-1.37L8 3.15 6.874 5.044a3 3 0 0 1-1.825 1.371l-2.218.576 1.54 1.916a3 3 0 0 1 .654 2.083l-.165 2.4 1.965-.836a3 3 0 0 1 2.348 0l1.965.836-.164-2.399zM7.61 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 14 14" id="status_canceled" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M5.2 3.8l4.9 4.9c.2.2.2.5 0 .7l-.7.7c-.2.2-.5.2-.7 0L3.8 5.2c-.2-.2-.2-.5 0-.7l.7-.7c.2-.2.5-.2.7 0"/></g></symbol><symbol viewBox="0 0 22 22" id="status_canceled_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M8.171 5.971l7.7 7.7a.76.76 0 0 1 0 1.1l-1.1 1.1a.76.76 0 0 1-1.1 0l-7.7-7.7a.76.76 0 0 1 0-1.1l1.1-1.1a.76.76 0 0 1 1.1 0"/></symbol><symbol viewBox="0 0 16 16" id="status_closed" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.83a1 1 0 0 1 1.414 1.416l-3.535 3.535a1 1 0 0 1-1.415.001l-2.12-2.12a1 1 0 1 1 1.413-1.415zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 14 14" id="status_created" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><circle cx="7" cy="7" r="3.25"/></g></symbol><symbol viewBox="0 0 22 22" id="status_created_borderless" xmlns="http://www.w3.org/2000/svg"><circle cx="11" cy="11" r="5.107"/></symbol><symbol viewBox="0 0 14 14" id="status_failed" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 5.969L5.599 4.568a.29.29 0 0 0-.413.004l-.614.614a.294.294 0 0 0-.004.413L5.968 7l-1.4 1.401a.29.29 0 0 0 .004.413l.614.614c.113.114.3.117.413.004L7 8.032l1.401 1.4a.29.29 0 0 0 .413-.004l.614-.614a.294.294 0 0 0 .004-.413L8.032 7l1.4-1.401a.29.29 0 0 0-.004-.413l-.614-.614a.294.294 0 0 0-.413-.004L7 5.968z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_failed_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 9.38L8.798 7.178a.455.455 0 0 0-.65.006l-.964.965a.462.462 0 0 0-.006.65L9.38 11l-2.202 2.202a.455.455 0 0 0 .006.65l.965.964a.462.462 0 0 0 .65.006L11 12.62l2.202 2.202a.455.455 0 0 0 .65-.006l.964-.965a.462.462 0 0 0 .006-.65L12.62 11l2.202-2.202a.455.455 0 0 0-.006-.65l-.965-.964a.462.462 0 0 0-.65-.006L11 9.38z"/></symbol><symbol viewBox="0 0 14 14" id="status_manual" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M10.5 7.63V6.37l-.787-.13c-.044-.175-.132-.349-.263-.61l.481-.652-.918-.913-.657.478a2.346 2.346 0 0 0-.612-.26L7.656 3.5H6.388l-.132.783c-.219.043-.394.13-.612.26l-.657-.478-.918.913.437.652c-.131.218-.175.392-.262.61l-.744.086v1.261l.787.13c.044.218.132.392.263.61l-.438.651.92.913.655-.434c.175.086.394.173.613.26l.131.783h1.313l.131-.783c.219-.043.394-.13.613-.26l.656.478.918-.913-.48-.652c.13-.218.218-.435.262-.61l.656-.13zM7 8.283a1.285 1.285 0 0 1-1.313-1.305c0-.739.57-1.304 1.313-1.304.744 0 1.313.565 1.313 1.304 0 .74-.57 1.305-1.313 1.305z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_manual_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M16.5 11.99v-1.98l-1.238-.206c-.068-.273-.206-.546-.412-.956l.756-1.025-1.444-1.435-1.03.752a3.686 3.686 0 0 0-.963-.41L12.03 5.5h-1.994l-.206 1.23c-.343.068-.618.205-.962.41l-1.031-.752-1.444 1.435.687 1.025c-.206.341-.275.615-.412.956L5.5 9.941v1.981l1.237.205c.07.342.207.615.413.957l-.688 1.025 1.444 1.434 1.032-.683c.274.137.618.274.962.41l.206 1.23h2.063l.206-1.23c.344-.068.619-.205.963-.41l1.03.752 1.444-1.435-.756-1.025c.207-.341.344-.683.413-.956l1.031-.205zM11 13.017c-1.169 0-2.063-.889-2.063-2.05 0-1.162.894-2.05 2.063-2.05s2.063.888 2.063 2.05c0 1.161-.894 2.05-2.063 2.05z"/></symbol><symbol viewBox="0 0 22 22" id="status_notfound_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M12.822 11.29c.816-.581 1.421-1.348 1.683-2.322.603-2.243-.973-4.553-3.53-4.553-1.15 0-2.085.41-2.775 1.089-.42.413-.672.835-.8 1.167a1.179 1.179 0 0 0 2.2.847c.016-.043.1-.184.252-.334.264-.259.613-.412 1.123-.412.938 0 1.47.78 1.254 1.584-.105.39-.37.726-.773 1.012a3.25 3.25 0 0 1-.945.47 1.179 1.179 0 0 0-.874 1.138v2.234a1.179 1.179 0 1 0 2.358 0v-1.43a5.9 5.9 0 0 0 .827-.492z"/><ellipse cx="10.825" cy="16.711" rx="1.275" ry="1.322"/></symbol><symbol viewBox="0 0 14 14" id="status_open" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><path d="M7 9.219a2.218 2.218 0 1 0 0-4.436A2.218 2.218 0 0 0 7 9.22zm0 1.12a3.338 3.338 0 1 1 0-6.676 3.338 3.338 0 0 1 0 6.676z"/></symbol><symbol viewBox="0 0 14 14" id="status_pending" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M4.7 5.3c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H5c-.2 0-.3-.1-.3-.3V5.3m3 0c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H8c-.2 0-.3-.1-.3-.3V5.3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_pending_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M7.386 8.329c0-.315.157-.472.471-.472h1.414c.315 0 .472.157.472.472v5.342c0 .315-.157.472-.472.472H7.857c-.314 0-.471-.157-.471-.472V8.33m4.714 0c0-.315.157-.472.471-.472h1.415c.314 0 .471.157.471.472v5.342c0 .315-.157.472-.471.472H12.57c-.314 0-.471-.157-.471-.472V8.33"/></symbol><symbol viewBox="0 0 14 14" id="status_running" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 3c2.2 0 4 1.8 4 4s-1.8 4-4 4c-1.3 0-2.5-.7-3.3-1.7L7 7V3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_running_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 4.714c3.457 0 6.286 2.829 6.286 6.286 0 3.457-2.829 6.286-6.286 6.286-2.043 0-3.929-1.1-5.186-2.672L11 11V4.714"/></symbol><symbol viewBox="0 0 14 14" id="status_skipped" xmlns="http://www.w3.org/2000/svg"><path d="M7 14A7 7 0 1 1 7 0a7 7 0 0 1 0 14z"/><path d="M7 13A6 6 0 1 0 7 1a6 6 0 0 0 0 12z" fill="#FFF"/><path d="M6.415 7.04L4.579 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L5.341 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L6.415 7.04zm2.54 0L7.119 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L7.881 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L8.955 7.04z"/></symbol><symbol viewBox="0 0 22 22" id="status_skipped_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M14.072 11.063l-2.82 2.82a.46.46 0 0 0-.001.652l.495.495a.457.457 0 0 0 .653-.001l3.7-3.7a.46.46 0 0 0 .001-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.479-3.479a.464.464 0 0 0-.654.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/><path d="M10.08 11.063l-2.819 2.82a.46.46 0 0 0-.002.652l.496.495a.457.457 0 0 0 .652-.001l3.7-3.7a.46.46 0 0 0 .002-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.48-3.479a.464.464 0 0 0-.653.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/></symbol><symbol viewBox="0 0 14 14" id="status_success" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_success_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.866 12.095l-1.95-1.95a.462.462 0 0 0-.647.01l-.964.964a.46.46 0 0 0-.01.646l3.013 3.014a.787.787 0 0 0 1.106.008l.425-.425 4.854-4.853a.462.462 0 0 0 .002-.659l-.964-.964a.468.468 0 0 0-.658.002l-4.207 4.207z"/></symbol><symbol viewBox="0 0 14 14" id="status_success_solid" xmlns="http://www.w3.org/2000/svg"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7zm6.278.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 14 14" id="status_warning" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6 3.5c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v4c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-4m0 6c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v1c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-1"/></g></symbol><symbol viewBox="0 0 22 22" id="status_warning_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.429 5.5c0-.471.314-.786.785-.786h1.572c.471 0 .785.315.785.786v6.286c0 .471-.314.785-.785.785h-1.572c-.471 0-.785-.314-.785-.785V5.5m0 9.429c0-.472.314-.786.785-.786h1.572c.471 0 .785.314.785.786V16.5c0 .471-.314.786-.785.786h-1.572c-.471 0-.785-.315-.785-.786v-1.571"/></symbol><symbol viewBox="0 0 16 16" id="stop" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 0h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="task-done" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="template" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm.8 2h2.4a.8.8 0 0 1 .8.8v1.4a.8.8 0 0 1-.8.8H3.8a.8.8 0 0 1-.8-.8V4.8a.8.8 0 0 1 .8-.8zm4.7 0h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm0 2h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm-5 3h9a.5.5 0 1 1 0 1h-9a.5.5 0 0 1 0-1zm0 2h9a.5.5 0 1 1 0 1h-9a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="terminal" xmlns="http://www.w3.org/2000/svg"><path d="M7 8a.997.997 0 0 1-.293.707l-1.414 1.414a1 1 0 1 1-1.414-1.414L4.586 8l-.707-.707a1 1 0 1 1 1.414-1.414l1.414 1.414A.997.997 0 0 1 7 8zM4 0h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H4zm5 7h2a1 1 0 0 1 0 2H9a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 11h5.282a2 2 0 0 0 1.963-2.38l-.563-2.905a3 3 0 0 0-.243-.732l-1.103-2.286A3 3 0 0 0 10.964 1H7a3 3 0 0 0-3 3v6.3a2 2 0 0 0 .436 1.247l3.11 3.9a.632.632 0 0 0 .941.053l.137-.137a1 1 0 0 0 .28-.87L8.329 11zM1 10h2V3H1a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 5h5.282a2 2 0 0 1 1.963 2.38l-.563 2.905a3 3 0 0 1-.243.732l-1.103 2.286A3 3 0 0 1 10.964 15H7a3 3 0 0 1-3-3V5.7a2 2 0 0 1 .436-1.247l3.11-3.9A.632.632 0 0 1 8.487.5l.137.137a1 1 0 0 1 .28.87L8.329 5zM1 6h2v7H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="thumbtack" xmlns="http://www.w3.org/2000/svg"><path d="M7.125 9h-2.19a.5.5 0 0 1-.417-.777L6 6V2L5.362.724A.5.5 0 0 1 5.809 0h4.382a.5.5 0 0 1 .447.724L10 2v4l1.482 2.223a.5.5 0 0 1-.416.777H8.875L8 16l-.875-7z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 16 16" id="timer" xmlns="http://www.w3.org/2000/svg"><path d="M12.022 3.27l.77-.77a1 1 0 0 1 1.415 1.414l-.728.729a7 7 0 1 1-1.456-1.372zM8 14A5 5 0 1 0 8 4a5 5 0 0 0 0 10zm0-9a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zM6 0h4a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-add" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 4V2a1 1 0 0 1 2 0v2h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0V6H8a1 1 0 1 1 0-2h2zm2 7a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-done" xmlns="http://www.w3.org/2000/svg"><path d="M8.243 7.485l4.95-4.95a1 1 0 1 1 1.414 1.415L8.95 9.607a.997.997 0 0 1-1.414 0L4.707 6.778a1 1 0 0 1 1.414-1.414l2.122 2.121zM12 11a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="token" xmlns="http://www.w3.org/2000/svg"><path d="M3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H3zm1 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="unapproval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.95 8.536l1.06-1.061a1 1 0 0 1 1.415 1.414l-1.061 1.06 1.06 1.061a1 1 0 0 1-1.414 1.415l-1.06-1.061-1.06 1.06a1 1 0 1 1-1.415-1.414l1.06-1.06-1.06-1.06a1 1 0 0 1 1.414-1.415l1.06 1.06zm-3.768-.33c.006.503.201 1.006.586 1.39l.353.354-.353.353a2 2 0 1 0 2.828 2.829l.354-.354.047.048C11.964 14.363 11.527 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.834 0 1.557.074 2.182.205zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="unassignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11 5h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="unlink" xmlns="http://www.w3.org/2000/svg"><path d="M11.295 8.845l-.659-1.664a1.78 1.78 0 0 0 .04-.04l1.415-1.414c.586-.586.654-1.468.152-1.97s-1.384-.434-1.97.152L8.859 5.323a1.781 1.781 0 0 0-.04.04l-1.664-.658c.141-.208.305-.408.491-.594l1.415-1.414c1.366-1.367 3.424-1.525 4.596-.354 1.171 1.172 1.013 3.23-.354 4.596L11.89 8.354c-.186.186-.386.35-.594.491zm-2.45 2.45a4.075 4.075 0 0 1-.491.594l-1.415 1.414c-1.366 1.367-3.424 1.525-4.596.354-1.171-1.172-1.013-3.23.354-4.596L4.11 7.646c.186-.186.386-.35.594-.491l.659 1.664a1.781 1.781 0 0 0-.04.04l-1.415 1.414c-.586.586-.654 1.468-.152 1.97s1.384.434 1.97-.152l1.414-1.414a1.78 1.78 0 0 0 .04-.04l1.664.658zm3.812-2.088h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-.05a.5.5 0 0 1 .5-.5zm-.384 2.116l1.415 1.414a.5.5 0 0 1 0 .708l-.037.036a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 0-.707l.036-.037a.5.5 0 0 1 .707 0zm-2.823 1.09a.5.5 0 0 1 .5-.5h.052a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9.95a.5.5 0 0 1-.5-.5v-2zm-2.748-9.16a.5.5 0 0 1-.5.5h-.05a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h.05a.5.5 0 0 1 .5.5v2zm-2.116.383a.5.5 0 0 1 0 .707l-.036.036a.5.5 0 0 1-.707 0L2.428 2.965a.5.5 0 0 1 0-.707l.037-.036a.5.5 0 0 1 .707 0l1.414 1.414zm-1.09 2.823h-2a.5.5 0 0 1-.5-.5v-.051a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5z"/></symbol><symbol viewBox="0 0 16 16" id="user" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 8c-6.888 0-6.976-.78-6.976-2.52S2.144 8 8 8s6.976 2.692 6.976 4.48c0 1.788-.088 2.52-6.976 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="users" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.521 8.01C15.103 8.19 16 10.755 16 12.48c0 1.533-.056 2.29-3.808 2.475.609-.54.808-1.331.808-2.475 0-1.911-.804-3.503-2.479-4.47zm-1.67-1.228A3.987 3.987 0 0 0 9.976 4a3.987 3.987 0 0 0-1.125-2.782 3 3 0 1 1 0 5.563zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="volume-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 5h1v6H1a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm2 0l4.445-2.964A1 1 0 0 1 9 2.87v10.26a1 1 0 0 1-1.555.833L3 11V5zm10.283 7.89a.5.5 0 0 1-.66-.752A5.485 5.485 0 0 0 14.5 8c0-1.601-.687-3.09-1.865-4.128a.5.5 0 0 1 .661-.75A6.484 6.484 0 0 1 15.5 8a6.485 6.485 0 0 1-2.217 4.89zm-2.002-2.236a.5.5 0 1 1-.652-.758c.55-.472.871-1.157.871-1.896 0-.732-.315-1.411-.856-1.883a.5.5 0 0 1 .658-.753A3.492 3.492 0 0 1 12.5 8c0 1.033-.45 1.994-1.219 2.654z"/></symbol><symbol viewBox="0 0 16 16" id="warning" xmlns="http://www.w3.org/2000/svg"><path d="M15.34 10.479A3 3 0 0 1 12.756 15h-9.51A3 3 0 0 1 .66 10.479l4.755-8.083a3 3 0 0 1 5.172 0l4.755 8.083zm-6.478-7.07a1 1 0 0 0-1.724 0l-4.755 8.084A1 1 0 0 0 3.245 13h9.51a1 1 0 0 0 .862-1.507L8.862 3.41zM8 5a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zm0 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="work" xmlns="http://www.w3.org/2000/svg"><path d="M12 3h1a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h1V2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1zM6 2v1h4V2H6zM3 5a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H3zm1.5 1a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm7 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol></svg>
\ No newline at end of file +<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 16 16" id="abuse" xmlns="http://www.w3.org/2000/svg"><path d="M11.408.328l4.029 3.222A1.5 1.5 0 0 1 16 4.72v6.555a1.5 1.5 0 0 1-.563 1.171l-4.026 3.224a1.5 1.5 0 0 1-.937.329H5.529a1.5 1.5 0 0 1-.937-.328L.563 12.45A1.5 1.5 0 0 1 0 11.28V4.724a1.5 1.5 0 0 1 .563-1.171L4.589.329A1.5 1.5 0 0 1 5.526 0h4.945c.34 0 .67.116.937.328zM10.296 2H5.702L2 4.964v6.074L5.704 14h4.594L14 11.036V4.962L10.296 2zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="account" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.195 9.965l-.568-.875a.25.25 0 0 1 .015-.294l.405-.5a.25.25 0 0 1 .283-.075l.938.36c.257-.183.543-.325.851-.42l.322-.988A.25.25 0 0 1 11.679 7h.642a.25.25 0 0 1 .238.173l.322.988c.308.095.594.237.851.42l.938-.36a.25.25 0 0 1 .283.076l.405.5a.25.25 0 0 1 .015.293l-.568.875c.113.297.18.616.193.95l.898.54a.25.25 0 0 1 .115.27l-.144.626a.25.25 0 0 1-.222.193l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.281a.25.25 0 0 1-.29-.05l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.904a.25.25 0 0 1-.289.051l-.577-.281a.25.25 0 0 1-.138-.26l.165-1.18a3.015 3.015 0 0 1-.512-.607l-1.115-.098a.25.25 0 0 1-.222-.193l-.144-.626a.25.25 0 0 1 .115-.27l.898-.54c.013-.334.08-.653.193-.95zM6.789 8.023A12.845 12.845 0 0 0 6 8c-5.036 0-6 2.74-6 4.48C0 14.22.076 15 6 15c.553 0 1.055-.006 1.51-.02A5.977 5.977 0 0 1 6 11c0-1.083.287-2.1.79-2.977zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM12 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="admin" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.162 2.5a3.5 3.5 0 0 1-3.163 5.479L6.08 14.766a1.5 1.5 0 0 1-2.598-1.5L7.4 6.479A3.5 3.5 0 0 1 10.564 1L8.9 3.88l2.599 1.5 1.663-2.88zm-8.63 11.949a.5.5 0 1 0 .5-.866.5.5 0 0 0-.5.866z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.414 7.95l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 1 0 1.414-1.415L10.414 7.95zm-7 0l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 0 0 1.414-1.415L3.414 7.95z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.536 7.95L1.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 1 1-1.414-1.415L5.536 7.95zm7 0L8.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.414-1.415l4.243-4.242z"/></symbol><symbol viewBox="0 0 16 16" id="angle-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 10.243l-4.95-4.95a1 1 0 0 0-1.414 1.414l5.657 5.657a.997.997 0 0 0 1.414 0l5.657-5.657a1 1 0 0 0-1.414-1.414L8 10.243z"/></symbol><symbol viewBox="0 0 16 16" id="angle-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.757 8l4.95-4.95a1 1 0 1 0-1.414-1.414L3.636 7.293a.997.997 0 0 0 0 1.414l5.657 5.657a1 1 0 0 0 1.414-1.414L5.757 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.243 8l-4.95-4.95a1 1 0 0 1 1.414-1.414l5.657 5.657a.997.997 0 0 1 0 1.414l-5.657 5.657a1 1 0 0 1-1.414-1.414L10.243 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 6.757l-4.95 4.95a1 1 0 1 1-1.414-1.414l5.657-5.657a.997.997 0 0 1 1.414 0l5.657 5.657a1 1 0 0 1-1.414 1.414L8 6.757z"/></symbol><symbol viewBox="0 0 16 16" id="appearance" xmlns="http://www.w3.org/2000/svg"><path d="M11.161 12.456l.232.121c.1.053.175.094.249.137.53.318.844.75.857 1.402.012 1.397-1.116 1.756-3.12 1.858a23.85 23.85 0 0 1-1.38.026A8 8 0 0 1 0 8a8 8 0 0 1 8-8c4.417 0 7.998 3.582 7.998 7.977.06 2.621-1.312 3.586-4.48 3.648-.602.008-1.068.043-1.4.104.228.192.598.47 1.043.727zm-3.287-.943c-.019-1.495 1.228-1.856 3.611-1.888C13.67 9.582 14.028 9.33 13.998 8A6 6 0 1 0 8 14c.603 0 .91-.004 1.277-.023a9.7 9.7 0 0 0 .478-.035c-1.172-.738-1.868-1.47-1.88-2.43zM6 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-2-3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM4 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="applications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 1v2h2V1H7zm0 5h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm0 1v2h2V7h-2zM1 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm0 1v2h2v-2H1zm6-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm6 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="approval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.536 10.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 1 1 9.12 9.243l1.415 1.414zM7.632 8.109A2 2 0 0 0 7 11.364l2.121 2.121a1.996 1.996 0 0 0 2.807.021C11.686 14.554 10.627 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.6 0 1.142.038 1.632.109zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-down" xmlns="http://www.w3.org/2000/svg"><path d="M10.472 7.282a.862.862 0 0 1 1.26-.006c.357.364.357.958 0 1.285L8.627 11.73A.886.886 0 0 1 8 12a.849.849 0 0 1-.627-.27L4.275 8.561a.904.904 0 0 1-.013-1.285.861.861 0 0 1 1.26-.007l2.486 2.527z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 6H2a2 2 0 1 0 0 4h7v2.586a1 1 0 0 0 1.707.707l4.586-4.586a1 1 0 0 0 0-1.414l-4.586-4.586A1 1 0 0 0 9 3.414V6z"/></symbol><symbol viewBox="0 0 16 16" id="assignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 5V4a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V7h-1a1 1 0 0 1 0-2h1zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="bold" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4 12.5v-9A1.5 1.5 0 0 1 5.5 2h2.104c2.182 0 3.879.681 3.879 2.982 0 1.067-.517 2.227-1.374 2.595v.073C11.176 7.963 12 8.865 12 10.466 12 12.914 10.19 14 7.911 14H5.5A1.5 1.5 0 0 1 4 12.5zm2.376-5.696H7.49c1.164 0 1.665-.552 1.665-1.417 0-.94-.534-1.289-1.649-1.289h-1.13v2.706zm0 5.098h1.341c1.293 0 1.956-.515 1.956-1.62 0-1.049-.647-1.472-1.956-1.472H6.376v3.092z"/></symbol><symbol viewBox="0 0 16 16" id="book" xmlns="http://www.w3.org/2000/svg"><path d="M7 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2v4.191a.5.5 0 0 1-.724.447l-1.052-.526a.5.5 0 0 0-.448 0l-1.052.526A.5.5 0 0 1 7 6.191V2zM5 0h6a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="bookmark" xmlns="http://www.w3.org/2000/svg"><path d="M6.746 10.505a2 2 0 0 1 2.508 0L11 11.911V3H5v8.91l1.746-1.405zM5 1h6a2 2 0 0 1 2 2v10.999a1 1 0 0 1-1.627.779L8 12.064l-3.373 2.714A1 1 0 0 1 3 13.998V3a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="branch" xmlns="http://www.w3.org/2000/svg"><path d="M6 11.978v.29a2 2 0 1 1-2 0V3.732a2 2 0 1 1 2 0v3.849c.592-.491 1.31-.854 2.15-1.081 1.308-.353 1.875-.882 1.893-1.743a2 2 0 1 1 2.002-.051C12.053 6.54 10.857 7.84 8.67 8.43 7.056 8.867 6.195 9.98 6 11.978zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 15a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="bullhorn" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.143 10H7V4H3a3 3 0 1 0 0 6h.143l.734 5.141a1 1 0 0 0 .99.859h1.556a.5.5 0 0 0 .495-.57L6.143 10zM8 4c1.034.02 2.039-.274 3.014-.883.727-.455 1.836-1.334 3.328-2.637A1 1 0 0 1 16 1.233v10.764a1 1 0 0 1-1.595.803c-1.658-1.227-2.788-1.992-3.392-2.294-.781-.39-1.785-.559-3.013-.506V4z"/></symbol><symbol viewBox="0 0 16 16" id="calendar" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 2h2a2 2 0 0 1 2 2H0a2 2 0 0 1 2-2h2V1a1 1 0 1 1 2 0v1h4V1a1 1 0 1 1 2 0v1zM0 4h16v9a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4zm2 2.5V13a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6.5a.5.5 0 0 0-.5-.5h-11a.5.5 0 0 0-.5.5zM5 8h2a1 1 0 1 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="cancel" xmlns="http://www.w3.org/2000/svg"><path d="M3.11 4.523a6 6 0 0 0 8.367 8.367L3.109 4.524zM4.522 3.11l8.368 8.368A6 6 0 0 0 4.524 3.11zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 16 16" id="chart" xmlns="http://www.w3.org/2000/svg"><path d="M15 14a1 1 0 0 1 0 2H2a2 2 0 0 1-2-2V1a1 1 0 1 1 2 0v13h13zM3.142 8.735l2.502-2.561a.5.5 0 0 1 .714-.003L8 7.833l3.592-4.553a.5.5 0 0 1 .796.015l2.516 3.454a.5.5 0 0 1 .096.295V12.5a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5V9.085a.5.5 0 0 1 .142-.35z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.078 8.2l3.535-3.536a2 2 0 0 1 2.828 2.828l-4.949 4.95c-.39.39-.902.586-1.414.586a1.994 1.994 0 0 1-1.414-.586l-4.95-4.95a2 2 0 1 1 2.828-2.828l3.536 3.535z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.977 7.998l3.535-3.535a2 2 0 1 0-2.828-2.828l-4.95 4.949c-.39.39-.586.902-.586 1.414 0 .512.196 1.024.586 1.414l4.95 4.95a2 2 0 1 0 2.828-2.828L7.977 7.998z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.22 7.998L4.683 4.463a2 2 0 0 1 2.828-2.828l4.95 4.949c.39.39.586.902.586 1.414a1.99 1.99 0 0 1-.586 1.414l-4.95 4.95a2 2 0 0 1-2.828-2.828l3.535-3.536z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.778 8.957l3.535 3.535a2 2 0 1 0 2.828-2.828l-4.949-4.95a1.994 1.994 0 0 0-1.414-.586c-.512 0-1.024.196-1.414.586l-4.95 4.95a2 2 0 1 0 2.828 2.828l3.536-3.535z"/></symbol><symbol viewBox="0 0 16 16" id="clock" xmlns="http://www.w3.org/2000/svg"><path d="M9 7h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V5a1 1 0 1 1 2 0v2zm-1 9A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.414 8l4.95-4.95a1 1 0 0 0-1.414-1.414L8 6.586l-4.95-4.95A1 1 0 0 0 1.636 3.05L6.586 8l-4.95 4.95a1 1 0 1 0 1.414 1.414L8 9.414l4.95 4.95a1 1 0 1 0 1.414-1.414L9.414 8z"/></symbol><symbol viewBox="0 0 16 16" id="code" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.871 8.243a.997.997 0 0 0-.293-.707L12.75 4.707a1 1 0 0 0-1.414 1.414l2.12 2.122-2.12 2.121a1 1 0 0 0 1.414 1.414l2.828-2.828a.997.997 0 0 0 .293-.707zm-13.243 0L4.75 6.12a1 1 0 1 0-1.414-1.414L.507 7.536a.997.997 0 0 0 0 1.414l2.829 2.828a1 1 0 1 0 1.414-1.414L2.628 8.243zm6.407-4.107a1 1 0 0 1 .707 1.225L8.19 11.157a1 1 0 1 1-1.931-.518L7.81 4.843a1 1 0 0 1 1.224-.707z"/></symbol><symbol viewBox="0 0 9 13" id="collapse"><path d="M.084.25C.01.18-.015.12.008.071.031.024.093 0 .194 0h8.521c.1 0 .162.024.185.072.023.048-.002.107-.075.177l-4.11 3.935a.372.372 0 0 1-.11.072h-.301a.508.508 0 0 1-.11-.072L.084.249zM.377 6.88a.364.364 0 0 1-.26-.105.334.334 0 0 1-.11-.25v-.709c0-.096.036-.179.11-.249a.364.364 0 0 1 .26-.105h8.15c.101 0 .188.035.261.105.074.07.11.153.11.25v.709c0 .096-.036.179-.11.249a.364.364 0 0 1-.26.105H.377zM.084 12.132c-.074.07-.099.129-.076.177.023.048.085.072.186.072h8.521c.1 0 .162-.024.185-.072.023-.048-.002-.107-.075-.177l-4.11-3.935a.372.372 0 0 0-.11-.072h-.301a.508.508 0 0 0-.11.072l-4.11 3.935z"/></symbol><symbol viewBox="0 0 16 16" id="comment" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comment-dots" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586zM5 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="comment-next" xmlns="http://www.w3.org/2000/svg"><path d="M8 5V4a.5.5 0 0 1 .8-.4l2.667 2a.5.5 0 0 1 0 .8L8.8 8.4A.5.5 0 0 1 8 8V7H6a1 1 0 1 1 0-2h2zM1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comments" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.75 10L0 13V3a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3.75zM13 5h1a2 2 0 0 1 2 2v8l-2.667-2H8a2 2 0 0 1-2-2h4a3 3 0 0 0 3-3V5z"/></symbol><symbol viewBox="0 0 16 16" id="commit" xmlns="http://www.w3.org/2000/svg"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3.876-1.008a4.002 4.002 0 0 1-7.752 0A1.01 1.01 0 0 1 4 9H1a1 1 0 1 1 0-2h3c.042 0 .083.003.124.008a4.002 4.002 0 0 1 7.752 0A1.01 1.01 0 0 1 12 7h3a1 1 0 0 1 0 2h-3a1.01 1.01 0 0 1-.124-.008z"/></symbol><symbol viewBox="0 0 16 16" id="credit-card" xmlns="http://www.w3.org/2000/svg"><path d="M14 5a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1h12zm0 3H2v3a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V8zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm6.5 8h3a.5.5 0 1 1 0 1h-3a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="cut" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="2" y="7" fill-rule="evenodd" rx="1"/></symbol><symbol viewBox="0 0 16 16" id="dashboard" xmlns="http://www.w3.org/2000/svg"><path d="M7.709 10.021l.696-2.6a.5.5 0 0 1 .966.26l-.657 2.45A2 2 0 0 1 10 12H6a2 2 0 0 1 1.709-1.979zM0 8.9a8 8 0 0 1 15.998 0H16v3.6a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5V8.9zM14 9A6 6 0 1 0 2 9v3.5a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 .5-.5V9zM3.5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm9 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-7-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm5 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1z"/></symbol><symbol viewBox="0 0 16 16" id="disk" xmlns="http://www.w3.org/2000/svg"><path d="M16 11.764V3a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v8.764A2.989 2.989 0 0 1 2 11V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v8c.768 0 1.47.289 2 .764zM2 12h12a2 2 0 1 1 0 4H2a2 2 0 1 1 0-4zm10 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="doc_code" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zm1.036 7.607a.498.498 0 0 1-.147.354l-1.414 1.414a.5.5 0 0 1-.707-.707l1.06-1.06-1.06-1.061a.5.5 0 0 1 .707-.707l1.414 1.414a.498.498 0 0 1 .147.353zm-4.822 0l1.06 1.061a.5.5 0 0 1-.706.707l-1.414-1.414a.498.498 0 0 1 0-.707l1.414-1.414a.5.5 0 1 1 .707.707l-1.06 1.06zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="doc_image" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM7.333 9.667l1.313-1.313a.5.5 0 0 1 .708 0L12 11H4l2.188-1.75a.5.5 0 0 1 .624 0l.521.417zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 8a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4 11h8v.7a.3.3 0 0 1-.3.3H4.3a.3.3 0 0 1-.3-.3V11z"/></symbol><symbol viewBox="0 0 16 16" id="doc_text" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 11h5a.5.5 0 1 1 0 1h-5a.5.5 0 1 1 0-1zm0-2h5a.5.5 0 1 1 0 1h-5a.5.5 0 0 1 0-1zm0-2h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/></symbol><symbol viewBox="0 0 105 26" id="double-headed-arrow" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.018 11.089L15.138.614c1.23-.911 3.086-.795 4.147.26.461.46.715 1.045.715 1.651v20.95C20 24.869 18.684 26 17.06 26a3.238 3.238 0 0 1-1.921-.614L1.019 14.911C-.212 14-.347 12.405.714 11.35c.094-.094.195-.18.303-.261zm102.964 0c.108.08.21.167.303.26 1.061 1.056.925 2.65-.303 3.562l-14.12 10.475A3.238 3.238 0 0 1 87.94 26C86.316 26 85 24.87 85 23.475V2.525c0-.606.254-1.192.715-1.65 1.061-1.056 2.917-1.172 4.146-.26l14.12 10.474zM35 17a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="download" xmlns="http://www.w3.org/2000/svg"><path d="M9 12h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0l-2-2.667A.5.5 0 0 1 6 12h1V8a1 1 0 1 1 2 0v4zM4 9a1 1 0 1 1 0 2 4 4 0 0 1-1.971-7.481 4 4 0 0 1 6.633-2.505 3.999 3.999 0 0 1 3.82 2.014A4 4 0 0 1 12 11a1 1 0 0 1 0-2 2 2 0 1 0 0-4h-1a2 2 0 0 0-3.112-1.662A2 2 0 1 0 4.268 5H4a2 2 0 1 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M14 10h-3a1 1 0 0 1-1-1V6H8.527A.527.527 0 0 0 8 6.527V13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-3zm-4-7H8.527c-.18 0-.355.013-.527.04V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2v2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3zM8.527 4h2.323a.5.5 0 0 1 .35.143l4.65 4.551a.5.5 0 0 1 .15.357V13a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V6.527A2.527 2.527 0 0 1 8.527 4z"/></symbol><symbol viewBox="0 0 16 16" id="earth" xmlns="http://www.w3.org/2000/svg"><path d="M8.7 2.04l-.082.177c.283.223.422.413.417.571-.008.237-.311.057-.444.274-.133.218.038.542-.112.637-.15.096-.398-.386-.479-.46-.054-.049-.166-.257-.336-.625l-.216-.225a.844.844 0 0 0-.418-.035c-.177.038-.075.1-.035.132.04.032.32.037.452.2.132.164.03.224-.05.298-.054.05-.157.062-.31.035H5.952l-.402.398.03.325.229.455.324-.463c.008-.206.058-.342.15-.41.14-.1.342-.15.534-.085.191.066-.057.218.011.271.068.053.204-.098.313-.02.11.08.07.155.104.322.036.167.254.114.398.328.144.215.19.29.147.483-.043.195-.168.26-.305.232-.138-.028-.107-.246-.275-.348-.168-.102-.266-.114-.386-.054-.12.06-.016.129.023.235.04.106.274.321.224.43-.05.107-.108.116-.42 0-.21-.077-.414-.007-.615.212l-.76.722c-.153.715-.3 1.13-.44 1.243-.211.17-.177-.483-.483-.656-.306-.174-.494-.047-.8-.07-.307-.023-.42.65-.38.873a.434.434 0 0 0 .221.321c.236-.141.39-.184.465-.128.11.084-.144.267-.074.425.07.158.314.069.386.283.073.213.084.48-.05.706-.135.227-.275.178-.4.053-.127-.126-.033-.375-.255-.704-.223-.329-.381-.337-.63-.787-.158-.287-.35-.743-.575-1.366a6 6 0 0 0 3.21 7.198l.001-.075c0-.577-.004-.944-.012-1.102-.011-.236-.95-.945-1.104-1.2-.154-.256-.34-.595-.355-.746-.016-.151.185-.232.344-.325.16-.093-.11-.367.028-.626.137-.258.395-.438.496-.356.101.081.058.228.267.333.209.104.077-.213.456-.178.38.035.143.201.252.216.11.016.113-.127.299-.143.186-.015.282.445.471.622.19.178.452.008.611.043.159.034.267.09.402.255.136.166-.03.352.073.557.103.205 1.07.22 1.433.255.364.034.371.011.371.324s-.166.314-.453.507c-.286.193-.166.462-.38.762-.212.3-.316.062-.622.14-.306.077-.413.382-.452.568-.039.186-.386.094-.877.232-.29.082-.429.144-.569.204a6.002 6.002 0 0 0 7.682-4.3c-.094-.384-.18-.63-.258-.74-.213-.297-.36.21-.924.49-.564.278-.57-.288-.81-.49-.16-.133-.212-.44-.158-.92-.005-.478.02-.828.077-1.049.057-.221.126-.543.207-.965.351-.373.606-.572.764-.595.237-.034.336.374.658.3a.315.315 0 0 0 .035-.01 5.993 5.993 0 0 0-.475-.824l-.309-.043a.646.646 0 0 0-.332-.117c-.205-.02-.025.128-.089.24-.064.112-.235.724-.437.685-.201-.039-.204-.374-.17-.668.036-.294-.077-.35-.2-.412-.124-.062-.325-.213-.556-.295-.232-.082-.123-.175-.093-.274.03-.1.208-.015.193-.058-.014-.044-.313-.135-.266-.167.03-.02.2-.02.506.003l.216-.012.293-.163a.58.58 0 0 0-.376-.22c-.233-.036-.513-.034-.73-.142-.205-.103-.458-.36-.643-.638A5.965 5.965 0 0 0 8.7 2.04zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 1600 1600" id="ellipsis_v" xmlns="http://www.w3.org/2000/svg"><path d="M1088 1248v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V224q0-40 28-68t68-28h192q40 0 68 28t28 68z"/></symbol><symbol viewBox="0 0 18 18" id="emoji_slightly_smiling_face" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369.721.721 0 0 1 .568.047.715.715 0 0 1 .37.445 2.91 2.91 0 0 0 1.084 1.518A2.93 2.93 0 0 0 9 12.75a2.93 2.93 0 0 0 1.775-.58 2.913 2.913 0 0 0 1.084-1.518.711.711 0 0 1 .375-.445.737.737 0 0 1 .575-.047c.195.063.34.186.433.37.094.183.11.372.047.568zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smile" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568zM14 6.37c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm-6.5 0c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm9 2.63a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smiley" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568h.001zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6c.92.397 1.91.6 2.912.598a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39c.397-.92.6-1.91.598-2.912zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z"/></symbol><symbol viewBox="0 0 16 16" id="epic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.985 8.044l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637A2 2 0 0 0 1.618 9h11.661a2 2 0 0 0 1.706-.956zm0 3l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637a2 2 0 0 0 .576.084h11.661a2 2 0 0 0 1.706-.956zM3.618 2h10.995a1 1 0 0 1 .948 1.316l-1.333 4a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l2-4A1 1 0 0 1 3.618 2zm-.382 4h9.322l.667-2H4.236l-1 2z"/></symbol><symbol viewBox="0 0 16 16" id="external-link" xmlns="http://www.w3.org/2000/svg"><path d="M13.121 4.177l-4.95 4.95a1 1 0 1 1-1.414-1.414l4.95-4.95-1.386-1.386a.5.5 0 0 1 .299-.85l4.709-.524a.5.5 0 0 1 .552.552l-.523 4.71a.5.5 0 0 1-.851.297l-1.386-1.385zM12 8.884a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3v-8a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-4z"/></symbol><symbol viewBox="0 0 16 16" id="eye" xmlns="http://www.w3.org/2000/svg"><path d="M8 14C4.816 14 2.253 12.284.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2s5.747 1.716 7.607 5.019a2 2 0 0 1 0 1.962C13.747 12.284 11.184 14 8 14zm0-2c2.41 0 4.338-1.29 5.864-4C12.338 5.29 10.411 4 8 4 5.59 4 3.662 5.29 2.136 8 3.662 10.71 5.589 12 8 12zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm1-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="eye-slash" xmlns="http://www.w3.org/2000/svg"><path d="M13.618 2.62L1.62 14.619a1 1 0 0 1-.985-1.668l1.525-1.526C1.516 10.742.926 9.927.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2c1.074 0 2.076.195 3.006.58l.944-.944a1 1 0 0 1 1.668.985zM8.068 11a3 3 0 0 0 2.931-2.932l-2.931 2.931zm-3.02-2.462a3 3 0 0 1 3.49-3.49l.884-.884A6.044 6.044 0 0 0 8 4C5.59 4 3.662 5.29 2.136 8c.445.79.924 1.46 1.439 2.011l1.473-1.473zm.421 5.06l1.658-1.658c.283.04.575.06.873.06 2.41 0 4.338-1.29 5.864-4a11.023 11.023 0 0 0-1.133-1.664l1.418-1.418a12.799 12.799 0 0 1 1.458 2.1 2 2 0 0 1 0 1.963C13.747 12.284 11.184 14 8 14a7.883 7.883 0 0 1-2.53-.402z"/></symbol><symbol viewBox="0 0 16 16" id="file-addition" xmlns="http://www.w3.org/2000/svg"><path d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3z"/></symbol><symbol viewBox="0 0 16 16" id="file-deletion" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm2 6h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="file-modified" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm5 4a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/></symbol><symbol viewBox="0 0 16 16" id="filter" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 6v9l-3.724-1.862A.5.5 0 0 1 6 12.691V6L1.854 1.854A.5.5 0 0 1 2.207 1h11.586a.5.5 0 0 1 .353.854L10 6z"/></symbol><symbol viewBox="0 0 16 16" id="folder" xmlns="http://www.w3.org/2000/svg"><path d="M13 3a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="folder-o" xmlns="http://www.w3.org/2000/svg"><path d="M13 5l-4.365-.005a2 2 0 0 1-1.882-1.33A1 1 0 0 0 5.81 3H2v9a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1zm0-2a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="folder-open" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 6H5.333a2 2 0 0 0-1.664.89l-3.333 5a2 2 0 0 0-.285.662A3.017 3.017 0 0 1 0 12V3a2 2 0 0 1 2-2h2.838a3 3 0 0 1 2.828 2H11a3 3 0 0 1 3 3zM5.333 7h9.53a1 1 0 0 1 .875 1.486l-2.492 4.485A2 2 0 0 1 11.498 14H2a1 1 0 0 1-.832-1.555l3.333-5A1 1 0 0 1 5.333 7z"/></symbol><symbol viewBox="0 0 16 16" id="fork" xmlns="http://www.w3.org/2000/svg"><path d="M9 12.268a2 2 0 1 1-2 0V8.874A4.002 4.002 0 0 1 4 5V3.732a2 2 0 1 1 2 0V5a2 2 0 1 0 4 0V3.732a2 2 0 1 1 2 0V5a4.002 4.002 0 0 1-3 3.874v3.394zM11 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm3 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="geo-nodes" xmlns="http://www.w3.org/2000/svg"><path d="M9.7 13.1l-.2.2c-.7.8-2 .9-2.8.1-.1 0-.1-.1-.1-.1l-.2-.2c-2 .2-3.4.7-3.4 1.4 0 .8 2.2 1.5 5 1.5s5-.7 5-1.5c0-.7-1.4-1.2-3.3-1.4M7.3 12.7c.4.4 1 .3 1.4-.1C11.6 9.5 13 7 13 5.3 13 2.4 10.8 0 8 0S3 2.4 3 5.3C3 7 4.4 9.5 7.3 12.7M8 2c1.6 0 3 1.4 3 3.3 0 1-1 2.8-3 5.2-2-2.4-3-4.2-3-5.2C5 3.4 6.4 2 8 2"/><circle cx="8" cy="5" r="1"/></symbol><symbol viewBox="0 0 16 16" id="git-merge" xmlns="http://www.w3.org/2000/svg"><path d="M11 12.268V5a1 1 0 0 0-1-1v1a.5.5 0 0 1-.8.4l-2.667-2a.5.5 0 0 1 0-.8L9.2.6a.5.5 0 0 1 .8.4v1a3 3 0 0 1 3 3v7.268a2 2 0 1 1-2 0zm-6 0a2 2 0 1 1-2 0V4.732a2 2 0 1 1 2 0v7.536zM4 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm8 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="group" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.048 11.997C-.377 11.975.013 11.782.013 10.56.013 9.235.653 8 4 8c.444 0 .84.022 1.194.062.164.435.426.82.76 1.132-1.786.389-2.721 1.353-2.906 2.803zm2.94-7.222a2.993 2.993 0 0 0-.976 1.95 2 2 0 1 1 .975-1.95zm6.964 7.222c-.185-1.45-1.12-2.414-2.906-2.803.334-.311.596-.697.76-1.132C11.16 8.022 11.556 8 12 8c3.346 0 3.987 1.235 3.987 2.56 0 1.222.39 1.415-3.035 1.437zm-1.964-5.272a2.993 2.993 0 0 0-.976-1.95 2 2 0 1 1 .976 1.95zM8 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 5c-2.177 0-3.987-.115-3.987-1.44S4.653 10 8 10c3.346 0 3.987 1.235 3.987 2.56S10.177 14 8 14z"/></symbol><symbol viewBox="0 0 16 16" id="history" xmlns="http://www.w3.org/2000/svg"><path d="M2.868 3.24a7 7 0 1 1-.043 9.475 1 1 0 0 1 1.478-1.348 5 5 0 1 0 .124-6.865l.796.645a.5.5 0 0 1-.193.873l-3.232.814a.5.5 0 0 1-.622-.504L1.3 3a.5.5 0 0 1 .814-.37l.754.61zM9 8h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V6a1 1 0 1 1 2 0v2z"/></symbol><symbol viewBox="0 0 16 16" id="home" xmlns="http://www.w3.org/2000/svg"><path d="M9 13h3v-3H4v3h3v-1a1 1 0 0 1 2 0v1zm5-3v3.659c0 .729-.657 1.341-1.5 1.341h-9c-.843 0-1.5-.612-1.5-1.341V10h-.88C.502 10 0 9.486 0 8.853c0-.307.12-.601.333-.816l6.405-6.463a1.56 1.56 0 0 1 2.374-.052L15.66 8.03c.444.441.455 1.167.024 1.622a1.108 1.108 0 0 1-.804.348H14zM7.95 3.273l-4.595 4.64h9.264l-4.67-4.64z"/></symbol><symbol viewBox="0 0 16 16" id="hook" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1h4zm0 1H6v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V4zM7 8a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3v4a2 2 0 1 0 4 0h-.44a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H15a4 4 0 0 1-7 2.646A4 4 0 0 1 1 12H.56a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H3a2 2 0 1 0 4 0V8z"/></symbol><symbol viewBox="0 0 16 16" id="hourglass" xmlns="http://www.w3.org/2000/svg"><path d="M10.331 4.889A2.988 2.988 0 0 0 11 3V2H5v1c0 .362.064.709.182 1.03l5.15.859zM3 14v-1c0-1.78.93-3.342 2.33-4.228.447-.327.67-.582.67-.764 0-.19-.242-.46-.725-.815A4.996 4.996 0 0 1 3 3V2H2a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2h-1v1a4.997 4.997 0 0 1-2.39 4.266c-.407.3-.61.545-.61.734 0 .19.203.434.61.734A4.997 4.997 0 0 1 13 13v1h1a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2h1zm8 0v-1a3 3 0 0 0-6 0v1h6z"/></symbol><symbol viewBox="0 0 38 38" id="image-comment-dark" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#1F78D1"/><path fill="#FFF" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></symbol><symbol viewBox="0 0 38 38" id="image-comment-light" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#FFF"/><path fill="#1F78D1" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></symbol><symbol viewBox="0 0 16 16" id="import" xmlns="http://www.w3.org/2000/svg"><path d="M9 8h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0L5.6 8.8A.5.5 0 0 1 6 8h1V1a1 1 0 1 1 2 0v7zM0 8a1 1 0 1 1 2 0 6 6 0 1 0 12 0 1 1 0 0 1 2 0A8 8 0 1 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-block" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.803 8a5.97 5.97 0 0 0-.462 1H4.5a.5.5 0 0 1 0-1h1.303zM4.5 5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1zm7.5.083a6.04 6.04 0 0 0-2 0V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.083a5.96 5.96 0 0 0 .72 2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v2.083zm1.121 3.796zM11 16a5 5 0 1 1 0-10 5 5 0 0 1 0 10zm-1.293-2.292a3 3 0 0 0 4.001-4.001l-4.001 4zm-1.415-1.415l4.001-4a3 3 0 0 0-4.001 4.001z"/></symbol><symbol viewBox="0 0 16 16" id="issue-child" xmlns="http://www.w3.org/2000/svg"><path d="M11 8H5v1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2V7a.997.997 0 0 1 1-1h3V4H4.5a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9v2h3a.997.997 0 0 1 1 1v2h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h1V8zm-9 3v2h3v-2H2zm9 0v2h3v-2h-3z"/></symbol><symbol viewBox="0 0 16 16" id="issue-close" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M10.874 2H12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-2c-.918 0-1.74-.413-2.29-1.063a3.987 3.987 0 0 0 1.988-.984A1 1 0 0 0 10 14h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-1V3c0-.345-.044-.68-.126-1zM4 0h3a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-external" xmlns="http://www.w3.org/2000/svg"><path d="M11 4a5.99 5.99 0 0 0-2 .341V3a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h2.528a6.003 6.003 0 0 0 2.705 1.736A2.99 2.99 0 0 1 8 16H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3v1zM8.212 8.97l-.568-.876A.25.25 0 0 1 7.66 7.8l.404-.5a.25.25 0 0 1 .284-.076l.938.36c.256-.182.543-.325.85-.42l.323-.988a.25.25 0 0 1 .237-.173h.643a.25.25 0 0 1 .238.173l.321.989c.308.094.595.237.852.418l.937-.359a.25.25 0 0 1 .284.076l.404.5a.25.25 0 0 1 .016.293l-.568.875c.113.297.18.616.192.95l.9.54a.25.25 0 0 1 .114.27l-.145.627a.25.25 0 0 1-.221.192l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.282a.25.25 0 0 1-.29-.051l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.905a.25.25 0 0 1-.29.05l-.577-.281a.25.25 0 0 1-.138-.26L9 12.254a3.015 3.015 0 0 1-.512-.607l-1.114-.098a.25.25 0 0 1-.222-.192l-.145-.627a.25.25 0 0 1 .115-.27l.899-.54c.012-.334.08-.653.192-.95zm2.806 2.034a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="issue-new" xmlns="http://www.w3.org/2000/svg"><path d="M10 2V1a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V4H9a1 1 0 1 1 0-2h1zm0 6a1 1 0 0 1 2 0v5a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h1a1 1 0 1 1 0 2H5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm0-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open-m" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-parent" xmlns="http://www.w3.org/2000/svg"><path d="M11 11H5v1h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H3v-2a.997.997 0 0 1 1-1h3V7H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H9v2h3a.997.997 0 0 1 1 1v2h2.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H11v-1zM6 3v2h4V3H6z"/></symbol><symbol viewBox="0 0 16 16" id="issues" xmlns="http://www.w3.org/2000/svg"><path d="M10.458 15.012l.311.055a3 3 0 0 0 3.476-2.433l1.389-7.879A3 3 0 0 0 13.2 1.28L11.23.933a3.002 3.002 0 0 0-.824-.031c.364.59.58 1.28.593 2.02l1.854.328a1 1 0 0 1 .811 1.158l-1.389 7.879a1 1 0 0 1-1.158.81l-.118-.02a3.98 3.98 0 0 1-.541 1.935zM3 0h4a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="italic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.5 12l2-8H6a1 1 0 1 1 0-2h6a1 1 0 0 1 0 2h-1.5l-2 8H10a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2h1.5z"/></symbol><symbol viewBox="0 0 16 16" id="key" xmlns="http://www.w3.org/2000/svg"><path d="M7.575 6.689a4.002 4.002 0 0 1 6.274-4.86 4 4 0 0 1-4.86 6.274l-2.21 2.21.706.708a1 1 0 1 1-1.414 1.414l-.707-.707-.707.707.707.707a1 1 0 1 1-1.414 1.414l-.707-.707a1 1 0 0 1-1.414-1.414l5.746-5.746zm2.032-.618a2 2 0 1 0 2.828-2.828A2 2 0 0 0 9.607 6.07z"/></symbol><symbol viewBox="0 0 16 16" id="key-2" xmlns="http://www.w3.org/2000/svg"><path d="M5.172 14.157l-.344.344-2.485.133a.462.462 0 0 1-.497-.503l.14-2.24a.599.599 0 0 1 .177-.382l5.155-5.155a4 4 0 1 1 2.828 2.828l-1.439 1.44-1.06-.354-.708.707.354 1.06-.707.708-1.06-.354-.708.707.354 1.06zm6.01-8.839a1 1 0 1 0 1.414-1.414 1 1 0 0 0-1.414 1.414z"/></symbol><symbol viewBox="0 0 16 16" id="label" xmlns="http://www.w3.org/2000/svg"><path d="M11.782 14.718a3 3 0 0 1-4.242 0L1.652 8.829a2 2 0 0 1-.565-1.702l.54-3.703a2 2 0 0 1 1.69-1.69l3.703-.54a2 2 0 0 1 1.703.564l5.888 5.888a3 3 0 0 1 0 4.243l-2.829 2.829zm1.415-5.657L7.309 3.173l-3.703.54-.54 3.702 5.888 5.888a1 1 0 0 0 1.414 0l2.829-2.828a1 1 0 0 0 0-1.414zM5.732 5.525A1 1 0 1 1 7.146 6.94a1 1 0 0 1-1.414-1.414z"/></symbol><symbol viewBox="0 0 16 16" id="labels" xmlns="http://www.w3.org/2000/svg"><path d="M9.424 2.254l2.08-.905a1 1 0 0 1 1.206.326l3.013 4.12a1 1 0 0 1 .16.849l-1.947 7.264a3 3 0 0 1-3.675 2.122l-.5-.135a3.999 3.999 0 0 0 1.082-1.782 1 1 0 0 0 1.16-.722l1.823-6.802-2.258-3.087-.687.299a2 2 0 0 0-.628-.88l-.829-.667zM.377 3.7L4.4.498a1 1 0 0 1 1.25.003L9.627 3.7a1 1 0 0 1 .373.78V13a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4.482A1 1 0 0 1 .377 3.7zM2 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V4.958L5.02 2.561 2 4.964V13zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="leave" xmlns="http://www.w3.org/2000/svg"><path d="M11 7V5.883a.5.5 0 0 1 .757-.429l3.528 2.117a.5.5 0 0 1 0 .858l-3.528 2.117a.5.5 0 0 1-.757-.43V9H7a1 1 0 1 1 0-2h4zm-2 6.256a1 1 0 0 1 2 0A2.744 2.744 0 0 1 8.256 16H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h5.19A2.81 2.81 0 0 1 11 2.81a1 1 0 0 1-2 0A.81.81 0 0 0 8.19 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h5.256c.41 0 .744-.333.744-.744z"/></symbol><symbol viewBox="0 0 16 16" id="level-up" xmlns="http://www.w3.org/2000/svg"><path fill="#2E2E2E" fill-rule="evenodd" d="M7 6h3.489a.5.5 0 0 0 .373-.832L6.374.117a.5.5 0 0 0-.748 0l-4.488 5.05A.5.5 0 0 0 1.51 6H5v7a3 3 0 0 0 3 3h6a1 1 0 0 0 0-2H8a1 1 0 0 1-1-1V6z"/></symbol><symbol viewBox="0 0 16 16" id="license" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12.56 8.9l2.66 4.606a.3.3 0 0 1-.243.45l-1.678.094a.1.1 0 0 0-.078.044l-.953 1.432a.3.3 0 0 1-.51-.016L9.097 10.9a5.994 5.994 0 0 0 3.464-2zm-5.23 2.063L4.707 15.51a.3.3 0 0 1-.51.016l-.953-1.432a.1.1 0 0 0-.078-.044l-1.678-.094a.3.3 0 0 1-.243-.45l2.48-4.297a5.983 5.983 0 0 0 3.607 1.754zM8 10A5 5 0 1 1 8 0a5 5 0 0 1 0 10zm0-2a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="link" xmlns="http://www.w3.org/2000/svg"><path d="M6.986 3.35l2.12-2.122a4 4 0 0 1 5.657 5.657l-2.828 2.829a4 4 0 0 1-5.657 0 1 1 0 0 1 1.414-1.415 2 2 0 0 0 2.829 0l2.828-2.828a2 2 0 1 0-2.828-2.828l-1.001 1a5.018 5.018 0 0 0-2.534-.294zm2.12 9.192l-2.12 2.121a4 4 0 1 1-5.658-5.656l2.829-2.829a4 4 0 0 1 5.657 0 1 1 0 1 1-1.415 1.414 2 2 0 0 0-2.828 0l-2.828 2.829a2 2 0 1 0 2.828 2.828l1.001-1.001a5.018 5.018 0 0 0 2.534.294z"/></symbol><symbol viewBox="0 0 16 16" id="list-bulleted" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-7h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 5h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm-4 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-2h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="list-numbered" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 2h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2zM1.156 5v-.828h.816V2.204h-.72v-.636c.432-.084.708-.192.996-.372h.756v2.976h.684V5H1.156zm-.18 5v-.588c.9-.828 1.596-1.464 1.596-1.98 0-.342-.192-.504-.468-.504-.252 0-.444.18-.624.36l-.552-.552c.396-.42.756-.612 1.32-.612.768 0 1.308.492 1.308 1.248 0 .612-.576 1.284-1.092 1.812.192-.024.468-.048.636-.048h.636V10H.976zm1.26 5.072c-.618 0-1.068-.204-1.356-.54l.468-.648c.234.216.51.36.78.36.336 0 .552-.12.552-.36 0-.288-.15-.456-.948-.456v-.72c.636 0 .828-.168.828-.432 0-.228-.138-.348-.396-.348-.252 0-.432.108-.672.312l-.516-.624c.372-.312.768-.492 1.236-.492.84 0 1.38.384 1.38 1.074 0 .366-.204.642-.612.822v.024c.432.132.732.432.732.912 0 .72-.684 1.116-1.476 1.116z"/></symbol><symbol viewBox="0 0 16 16" id="location" xmlns="http://www.w3.org/2000/svg"><path d="M8.755 15.144a1 1 0 0 1-1.51 0C3.748 11.114 2 8.065 2 6a6 6 0 1 1 12 0c0 2.065-1.748 5.113-5.245 9.144zM12 6a4 4 0 1 0-8 0c0 1.314 1.312 3.71 4 6.944C10.688 9.71 12 7.314 12 6zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="location-dot" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.314 13.087C4.382 13.295 3 13.85 3 14.5c0 .828 2.239 1.5 5 1.5s5-.672 5-1.5c0-.65-1.382-1.205-3.314-1.413l-.202.225a2 2 0 0 1-2.968 0l-.202-.225zm2.428-.445a1 1 0 0 1-1.484 0C4.419 9.5 3 7.037 3 5.252 3 2.353 5.239 0 8 0s5 2.352 5 5.253c0 1.784-1.42 4.247-4.258 7.389zM11 5.252C11 3.436 9.634 2 8 2S5 3.435 5 5.253c0 1.027.974 2.824 3 5.203 2.026-2.38 3-4.176 3-5.203zM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="lock" xmlns="http://www.w3.org/2000/svg"><path d="M10 5V4h2v1a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V8a3 3 0 0 1 3-3V4h2v1h4zM4 7a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H4zm0-3a4 4 0 1 1 8 0h-2a2 2 0 1 0-4 0H4z"/></symbol><symbol viewBox="0 0 16 16" id="lock-open" xmlns="http://www.w3.org/2000/svg"><path d="M4.044 4a4 4 0 0 1 6.99-2.658 1 1 0 1 1-1.495 1.33A2 2 0 0 0 6.044 4a.998.998 0 0 1-.07.367v.701H12a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3v-5a3 3 0 0 1 2.974-3V4h.07zM4 7.07a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="log" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4zm1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-5h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm0 3h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm-3 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-2h3a1 1 0 0 1 0 2H8a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="mail" xmlns="http://www.w3.org/2000/svg"><path d="M14 5.6L9.338 9.796a2 2 0 0 1-2.676 0L2 5.6V11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5.6zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm.212 2L8 8.31 12.788 4H3.212z"/></symbol><symbol viewBox="0 0 16 16" id="menu" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.143 2h13.714C15.488 2 16 2.448 16 3s-.512 1-1.143 1H1.143C.512 4 0 3.552 0 3s.512-1 1.143-1zm0 5h13.714C15.488 7 16 7.448 16 8s-.512 1-1.143 1H1.143C.512 9 0 8.552 0 8s.512-1 1.143-1zm0 5h13.714c.631 0 1.143.448 1.143 1s-.512 1-1.143 1H1.143C.512 14 0 13.552 0 13s.512-1 1.143-1z"/></symbol><symbol viewBox="0 0 16 16" id="merge-request-close" xmlns="http://www.w3.org/2000/svg"><path d="M9.414 8l1.414 1.414a1 1 0 1 1-1.414 1.414L8 9.414l-1.414 1.414a1 1 0 1 1-1.414-1.414L6.586 8 5.172 6.586a1 1 0 1 1 1.414-1.414L8 6.586l1.414-1.414a1 1 0 1 1 1.414 1.414L9.414 8zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="messages" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-.98-1.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM4.464 2.464L5.88 3.88a3 3 0 0 0 0 4.242L4.464 9.536a5 5 0 0 1 0-7.072zm7.072 7.072L10.12 8.12a3 3 0 0 0 0-4.242l1.415-1.415a5 5 0 0 1 0 7.072zM2.343.343l1.414 1.414a6 6 0 0 0 0 8.486l-1.414 1.414a8 8 0 0 1 0-11.314zm11.314 11.314l-1.414-1.414a6 6 0 0 0 0-8.486L13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="mobile-issue-close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.657 10.728L2.12 7.192A1 1 0 1 0 .707 8.607l4.243 4.242a.997.997 0 0 0 1.414 0l8.485-8.485a1 1 0 1 0-1.414-1.414l-7.778 7.778z"/></symbol><symbol viewBox="0 0 16 16" id="monitor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 13v1h3a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2h3v-1H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3h-3zM3 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm5.723 6.416l-2.66-1.773-1.71 1.71a.5.5 0 1 1-.707-.707l2-2a.5.5 0 0 1 .631-.062l2.66 1.773 2.71-2.71a.5.5 0 0 1 .707.707l-3 3a.5.5 0 0 1-.631.062z"/></symbol><symbol viewBox="0 0 16 16" id="more" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="notifications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 14H2.435a2 2 0 0 1-1.761-2.947c.962-1.788 1.521-3.065 1.68-3.832.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024c3.755.528 4.375 4.27 4.761 6.043.188.86.742 2.188 1.661 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0zm5.805-6.468c-.325-1.492-.37-1.674-.61-2.288C10.6 3.716 9.742 3 8.07 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.208 1.012-.827 2.424-1.877 4.375H13.64c-.993-1.937-1.6-3.396-1.835-4.468z"/></symbol><symbol viewBox="0 0 16 16" id="notifications-off" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.26 5.089c.243.757.382 1.478.5 2.017.187.86.74 2.188 1.66 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0H4.35l2-2h7.29c-.993-1.937-1.6-3.396-1.835-4.468-.07-.326-.129-.59-.178-.81l1.634-1.633zM10.943 1.75l-1.48 1.48C9.07 3.076 8.612 3 8.069 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.065.317-.17.673-.317 1.073L.45 12.242a1.99 1.99 0 0 1 .224-1.19c.962-1.787 1.521-3.064 1.68-3.831.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024 4.867 4.867 0 0 1 1.944.688zm2.932-.105a1 1 0 0 1 0 1.415L2.561 14.374a1 1 0 1 1-1.415-1.414L12.46 1.646a1 1 0 0 1 1.414 0z"/></symbol><symbol viewBox="0 0 16 16" id="overview" xmlns="http://www.w3.org/2000/svg"><path d="M2 0h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2h-3zM2 9h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3h-3z"/></symbol><symbol viewBox="0 0 16 16" id="pencil" xmlns="http://www.w3.org/2000/svg"><path d="M13.02 1.293l1.414 1.414a1 1 0 0 1 0 1.414L4.119 14.436a1 1 0 0 1-.704.293l-2.407.008L1 12.316a1 1 0 0 1 .293-.71L11.605 1.292a1 1 0 0 1 1.414 0zm-1.416 1.415l-.707.707L12.31 4.83l.707-.707-1.414-1.415zM3.411 13.73l1.123-1.122H3.12v-1.415L2 12.312l.005 1.422 1.406-.005z"/></symbol><symbol viewBox="0 0 16 16" id="pencil-square" xmlns="http://www.w3.org/2000/svg"><path d="M12 9a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V9zm.778-7.179l1.414 1.415-6.476 6.476a1 1 0 0 1-.498.27l-1.51.325.323-1.512a1 1 0 0 1 .27-.497l6.477-6.477zM15.607.407a1 1 0 0 1 0 1.414l-.708.707-1.414-1.414.707-.707a1 1 0 0 1 1.415 0z"/></symbol><symbol viewBox="0 0 16 16" id="pipeline" xmlns="http://www.w3.org/2000/svg"><path d="M8.969 7.25a2 2 0 1 1-1.938 0A1.002 1.002 0 0 1 7 7V5.083a.2.2 0 0 1 .06-.142l.877-.87a.1.1 0 0 1 .141 0l.864.87A.2.2 0 0 1 9 5.083V7c0 .086-.01.17-.031.25zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm4.5-4a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zM8 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="play" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.765 15.835c-.545.321-1.258.159-1.593-.363A1.075 1.075 0 0 1 1 14.89V1.11C1 .496 1.518 0 2.158 0c.214 0 .424.057.607.165l11.684 6.89c.544.321.714 1.005.38 1.526a1.135 1.135 0 0 1-.38.364l-11.684 6.89z"/></symbol><symbol viewBox="0 0 16 16" id="plus" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7H2a1 1 0 1 0 0 2h5v5a1 1 0 0 0 2 0V9h5a1 1 0 0 0 0-2H9V2a1 1 0 1 0-2 0v5z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 7V4a1 1 0 1 0-2 0v3H4a1 1 0 1 0 0 2h3v3a1 1 0 0 0 2 0V9h3a1 1 0 0 0 0-2H9zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square-o" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="podcast" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862a1 1 0 0 1-.785 1.177A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-1-1 1 1 0 0 1 .02-.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 7.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4.464 2.464A1 1 0 0 1 5.88 3.88a3 3 0 0 0 0 4.242 1 1 0 0 1-1.415 1.415 5 5 0 0 1 0-7.072zm7.072 7.072A1 1 0 0 1 10.12 8.12a3 3 0 0 0 0-4.242 1 1 0 0 1 1.415-1.415 5 5 0 0 1 0 7.072zM2.343.343a1 1 0 1 1 1.414 1.414 6 6 0 0 0 0 8.486 1 1 0 1 1-1.414 1.414 8 8 0 0 1 0-11.314zm11.314 11.314a1 1 0 1 1-1.414-1.414 6 6 0 0 0 0-8.486A1 1 0 0 1 13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="preferences" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 12h10a1 1 0 0 1 0 2H5a1 1 0 0 1-2 0v-2a1 1 0 0 1 2 0zm-3 0H1a1 1 0 0 0 0 2h1v-2zm11-5h2a1 1 0 0 1 0 2h-2a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0zm-3 0H1a1 1 0 1 0 0 2h9V7zM6 2h9a1 1 0 0 1 0 2H6a1 1 0 1 1-2 0V2a1 1 0 1 1 2 0zM3 2H1a1 1 0 1 0 0 2h2V2z"/></symbol><symbol viewBox="0 0 16 16" id="profile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-4.274-3.404C4.412 9.709 5.694 9 8 9c2.313 0 3.595.7 4.28 1.586A4.997 4.997 0 0 1 8 13a4.997 4.997 0 0 1-4.274-2.404zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="project" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177l-.038.044a.505.505 0 0 0 .038-.044zm-.787 0a.5.5 0 0 0 .038.043l-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="push-rules" xmlns="http://www.w3.org/2000/svg"><path d="M6.268 9a2 2 0 0 1 3.464 0H11a1 1 0 0 1 0 2H9.732a2 2 0 0 1-3.464 0H5a1 1 0 0 1 0-2h1.268zM7 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1v3.515a.3.3 0 0 1-.434.268l-1.432-.716a.3.3 0 0 0-.268 0l-1.432.716A.3.3 0 0 1 7 5.515V2zM4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm4 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="question" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm-1.46-5.602h2.233a3.97 3.97 0 0 1 .051-.558c.029-.17.073-.326.133-.469.06-.143.14-.28.242-.41.102-.13.228-.263.38-.399.26-.24.504-.467.733-.683a5.03 5.03 0 0 0 .598-.668c.17-.23.302-.477.399-.742a2.66 2.66 0 0 0 .144-.907c0-.505-.083-.95-.25-1.335a2.55 2.55 0 0 0-.723-.97 3.2 3.2 0 0 0-1.152-.589 5.441 5.441 0 0 0-1.531-.2c-.516 0-.998.063-1.445.188a3.19 3.19 0 0 0-1.168.59c-.331.268-.594.61-.79 1.027-.195.417-.295.917-.3 1.5h2.64c.006-.224.04-.416.102-.578.062-.161.142-.293.238-.394a.921.921 0 0 1 .332-.227 1.04 1.04 0 0 1 .39-.074c.34 0 .593.095.763.285.169.19.254.488.254.895 0 .328-.106.63-.317.906-.21.276-.499.565-.863.867-.214.182-.39.374-.531.574-.141.2-.253.42-.336.657a3.656 3.656 0 0 0-.176.777 7.89 7.89 0 0 0-.05.937zm-.321 2.375c0 .188.035.362.105.524.07.161.17.3.301.418.13.117.284.21.46.277.178.068.376.102.595.102.218 0 .416-.034.593-.102.178-.068.331-.16.461-.277a1.2 1.2 0 0 0 .301-.418c.07-.162.106-.336.106-.524a1.3 1.3 0 0 0-.106-.523 1.2 1.2 0 0 0-.3-.418 1.461 1.461 0 0 0-.462-.277 1.651 1.651 0 0 0-.593-.102c-.22 0-.417.034-.594.102a1.46 1.46 0 0 0-.461.277 1.2 1.2 0 0 0-.3.418 1.284 1.284 0 0 0-.106.523z"/></symbol><symbol viewBox="0 0 16 16" id="question-o" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-.778-4.151c0-.301.014-.575.044-.82a3.2 3.2 0 0 1 .154-.68c.073-.208.17-.4.294-.575.123-.176.278-.343.465-.503a4.81 4.81 0 0 0 .755-.758c.185-.242.277-.506.277-.793 0-.356-.074-.617-.222-.783-.148-.166-.37-.25-.667-.25a.92.92 0 0 0-.342.065.806.806 0 0 0-.29.199 1.04 1.04 0 0 0-.209.345 1.5 1.5 0 0 0-.088.506H5.082c.005-.51.092-.948.263-1.313.171-.364.401-.664.69-.899.29-.234.63-.406 1.023-.516a4.66 4.66 0 0 1 1.264-.164c.497 0 .944.058 1.34.174.397.117.733.289 1.008.517.276.227.487.51.633.847.146.337.218.727.218 1.17 0 .295-.042.56-.126.792a2.52 2.52 0 0 1-.349.65 4.4 4.4 0 0 1-.523.584c-.2.19-.414.389-.642.598a2.73 2.73 0 0 0-.332.349c-.089.114-.16.233-.212.359a1.868 1.868 0 0 0-.116.41 3.39 3.39 0 0 0-.044.489H7.222zm-.28 2.078c0-.164.03-.317.092-.458a1.05 1.05 0 0 1 .263-.366c.114-.103.248-.183.403-.243a1.45 1.45 0 0 1 .52-.089c.191 0 .364.03.52.09.154.059.289.14.403.242.114.103.201.224.263.366.061.141.092.294.092.458 0 .164-.03.316-.092.458a1.05 1.05 0 0 1-.263.365 1.278 1.278 0 0 1-.404.243 1.43 1.43 0 0 1-.52.089c-.19 0-.364-.03-.519-.089-.155-.06-.29-.14-.403-.243a1.05 1.05 0 0 1-.263-.365 1.135 1.135 0 0 1-.093-.458z"/></symbol><symbol viewBox="0 0 16 16" id="quote" xmlns="http://www.w3.org/2000/svg"><path d="M15 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9h-2a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1zM7 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="redo" xmlns="http://www.w3.org/2000/svg"><path d="M4.625 4.423A4.897 4.897 0 0 1 8.079 3c2.73 0 4.944 2.239 4.944 5s-2.214 5-4.944 5c-1.41 0-2.723-.6-3.655-1.633a.98.98 0 0 0-1.397-.066 1.008 1.008 0 0 0-.064 1.413A6.87 6.87 0 0 0 8.079 15C11.9 15 15 11.866 15 8s-3.099-7-6.921-7A6.866 6.866 0 0 0 3.08 3.158L1.833 2.137a.49.49 0 0 0-.695.074.504.504 0 0 0-.11.311L1 7.26a.497.497 0 0 0 .6.492l4.576-1.013a.5.5 0 0 0 .206-.877L4.625 4.423z"/></symbol><symbol viewBox="0 0 16 16" id="remove" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2v10a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V3zm3-2a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1H5zM4 3v10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3H4zm2.5 2a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm3 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 16 16" id="repeat" xmlns="http://www.w3.org/2000/svg"><path d="M11.375 4.423A4.897 4.897 0 0 0 7.921 3c-2.73 0-4.944 2.239-4.944 5s2.214 5 4.944 5c1.41 0 2.723-.6 3.655-1.633a.98.98 0 0 1 1.397-.066c.403.373.432 1.005.064 1.413A6.87 6.87 0 0 1 7.921 15C4.1 15 1 11.866 1 8s3.099-7 6.921-7c1.915 0 3.706.792 4.999 2.158l1.247-1.021a.49.49 0 0 1 .695.074c.07.088.11.198.11.311L15 7.26a.497.497 0 0 1-.6.492L9.824 6.739a.5.5 0 0 1-.206-.877l1.757-1.439z"/></symbol><symbol viewBox="0 0 16 16" id="retry" xmlns="http://www.w3.org/2000/svg"><path d="M4.114 6.958a4 4 0 0 0 5.283 4.775 1 1 0 1 1 .712 1.87A6 6 0 0 1 2.182 6.44l-.741-.2a.5.5 0 0 1-.12-.915l2.195-1.268a.5.5 0 0 1 .683.183l1.268 2.196a.5.5 0 0 1-.563.733l-.79-.212zm7.777 2.084a4 4 0 0 0-5.284-4.775 1 1 0 0 1-.712-1.87 6 6 0 0 1 7.927 7.162l.742.2a.5.5 0 0 1 .12.915l-2.196 1.268a.5.5 0 0 1-.683-.183l-1.267-2.196a.5.5 0 0 1 .562-.733l.79.212z"/></symbol><symbol viewBox="0 0 16 16" id="scale" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.99 9a.792.792 0 0 0-.078-.231L13 7l-.912 1.769a.791.791 0 0 0-.077.231h1.978zm-10 0a.792.792 0 0 0-.078-.231L3 7l-.912 1.769A.791.791 0 0 0 2.011 9h1.978zM2 0h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm3 14h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zM8 4a1 1 0 0 1 1 1v9H7V5a1 1 0 0 1 1-1zm-4.53-.714l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 3 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158L2.53 3.286a.53.53 0 0 1 .94 0zm10 0l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 13 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158l2.266-4.735a.53.53 0 0 1 .94 0z"/></symbol><symbol viewBox="0 0 16 16" id="screen-full" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 14v-2a1 1 0 0 1 2 0v3a.997.997 0 0 1-1 1h-3a1 1 0 0 1 0-2h2zM2 14v-2a1 1 0 0 0-2 0v3a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2H2zM15.707.293A.997.997 0 0 1 16 1v3a1 1 0 0 1-2 0V2h-2a1 1 0 0 1 0-2h3c.276 0 .526.112.707.293zM2 2v2a1 1 0 1 1-2 0V1a.997.997 0 0 1 1-1h3a1 1 0 1 1 0 2H2zm4 4h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="screen-normal" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 3V1a1 1 0 1 1 2 0v3a.997.997 0 0 1-1 1H1a1 1 0 1 1 0-2h2zm10 0h2a1 1 0 0 1 0 2h-3a.997.997 0 0 1-1-1V1a1 1 0 0 1 2 0v2zM3 13H1a1 1 0 0 1 0-2h3a.997.997 0 0 1 1 1v3a1 1 0 0 1-2 0v-2zm10 0v2a1 1 0 0 1-2 0v-3a.997.997 0 0 1 1-1h3a1 1 0 0 1 0 2h-2zM6.5 7h3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 12 16" id="scroll_down" xmlns="http://www.w3.org/2000/svg"><path class="fbfirst-triangle" d="M1.048 14.155a.508.508 0 0 0-.32.105c-.091.07-.136.154-.136.25v.71c0 .095.045.178.135.249.09.07.197.105.321.105h10.043a.51.51 0 0 0 .321-.105c.09-.07.136-.154.136-.25v-.71c0-.095-.045-.178-.136-.249a.508.508 0 0 0-.32-.105"/><path class="fbsecond-triangle" d="M.687 8.027c-.09-.087-.122-.16-.093-.22.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 12.91a.458.458 0 0 1-.136.089h-.37a.626.626 0 0 1-.136-.09"/><path class="fbthird-triangle" d="M.687 1.027C.597.94.565.867.594.807c.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 5.91a.458.458 0 0 1-.136.09h-.37a.626.626 0 0 1-.136-.09"/></symbol><symbol viewBox="0 0 12 16" id="scroll_up" xmlns="http://www.w3.org/2000/svg"><path d="M1.048 1.845a.508.508 0 0 1-.32-.105c-.091-.07-.136-.154-.136-.25V.78c0-.095.045-.178.135-.249a.508.508 0 0 1 .321-.105h10.043a.51.51 0 0 1 .321.105c.09.07.136.154.136.25v.71c0 .095-.045.178-.136.249a.508.508 0 0 1-.32.105M.687 7.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 3.09A.458.458 0 0 0 6.257 3h-.37a.626.626 0 0 0-.136.09M.687 14.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 10.09a.458.458 0 0 0-.136-.09h-.37a.626.626 0 0 0-.136.09"/></symbol><symbol viewBox="0 0 16 16" id="search" xmlns="http://www.w3.org/2000/svg"><path d="M8.853 8.854a3.5 3.5 0 1 0-4.95-4.95 3.5 3.5 0 0 0 4.95 4.95zm.207 2.328a5.5 5.5 0 1 1 2.121-2.121l3.329 3.328a1.5 1.5 0 0 1-2.121 2.121L9.06 11.182z"/></symbol><symbol viewBox="0 0 16 16" id="settings" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.415 5.803L1.317 4.084A.5.5 0 0 1 1.35 3.5l.805-.994a.5.5 0 0 1 .564-.153l1.878.704a5.975 5.975 0 0 1 1.65-.797L6.885.342A.5.5 0 0 1 7.36 0h1.28a.5.5 0 0 1 .474.342l.639 1.918a5.97 5.97 0 0 1 1.65.797l1.877-.704a.5.5 0 0 1 .565.153l.805.994a.5.5 0 0 1 .032.584l-1.097 1.719c.217.551.354 1.143.399 1.76l1.731 1.058a.5.5 0 0 1 .227.54l-.288 1.246a.5.5 0 0 1-.44.385l-2.008.19a6.026 6.026 0 0 1-1.142 1.431l.265 1.995a.5.5 0 0 1-.277.516l-1.15.56a.5.5 0 0 1-.576-.1l-1.424-1.452a6.047 6.047 0 0 1-1.804 0l-1.425 1.453a.5.5 0 0 1-.576.1l-1.15-.561a.5.5 0 0 1-.276-.516l.265-1.995a6.026 6.026 0 0 1-1.143-1.43l-2.008-.191a.5.5 0 0 1-.44-.385L.058 9.16a.5.5 0 0 1 .226-.539l1.732-1.058a5.968 5.968 0 0 1 .399-1.76zM8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="shield" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8c1.657 0 3 1.373 3 3.067v7.346c0 1.065-.54 2.053-1.426 2.611l-4 2.52a2.944 2.944 0 0 1-3.148 0l-4-2.52A3.083 3.083 0 0 1 1 10.414V3.066C1 1.373 2.343 0 4 0zm0 2.045c-.552 0-1 .457-1 1.022v7.346c0 .355.18.685.475.87l4 2.52a.981.981 0 0 0 1.05 0l4-2.52c.295-.185.475-.515.475-.87V3.067c0-.565-.448-1.022-1-1.022H4zm0 1.533c0-.282.224-.511.5-.511h4V12.1a.52.52 0 0 1-.069.258.494.494 0 0 1-.684.183l-3.5-2.098a.513.513 0 0 1-.247-.44V3.577z"/></symbol><symbol viewBox="0 0 16 16" id="slight-frown" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-2.163-3.275a2.499 2.499 0 0 1 4.343.03.5.5 0 0 1-.871.49 1.5 1.5 0 0 0-2.607-.018.5.5 0 1 1-.865-.502zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="slight-smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-5.163 2.254a.5.5 0 1 1 .865-.502 1.499 1.499 0 0 0 2.607-.018.5.5 0 1 1 .871.49 2.499 2.499 0 0 1-4.343.03z"/></symbol><symbol viewBox="0 0 16 16" id="smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM6.18 6.27a.5.5 0 0 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zm6 0a.5.5 0 1 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zM5 9a3 3 0 0 0 6 0H5z"/></symbol><symbol viewBox="0 0 16 16" id="smiley" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM5 9h6a3 3 0 0 1-6 0z"/></symbol><symbol viewBox="0 0 16 16" id="snippet" xmlns="http://www.w3.org/2000/svg"><path d="M10.67 9.31a3.001 3.001 0 0 1 2.062 5.546 3 3 0 0 1-3.771-4.559 1.007 1.007 0 0 1-.095-.137l-4.5-7.794a1 1 0 0 1 1.732-1l4.5 7.794c.028.05.052.1.071.15zm-3.283.35l-.289.5c-.028.05-.06.095-.095.137a3.001 3.001 0 0 1-3.77 4.56A3 3 0 0 1 5.294 9.31c.02-.051.043-.102.071-.15l.866-1.5 1.155 2zm2.31-4l-1.156-2 1.325-2.294a1 1 0 0 1 1.732 1L9.696 5.66zm-5.465 7.464a1 1 0 1 0 1-1.732 1 1 0 0 0-1 1.732zm7.5 0a1 1 0 1 0-1-1.732 1 1 0 0 0 1 1.732z"/></symbol><symbol viewBox="0 0 16 16" id="spam" xmlns="http://www.w3.org/2000/svg"><path d="M8.75.433l5.428 3.134a1.5 1.5 0 0 1 .75 1.299v6.268a1.5 1.5 0 0 1-.75 1.299L8.75 15.567a1.5 1.5 0 0 1-1.5 0l-5.428-3.134a1.5 1.5 0 0 1-.75-1.299V4.866a1.5 1.5 0 0 1 .75-1.299L7.25.433a1.5 1.5 0 0 1 1.5 0zM3.072 5.155v5.69L8 13.691l4.928-2.846v-5.69L8 2.309 3.072 5.155zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 14 14" id="spinner" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="7" cy="7" r="6" stroke="#000" stroke-opacity=".1" stroke-width="2"/><path fill="#000" fill-opacity=".1" fill-rule="nonzero" d="M7 0a7 7 0 0 1 7 7h-2a5 5 0 0 0-5-5V0z"/></g></symbol><symbol viewBox="0 0 16 16" id="star" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.609 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 16 16" id="star-o" xmlns="http://www.w3.org/2000/svg"><path d="M10.975 10.99a3 3 0 0 1 .655-2.083l1.54-1.916-2.219-.576a3 3 0 0 1-1.825-1.37L8 3.15 6.874 5.044a3 3 0 0 1-1.825 1.371l-2.218.576 1.54 1.916a3 3 0 0 1 .654 2.083l-.165 2.4 1.965-.836a3 3 0 0 1 2.348 0l1.965.836-.164-2.399zM7.61 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 14 14" id="status_canceled" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M5.2 3.8l4.9 4.9c.2.2.2.5 0 .7l-.7.7c-.2.2-.5.2-.7 0L3.8 5.2c-.2-.2-.2-.5 0-.7l.7-.7c.2-.2.5-.2.7 0"/></g></symbol><symbol viewBox="0 0 22 22" id="status_canceled_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M8.171 5.971l7.7 7.7a.76.76 0 0 1 0 1.1l-1.1 1.1a.76.76 0 0 1-1.1 0l-7.7-7.7a.76.76 0 0 1 0-1.1l1.1-1.1a.76.76 0 0 1 1.1 0"/></symbol><symbol viewBox="0 0 16 16" id="status_closed" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.83a1 1 0 0 1 1.414 1.416l-3.535 3.535a1 1 0 0 1-1.415.001l-2.12-2.12a1 1 0 1 1 1.413-1.415zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 14 14" id="status_created" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><circle cx="7" cy="7" r="3.25"/></g></symbol><symbol viewBox="0 0 22 22" id="status_created_borderless" xmlns="http://www.w3.org/2000/svg"><circle cx="11" cy="11" r="5.107"/></symbol><symbol viewBox="0 0 14 14" id="status_failed" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 5.969L5.599 4.568a.29.29 0 0 0-.413.004l-.614.614a.294.294 0 0 0-.004.413L5.968 7l-1.4 1.401a.29.29 0 0 0 .004.413l.614.614c.113.114.3.117.413.004L7 8.032l1.401 1.4a.29.29 0 0 0 .413-.004l.614-.614a.294.294 0 0 0 .004-.413L8.032 7l1.4-1.401a.29.29 0 0 0-.004-.413l-.614-.614a.294.294 0 0 0-.413-.004L7 5.968z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_failed_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 9.38L8.798 7.178a.455.455 0 0 0-.65.006l-.964.965a.462.462 0 0 0-.006.65L9.38 11l-2.202 2.202a.455.455 0 0 0 .006.65l.965.964a.462.462 0 0 0 .65.006L11 12.62l2.202 2.202a.455.455 0 0 0 .65-.006l.964-.965a.462.462 0 0 0 .006-.65L12.62 11l2.202-2.202a.455.455 0 0 0-.006-.65l-.965-.964a.462.462 0 0 0-.65-.006L11 9.38z"/></symbol><symbol viewBox="0 0 14 14" id="status_manual" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M10.5 7.63V6.37l-.787-.13c-.044-.175-.132-.349-.263-.61l.481-.652-.918-.913-.657.478a2.346 2.346 0 0 0-.612-.26L7.656 3.5H6.388l-.132.783c-.219.043-.394.13-.612.26l-.657-.478-.918.913.437.652c-.131.218-.175.392-.262.61l-.744.086v1.261l.787.13c.044.218.132.392.263.61l-.438.651.92.913.655-.434c.175.086.394.173.613.26l.131.783h1.313l.131-.783c.219-.043.394-.13.613-.26l.656.478.918-.913-.48-.652c.13-.218.218-.435.262-.61l.656-.13zM7 8.283a1.285 1.285 0 0 1-1.313-1.305c0-.739.57-1.304 1.313-1.304.744 0 1.313.565 1.313 1.304 0 .74-.57 1.305-1.313 1.305z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_manual_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M16.5 11.99v-1.98l-1.238-.206c-.068-.273-.206-.546-.412-.956l.756-1.025-1.444-1.435-1.03.752a3.686 3.686 0 0 0-.963-.41L12.03 5.5h-1.994l-.206 1.23c-.343.068-.618.205-.962.41l-1.031-.752-1.444 1.435.687 1.025c-.206.341-.275.615-.412.956L5.5 9.941v1.981l1.237.205c.07.342.207.615.413.957l-.688 1.025 1.444 1.434 1.032-.683c.274.137.618.274.962.41l.206 1.23h2.063l.206-1.23c.344-.068.619-.205.963-.41l1.03.752 1.444-1.435-.756-1.025c.207-.341.344-.683.413-.956l1.031-.205zM11 13.017c-1.169 0-2.063-.889-2.063-2.05 0-1.162.894-2.05 2.063-2.05s2.063.888 2.063 2.05c0 1.161-.894 2.05-2.063 2.05z"/></symbol><symbol viewBox="0 0 22 22" id="status_notfound_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M12.822 11.29c.816-.581 1.421-1.348 1.683-2.322.603-2.243-.973-4.553-3.53-4.553-1.15 0-2.085.41-2.775 1.089-.42.413-.672.835-.8 1.167a1.179 1.179 0 0 0 2.2.847c.016-.043.1-.184.252-.334.264-.259.613-.412 1.123-.412.938 0 1.47.78 1.254 1.584-.105.39-.37.726-.773 1.012a3.25 3.25 0 0 1-.945.47 1.179 1.179 0 0 0-.874 1.138v2.234a1.179 1.179 0 1 0 2.358 0v-1.43a5.9 5.9 0 0 0 .827-.492z"/><ellipse cx="10.825" cy="16.711" rx="1.275" ry="1.322"/></symbol><symbol viewBox="0 0 14 14" id="status_open" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><path d="M7 9.219a2.218 2.218 0 1 0 0-4.436A2.218 2.218 0 0 0 7 9.22zm0 1.12a3.338 3.338 0 1 1 0-6.676 3.338 3.338 0 0 1 0 6.676z"/></symbol><symbol viewBox="0 0 14 14" id="status_pending" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M4.7 5.3c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H5c-.2 0-.3-.1-.3-.3V5.3m3 0c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H8c-.2 0-.3-.1-.3-.3V5.3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_pending_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M7.386 8.329c0-.315.157-.472.471-.472h1.414c.315 0 .472.157.472.472v5.342c0 .315-.157.472-.472.472H7.857c-.314 0-.471-.157-.471-.472V8.33m4.714 0c0-.315.157-.472.471-.472h1.415c.314 0 .471.157.471.472v5.342c0 .315-.157.472-.471.472H12.57c-.314 0-.471-.157-.471-.472V8.33"/></symbol><symbol viewBox="0 0 14 14" id="status_running" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 3c2.2 0 4 1.8 4 4s-1.8 4-4 4c-1.3 0-2.5-.7-3.3-1.7L7 7V3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_running_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 4.714c3.457 0 6.286 2.829 6.286 6.286 0 3.457-2.829 6.286-6.286 6.286-2.043 0-3.929-1.1-5.186-2.672L11 11V4.714"/></symbol><symbol viewBox="0 0 14 14" id="status_skipped" xmlns="http://www.w3.org/2000/svg"><path d="M7 14A7 7 0 1 1 7 0a7 7 0 0 1 0 14z"/><path d="M7 13A6 6 0 1 0 7 1a6 6 0 0 0 0 12z" fill="#FFF"/><path d="M6.415 7.04L4.579 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L5.341 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L6.415 7.04zm2.54 0L7.119 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L7.881 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L8.955 7.04z"/></symbol><symbol viewBox="0 0 22 22" id="status_skipped_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M14.072 11.063l-2.82 2.82a.46.46 0 0 0-.001.652l.495.495a.457.457 0 0 0 .653-.001l3.7-3.7a.46.46 0 0 0 .001-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.479-3.479a.464.464 0 0 0-.654.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/><path d="M10.08 11.063l-2.819 2.82a.46.46 0 0 0-.002.652l.496.495a.457.457 0 0 0 .652-.001l3.7-3.7a.46.46 0 0 0 .002-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.48-3.479a.464.464 0 0 0-.653.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/></symbol><symbol viewBox="0 0 14 14" id="status_success" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_success_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.866 12.095l-1.95-1.95a.462.462 0 0 0-.647.01l-.964.964a.46.46 0 0 0-.01.646l3.013 3.014a.787.787 0 0 0 1.106.008l.425-.425 4.854-4.853a.462.462 0 0 0 .002-.659l-.964-.964a.468.468 0 0 0-.658.002l-4.207 4.207z"/></symbol><symbol viewBox="0 0 14 14" id="status_success_solid" xmlns="http://www.w3.org/2000/svg"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7zm6.278.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 14 14" id="status_warning" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6 3.5c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v4c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-4m0 6c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v1c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-1"/></g></symbol><symbol viewBox="0 0 22 22" id="status_warning_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.429 5.5c0-.471.314-.786.785-.786h1.572c.471 0 .785.315.785.786v6.286c0 .471-.314.785-.785.785h-1.572c-.471 0-.785-.314-.785-.785V5.5m0 9.429c0-.472.314-.786.785-.786h1.572c.471 0 .785.314.785.786V16.5c0 .471-.314.786-.785.786h-1.572c-.471 0-.785-.315-.785-.786v-1.571"/></symbol><symbol viewBox="0 0 16 16" id="stop" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 0h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="task-done" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="template" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm.8 2h2.4a.8.8 0 0 1 .8.8v1.4a.8.8 0 0 1-.8.8H3.8a.8.8 0 0 1-.8-.8V4.8a.8.8 0 0 1 .8-.8zm4.7 0h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm0 2h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm-5 3h9a.5.5 0 1 1 0 1h-9a.5.5 0 0 1 0-1zm0 2h9a.5.5 0 1 1 0 1h-9a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="terminal" xmlns="http://www.w3.org/2000/svg"><path d="M7 8a.997.997 0 0 1-.293.707l-1.414 1.414a1 1 0 1 1-1.414-1.414L4.586 8l-.707-.707a1 1 0 1 1 1.414-1.414l1.414 1.414A.997.997 0 0 1 7 8zM4 0h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H4zm5 7h2a1 1 0 0 1 0 2H9a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 11h5.282a2 2 0 0 0 1.963-2.38l-.563-2.905a3 3 0 0 0-.243-.732l-1.103-2.286A3 3 0 0 0 10.964 1H7a3 3 0 0 0-3 3v6.3a2 2 0 0 0 .436 1.247l3.11 3.9a.632.632 0 0 0 .941.053l.137-.137a1 1 0 0 0 .28-.87L8.329 11zM1 10h2V3H1a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 5h5.282a2 2 0 0 1 1.963 2.38l-.563 2.905a3 3 0 0 1-.243.732l-1.103 2.286A3 3 0 0 1 10.964 15H7a3 3 0 0 1-3-3V5.7a2 2 0 0 1 .436-1.247l3.11-3.9A.632.632 0 0 1 8.487.5l.137.137a1 1 0 0 1 .28.87L8.329 5zM1 6h2v7H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="thumbtack" xmlns="http://www.w3.org/2000/svg"><path d="M7.125 9h-2.19a.5.5 0 0 1-.417-.777L6 6V2L5.362.724A.5.5 0 0 1 5.809 0h4.382a.5.5 0 0 1 .447.724L10 2v4l1.482 2.223a.5.5 0 0 1-.416.777H8.875L8 16l-.875-7z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 16 16" id="timer" xmlns="http://www.w3.org/2000/svg"><path d="M12.022 3.27l.77-.77a1 1 0 0 1 1.415 1.414l-.728.729a7 7 0 1 1-1.456-1.372zM8 14A5 5 0 1 0 8 4a5 5 0 0 0 0 10zm0-9a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zM6 0h4a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-add" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 4V2a1 1 0 0 1 2 0v2h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0V6H8a1 1 0 1 1 0-2h2zm2 7a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-done" xmlns="http://www.w3.org/2000/svg"><path d="M8.243 7.485l4.95-4.95a1 1 0 1 1 1.414 1.415L8.95 9.607a.997.997 0 0 1-1.414 0L4.707 6.778a1 1 0 0 1 1.414-1.414l2.122 2.121zM12 11a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="token" xmlns="http://www.w3.org/2000/svg"><path d="M3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H3zm1 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="unapproval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.95 8.536l1.06-1.061a1 1 0 0 1 1.415 1.414l-1.061 1.06 1.06 1.061a1 1 0 0 1-1.414 1.415l-1.06-1.061-1.06 1.06a1 1 0 1 1-1.415-1.414l1.06-1.06-1.06-1.06a1 1 0 0 1 1.414-1.415l1.06 1.06zm-3.768-.33c.006.503.201 1.006.586 1.39l.353.354-.353.353a2 2 0 1 0 2.828 2.829l.354-.354.047.048C11.964 14.363 11.527 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.834 0 1.557.074 2.182.205zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="unassignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11 5h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="unlink" xmlns="http://www.w3.org/2000/svg"><path d="M11.295 8.845l-.659-1.664a1.78 1.78 0 0 0 .04-.04l1.415-1.414c.586-.586.654-1.468.152-1.97s-1.384-.434-1.97.152L8.859 5.323a1.781 1.781 0 0 0-.04.04l-1.664-.658c.141-.208.305-.408.491-.594l1.415-1.414c1.366-1.367 3.424-1.525 4.596-.354 1.171 1.172 1.013 3.23-.354 4.596L11.89 8.354c-.186.186-.386.35-.594.491zm-2.45 2.45a4.075 4.075 0 0 1-.491.594l-1.415 1.414c-1.366 1.367-3.424 1.525-4.596.354-1.171-1.172-1.013-3.23.354-4.596L4.11 7.646c.186-.186.386-.35.594-.491l.659 1.664a1.781 1.781 0 0 0-.04.04l-1.415 1.414c-.586.586-.654 1.468-.152 1.97s1.384.434 1.97-.152l1.414-1.414a1.78 1.78 0 0 0 .04-.04l1.664.658zm3.812-2.088h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-.05a.5.5 0 0 1 .5-.5zm-.384 2.116l1.415 1.414a.5.5 0 0 1 0 .708l-.037.036a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 0-.707l.036-.037a.5.5 0 0 1 .707 0zm-2.823 1.09a.5.5 0 0 1 .5-.5h.052a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9.95a.5.5 0 0 1-.5-.5v-2zm-2.748-9.16a.5.5 0 0 1-.5.5h-.05a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h.05a.5.5 0 0 1 .5.5v2zm-2.116.383a.5.5 0 0 1 0 .707l-.036.036a.5.5 0 0 1-.707 0L2.428 2.965a.5.5 0 0 1 0-.707l.037-.036a.5.5 0 0 1 .707 0l1.414 1.414zm-1.09 2.823h-2a.5.5 0 0 1-.5-.5v-.051a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5z"/></symbol><symbol viewBox="0 0 16 16" id="user" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 8c-6.888 0-6.976-.78-6.976-2.52S2.144 8 8 8s6.976 2.692 6.976 4.48c0 1.788-.088 2.52-6.976 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="users" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.521 8.01C15.103 8.19 16 10.755 16 12.48c0 1.533-.056 2.29-3.808 2.475.609-.54.808-1.331.808-2.475 0-1.911-.804-3.503-2.479-4.47zm-1.67-1.228A3.987 3.987 0 0 0 9.976 4a3.987 3.987 0 0 0-1.125-2.782 3 3 0 1 1 0 5.563zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="volume-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 5h1v6H1a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm2 0l4.445-2.964A1 1 0 0 1 9 2.87v10.26a1 1 0 0 1-1.555.833L3 11V5zm10.283 7.89a.5.5 0 0 1-.66-.752A5.485 5.485 0 0 0 14.5 8c0-1.601-.687-3.09-1.865-4.128a.5.5 0 0 1 .661-.75A6.484 6.484 0 0 1 15.5 8a6.485 6.485 0 0 1-2.217 4.89zm-2.002-2.236a.5.5 0 1 1-.652-.758c.55-.472.871-1.157.871-1.896 0-.732-.315-1.411-.856-1.883a.5.5 0 0 1 .658-.753A3.492 3.492 0 0 1 12.5 8c0 1.033-.45 1.994-1.219 2.654z"/></symbol><symbol viewBox="0 0 16 16" id="warning" xmlns="http://www.w3.org/2000/svg"><path d="M15.572 10.506c.867 1.42.375 3.247-1.098 4.082a3.184 3.184 0 0 1-1.57.412h-9.81C1.387 15 0 13.665 0 12.018a2.9 2.9 0 0 1 .427-1.512L5.332 2.47C6.2 1.05 8.096.577 9.57 1.412c.453.257.831.622 1.098 1.059l4.905 8.035zM8.89 3.479a1.014 1.014 0 0 0-.366-.353 1.053 1.053 0 0 0-1.412.353l-4.905 8.035a.967.967 0 0 0-.143.504c0 .549.462.994 1.032.994h9.81c.184 0 .364-.048.523-.137a.974.974 0 0 0 .366-1.361L8.889 3.479zM8 5a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zm0 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="work" xmlns="http://www.w3.org/2000/svg"><path d="M12 3h1a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h1V2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1zM6 2v1h4V2H6zM3 5a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H3zm1.5 1a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm7 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/job_not_triggered.svg b/app/assets/images/illustrations/job_not_triggered.svg new file mode 100644 index 00000000000..e13c1cb0a7d --- /dev/null +++ b/app/assets/images/illustrations/job_not_triggered.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 310 141" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="none" fill-rule="evenodd"><g fill-rule="nonzero"><path fill="#e5e5e5" d="M48 69c0-1.105.887-2 1.998-2h4c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.992 1.992 0 0 1 48 69m14 0c0-1.105.887-2 1.998-2h4c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.992 1.992 0 0 1 62 69"/><g fill="#31af64"><path d="M19 88C8.507 88 0 79.493 0 69s8.507-19 19-19 19 8.507 19 19-8.507 19-19 19m0-4c8.284 0 15-6.716 15-15 0-8.284-6.716-15-15-15-8.284 0-15 6.716-15 15 0 8.284 6.716 15 15 15"/><path d="M17.07 71.02l-2.829-2.828a1.995 1.995 0 0 0-2.828 0 1.997 1.997 0 0 0 0 2.83l4.243 4.243a1.993 1.993 0 0 0 2.823.005l7.79-7.79a1.998 1.998 0 0 0-.007-2.822 1.99 1.99 0 0 0-2.822-.006l-6.37 6.37v-.001"/></g></g><g transform="translate(187)"><rect width="116" height="134" y="7" fill="#f9f9f9" rx="10"/><rect width="116" height="134" x="5" y="2" fill="#fff" rx="10"/><path fill="#eee" fill-rule="nonzero" d="M15 4a8 8 0 0 0-8 8v114a8 8 0 0 0 8 8h96a8 8 0 0 0 8-8V12a8 8 0 0 0-8-8H15m0-4h96c6.627 0 12 5.373 12 12v114c0 6.627-5.373 12-12 12H15c-6.627 0-12-5.373-12-12V12C3 5.373 8.373 0 15 0"/><g transform="translate(23 25)"><g fill="#e1dbf1"><rect width="16" height="4" rx="2"/><rect width="16" height="4" x="32" y="12" rx="2"/></g><rect width="16" height="4" x="44" fill="#eee" rx="2"/><rect width="16" height="4" x="12" y="24" fill="#e1dbf1" rx="2"/><rect width="16" height="4" x="64" y="36" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="20" fill="#fee1d3" rx="2" id="a"/><rect width="8" height="4" x="32" y="36" fill="#fc6d26" rx="2"/><rect width="8" height="4" x="52" y="12" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="64" fill="#fef0e8" rx="2" id="b"/><rect width="12" height="4" x="16" y="48" fill="#e1dbf1" rx="2"/><rect width="8" height="4" x="44" y="36" fill="#fc6d26" rx="2"/><g fill="#e1dbf1"><rect width="4" height="4" x="56" y="36" rx="2"/><rect width="4" height="4" x="64" y="60" rx="2"/></g><rect width="4" height="4" x="72" y="60" fill="#fc6d26" rx="2"/><rect width="8" height="4" x="32" fill="#fc6d26" rx="2" id="c"/><g fill="#eee"><rect width="28" height="4" y="36" rx="2"/><rect width="28" height="4" x="44" y="48" rx="2"/></g><rect width="28" height="4" x="32" y="60" fill="#efedf8" rx="2"/><rect width="28" height="4" y="12" fill="#6b4fbb" rx="2"/><rect width="28" height="4" x="32" y="24" fill="#c3b8e3" rx="2"/><rect width="8" height="4" y="24" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="32" y="48" fill="#6b4fbb" rx="2"/><rect width="12" height="4" y="48" fill="#fc6d26" rx="2"/><g fill="#fef0e8"><rect width="12" height="4" y="60" rx="2"/><rect width="12" height="4" x="16" y="60" rx="2"/></g></g><g transform="translate(23 97)"><rect width="16" height="4" fill="#efedf8" rx="2"/><rect width="16" height="4" x="18" y="12" fill="#fc6d26" rx="2"/><rect width="16" height="4" x="44" fill="#6b4fbb" rx="2"/><use xlink:href="#a"/><rect width="8" height="4" x="38" y="12" fill="#fef0e8" rx="2"/><use xlink:href="#b"/><use xlink:href="#c"/><rect width="14" height="4" y="12" fill="#eee" rx="2"/></g></g><g fill-rule="nonzero"><path fill="#eee" d="M109 101a2 2 0 1 1 0-4c2.524 0 5-.346 7.379-1.02a2 2 0 0 1 1.091 3.849 31.007 31.007 0 0 1-8.47 1.172m18.09-5.825a31.174 31.174 0 0 0 6.187-5.899 2 2 0 1 0-3.131-2.489 27.133 27.133 0 0 1-5.393 5.142 2.001 2.001 0 0 0 2.337 3.247m11.297-15.288a30.923 30.923 0 0 0 1.576-8.407 2 2 0 1 0-3.996-.188 26.875 26.875 0 0 1-1.372 7.32 2 2 0 1 0 3.791 1.275m.283-18.89a30.855 30.855 0 0 0-3.593-7.763 2 2 0 1 0-3.362 2.166 26.905 26.905 0 0 1 3.128 6.757 2 2 0 0 0 3.828-1.16M127.875 45.41a30.973 30.973 0 0 0-7.435-4.228 2 2 0 0 0-1.477 3.717 26.936 26.936 0 0 1 6.474 3.682 2 2 0 0 0 2.438-3.172m-17.834-6.391a31.09 31.09 0 0 0-8.5.886 2 2 0 0 0 .959 3.883 27.06 27.06 0 0 1 7.408-.771 2 2 0 1 0 .132-3.998m-18.272 5.207a31.139 31.139 0 0 0-6.383 5.688 2 2 0 1 0 3.045 2.593 27.152 27.152 0 0 1 5.564-4.957 2 2 0 1 0-2.226-3.324M79.96 59.121a30.864 30.864 0 0 0-1.862 8.349 2 2 0 1 0 3.987.323c.203-2.506.75-4.946 1.62-7.268a2 2 0 1 0-3.746-1.404m-.923 18.873a30.827 30.827 0 0 0 3.327 7.881 2.001 2.001 0 0 0 3.435-2.051 26.785 26.785 0 0 1-2.895-6.859 2 2 0 0 0-3.865 1.029M89.301 93.94a31.008 31.008 0 0 0 7.286 4.476 2 2 0 1 0 1.603-3.665 26.983 26.983 0 0 1-6.346-3.899 2 2 0 0 0-2.543 3.087m17.61 6.991a2 2 0 0 1 .265-3.991c.601.04 1.205.06 1.812.06a1.999 1.999 0 1 1-.001 3.999c-.695 0-1.387-.023-2.076-.069"/><path fill="#fc0" d="M117.78 63.798c.241.268.288.563.14.884l-10.848 23.24c-.174.334-.455.502-.843.502-.054 0-.148-.014-.282-.04a.855.855 0 0 1-.512-.382.761.761 0 0 1-.09-.603l3.957-16.232-8.156 2.03a1.08 1.08 0 0 1-.241.02.93.93 0 0 1-.623-.222c-.24-.2-.328-.462-.26-.783l4.04-16.574a.858.858 0 0 1 .321-.462.917.917 0 0 1 .563-.18h6.59c.254 0 .468.083.642.25a.797.797 0 0 1 .261.593.818.818 0 0 1-.1.362l-3.435 9.301 7.955-1.969c.107-.027.187-.04.241-.04.254 0 .482.1.683.301"/><path fill="#e5e5e5" d="M148 69c0-1.105.887-2 1.998-2h4c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.992 1.992 0 0 1 148 69m14 0c0-1.105.887-2 1.998-2h4c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.992 1.992 0 0 1 162 69"/></g></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/manual_action.svg b/app/assets/images/illustrations/manual_action.svg new file mode 100644 index 00000000000..85735855b46 --- /dev/null +++ b/app/assets/images/illustrations/manual_action.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 398 151" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="none" fill-rule="evenodd"><path fill="#fef0e8" stroke="#fc6d26" stroke-width="4" d="M57.7 106.5h21.6a4.2 4.2 0 0 1 4.2 4.2v5.6a4.2 4.2 0 0 1-4.2 4.2H57.7a4.2 4.2 0 0 1-4.2-4.2v-5.6a4.2 4.2 0 0 1 4.2-4.2"/><g transform="translate(42 117)"><rect width="52" height="23" x=".5" y=".5" fill="#fff" stroke="#eee" stroke-width="4" rx="4.2"/><g fill="#fdc4a8"><rect width="11" height="2" x="8" y="8" rx="1"/><rect width="11" height="2" x="8" y="14" rx="1"/></g></g><g fill-rule="nonzero"><path fill="#e1dbf1" d="M96.31 132.32c1.048 0 1.648.007 4.319.042 11.523.153 18.377-.12 26.32-1.533 24.23-4.309 38.521-18.02 38.521-45.03 0-31.02 21.885-44.487 66.903-40.522l.351-3.985c-47.09-4.147-71.25 10.727-71.25 44.507 0 24.868-12.746 37.1-35.22 41.09-7.623 1.356-14.284 1.621-25.567 1.471a287.717 287.717 0 0 0-4.372-.042v4"/><path fill="#eee" d="M242 57.678c-6.29-1.373-11-6.976-11-13.678 0-6.702 4.71-12.304 11-13.678v4.136c-4.057 1.274-7 5.065-7 9.542 0 4.478 2.943 8.268 7 9.542v4.136"/></g><g transform="translate(242)"><rect width="116" height="134" y="7" fill="#f9f9f9" rx="10"/><rect width="116" height="134" x="5" y="2" fill="#fff" rx="10"/><path fill="#eee" fill-rule="nonzero" d="M15 4a8 8 0 0 0-8 8v114a8 8 0 0 0 8 8h96a8 8 0 0 0 8-8V12a8 8 0 0 0-8-8H15m0-4h96c6.627 0 12 5.373 12 12v114c0 6.627-5.373 12-12 12H15c-6.627 0-12-5.373-12-12V12C3 5.373 8.373 0 15 0"/><g transform="translate(23 25)"><g fill="#e1dbf1"><rect width="16" height="4" rx="2"/><rect width="16" height="4" x="32" y="12" rx="2"/></g><rect width="16" height="4" x="44" fill="#eee" rx="2"/><rect width="16" height="4" x="12" y="24" fill="#e1dbf1" rx="2"/><rect width="16" height="4" x="64" y="36" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="20" fill="#fee1d3" rx="2" id="a"/><rect width="8" height="4" x="32" y="36" fill="#fc6d26" rx="2"/><rect width="8" height="4" x="52" y="12" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="64" fill="#fef0e8" rx="2" id="b"/><rect width="12" height="4" x="16" y="48" fill="#e1dbf1" rx="2"/><rect width="8" height="4" x="44" y="36" fill="#fc6d26" rx="2"/><g fill="#e1dbf1"><rect width="4" height="4" x="56" y="36" rx="2"/><rect width="4" height="4" x="64" y="60" rx="2"/></g><rect width="4" height="4" x="72" y="60" fill="#fc6d26" rx="2"/><rect width="8" height="4" x="32" fill="#fc6d26" rx="2" id="c"/><g fill="#eee"><rect width="28" height="4" y="36" rx="2"/><rect width="28" height="4" x="44" y="48" rx="2"/></g><rect width="28" height="4" x="32" y="60" fill="#efedf8" rx="2"/><rect width="28" height="4" y="12" fill="#6b4fbb" rx="2"/><rect width="28" height="4" x="32" y="24" fill="#c3b8e3" rx="2"/><rect width="8" height="4" y="24" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="32" y="48" fill="#6b4fbb" rx="2"/><rect width="12" height="4" y="48" fill="#fc6d26" rx="2"/><g fill="#fef0e8"><rect width="12" height="4" y="60" rx="2"/><rect width="12" height="4" x="16" y="60" rx="2"/></g><g transform="translate(0 72)"><rect width="16" height="4" fill="#efedf8" rx="2"/><rect width="16" height="4" x="18" y="12" fill="#fc6d26" rx="2"/><rect width="16" height="4" x="44" fill="#6b4fbb" rx="2"/><use xlink:href="#a"/><rect width="8" height="4" x="38" y="12" fill="#fef0e8" rx="2"/><use xlink:href="#b"/><use xlink:href="#c"/><rect width="14" height="4" y="12" fill="#eee" rx="2"/></g></g></g><g transform="translate(330 83)"><circle cx="33" cy="33" r="33" fill="#fff"/><g fill-rule="nonzero"><path fill="#eee" d="M33 68C13.67 68-2 52.33-2 33S13.67-2 33-2s35 15.67 35 35-15.67 35-35 35m0-4c17.12 0 31-13.879 31-31C64 15.88 50.121 2 33 2 15.88 2 2 15.879 2 33c0 17.12 13.879 31 31 31"/><path fill="#6b4fbb" stroke="#6b4fbb" stroke-width=".968" d="M42.383 34.655v-3.308l-2.112-.343c-.116-.456-.351-.913-.703-1.598l1.29-1.711-2.463-2.398-1.76 1.256a6.347 6.347 0 0 0-1.642-.684l-.233-2.055h-3.401l-.352 2.055c-.586.114-1.055.342-1.642.684l-1.76-1.255-2.463 2.397 1.173 1.711c-.352.57-.469 1.027-.704 1.598l-1.995.228v3.31l2.112.342c.116.57.351 1.027.703 1.598l-1.172 1.712 2.463 2.397 1.759-1.141c.469.227 1.056.456 1.642.684l.352 2.055h3.518l.352-2.055c.586-.114 1.055-.342 1.642-.684l1.76 1.255 2.463-2.397-1.29-1.712a6.03 6.03 0 0 0 .703-1.598l1.76-.344M33 36.367c-1.994 0-3.519-1.484-3.519-3.424 0-1.941 1.525-3.424 3.519-3.424 1.994 0 3.519 1.483 3.519 3.424 0 1.94-1.525 3.424-3.519 3.424" stroke-linecap="round" stroke-linejoin="bevel"/><path fill="#e1dbf1" d="M33 53.563c-11.598 0-21-9.206-21-20.563s9.402-20.563 21-20.563S54 21.643 54 33s-9.402 20.563-21 20.563m0-4.375c9.13 0 16.532-7.248 16.532-16.188 0-8.94-7.402-16.188-16.532-16.188-9.13 0-16.532 7.248-16.532 16.188 0 8.94 7.402 16.188 16.532 16.188"/></g></g><path fill="#fff" d="M164 114c14.912 0 27-12.09 27-27 0-14.912-12.09-27-27-27-14.912 0-27 12.09-27 27 0 14.912 12.09 27 27 27"/><g fill-rule="nonzero"><path fill="#eee" d="M164 118c-17.12 0-31-13.879-31-31 0-17.12 13.879-31 31-31 17.12 0 31 13.879 31 31 0 17.12-13.879 31-31 31m0-4c14.912 0 27-12.09 27-27 0-14.912-12.09-27-27-27-14.912 0-27 12.09-27 27 0 14.912 12.09 27 27 27"/><path fill="#fc0" d="M172.78 80.798c.241.268.288.563.14.884l-10.848 23.24c-.174.334-.455.502-.843.502-.054 0-.148-.014-.282-.04a.855.855 0 0 1-.512-.382.761.761 0 0 1-.09-.603l3.957-16.232-8.156 2.03a1.08 1.08 0 0 1-.241.02.93.93 0 0 1-.623-.222c-.24-.2-.328-.462-.26-.783l4.04-16.574a.858.858 0 0 1 .321-.462.917.917 0 0 1 .563-.18h6.59c.254 0 .468.083.642.25a.797.797 0 0 1 .261.593.818.818 0 0 1-.1.362l-3.435 9.301 7.955-1.969c.107-.027.187-.04.241-.04.254 0 .482.1.683.301"/></g><g><path fill="#eee" fill-rule="nonzero" d="M37.801 99.01l5.355 2.648c2.271 1.122 4.643-.252 4.809-2.778l.487-7.546a27.675 27.675 0 0 0 2.87-4.076c7.594-13.152 3.088-29.972-10.07-37.565-13.153-7.594-29.971-3.087-37.566 10.07-7.594 13.154-3.087 29.973 10.07 37.565a27.46 27.46 0 0 0 24.05 1.687m.952-3.992a2.002 2.002 0 0 0-1.698-.035 23.454 23.454 0 0 1-21.299-1.124c-11.24-6.488-15.09-20.86-8.602-32.1 6.49-11.239 20.862-15.09 32.1-8.601 11.239 6.489 15.09 20.862 8.6 32.1a23.519 23.519 0 0 1-2.849 3.939 1.995 1.995 0 0 0-.504 1.204l-.466 7.229-5.285-2.613"/><path fill="#fdc4a8" d="M21.137 70.471A7.495 7.495 0 0 0 27.5 74c2.684 0 5.04-1.41 6.363-3.529C36.377 71.869 38 74.267 38 77.674c0 5.799-2.739 9.587-10.5 9.587S17 83.473 17 77.674c0-3.407 1.622-5.804 4.137-7.203M27.5 72a5.5 5.5 0 1 1 0-11 5.5 5.5 0 1 1 0 11"/></g></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/merge_request_changes_empty.svg b/app/assets/images/illustrations/merge_request_changes_empty.svg index 707efa736e4..40efeb2de57 100644 --- a/app/assets/images/illustrations/merge_request_changes_empty.svg +++ b/app/assets/images/illustrations/merge_request_changes_empty.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 374 268" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><rect id="d" width="230" height="176" rx="10" fill="#fff"/><rect id="e" width="14" rx="2" height="4"/><rect id="f" width="14" x="40" rx="2" height="4"/><rect id="g" width="14" x="40" y="24" rx="2" height="4"/><rect id="h" width="7" x="20" y="12" rx="2" height="4"/><rect id="i" width="7" y="24" rx="2" height="4"/><rect id="j" width="7" x="33" y="12" rx="2" height="4"/><circle id="l" cx="31" cy="31" r="31"/><circle id="c" cx="35" cy="35" r="35"/><circle id="a" cx="44" cy="44" r="44"/><circle id="b" cx="31" cy="31" r="31"/></defs><g fill="none" fill-rule="evenodd"><g transform="translate(0 94)"><circle cx="57" cy="57" r="44" fill="#f9f9f9"/><g transform="rotate(-7.999 120.507 -22.508)"><use fill="#fff" xlink:href="#a"/><circle cx="44" cy="44" r="42" stroke="#eee" stroke-width="4"/><path fill="#fee1d3" fill-rule="nonzero" d="M34.394 55.736A4 4 0 0 1 36.706 55H56a6 6 0 0 0 6-6V35a6 6 0 0 0-6-6H34a6 6 0 0 0-6 6v25.26l6.394-4.529m2.312 3.264l-7.972 5.647A3.001 3.001 0 0 1 24 62.194v-27.2c0-5.523 4.477-10 10-10h22c5.523 0 10 4.477 10 10v14c0 5.523-4.477 10-10 10H36.706"/><path fill="#fc6d26" d="M38 40a2 2 0 1 1 .001 3.999A2 2 0 0 1 38 40m7 0a2 2 0 1 1 .001 3.999A2 2 0 0 1 45 40m7 0a2 2 0 1 1 .001 3.999A2 2 0 0 1 52 40"/></g></g><g transform="translate(48)"><circle cx="41" cy="41" r="31" fill="#f9f9f9"/><g transform="rotate(-7.999 84.554 -15.551)"><use fill="#fff" xlink:href="#b"/><circle cx="31" cy="31" r="29" stroke="#eee" stroke-width="4"/><rect width="20" height="4" x="21" y="29" fill="#6b4fbb" rx="2"/></g></g><path fill="#f9f9f9" d="M235.58 229H102c-6.627 0-12-5.373-12-12V65c0-6.627 5.373-12 12-12h206c6.627 0 12 5.373 12 12v18.399A34.834 34.834 0 0 1 337 79c19.33 0 35 15.67 35 35s-15.67 35-35 35a34.831 34.831 0 0 1-17-4.399v72.4c0 6.627-5.373 12-12 12h-11.58c.381 1.941.58 3.947.58 6 0 17.12-13.879 31-31 31-17.12 0-31-13.879-31-31 0-2.053.2-4.059.58-6"/><g transform="translate(87 50)"><g transform="rotate(7.999 -44.933 1563.894)"><use fill="#fff" xlink:href="#c"/><circle cx="35" cy="35" r="33" stroke="#eee" stroke-width="4"/><g transform="translate(20 19)"><circle cx="15" cy="16" r="15" fill="#f4f1fa" stroke="#6b4fbb" stroke-width="3"/><g fill="#6b4fbb"><path d="M19.419 6.996h-.007L16.959 4l-2.454 2.997H14.5L12.046 4 9.591 6.998h-.003L7.133 4 4.677 6.999H2.001c2.605-4.204 7.231-7 12.502-7 5.269 0 9.892 2.793 12.498 6.994h-2.676l-2.452-2.994-2.453 2.996"/><circle cx="9.5" cy="17.5" r="1.5"/><circle cx="20.5" cy="17.5" r="1.5"/></g></g></g><use xlink:href="#d"/><rect width="226" height="172" x="2" y="2" stroke="#eee" stroke-width="4" rx="10"/><rect width="4" height="122" x="33" y="42" fill="#eee" rx="2"/><g transform="translate(13 59)"><rect width="10" height="4" fill="#fee1d3" rx="2"/><rect width="10" height="4" y="12" fill="#f0edf8" rx="2"/><rect width="10" height="4" y="24" fill="#fef0e9" rx="2"/><rect width="10" height="4" y="36" fill="#fee1d3" rx="2"/><rect width="10" height="4" y="48" fill="#e1dbf1" rx="2"/><rect width="10" height="4" y="60" fill="#f0edf8" rx="2"/><rect width="10" height="4" y="72" fill="#fef0e9" rx="2"/><rect width="10" height="4" y="84" fill="#fee1d3" rx="2"/></g><g transform="translate(55 59)"><use fill="#6b4fbb" xlink:href="#e"/><rect width="14" height="4" x="20" fill="#f0edf8" rx="2"/><use fill="#fef0e9" xlink:href="#f"/><rect width="14" height="4" y="12" fill="#f0edf8" rx="2"/><use fill="#fef0e9" xlink:href="#g"/><rect width="14" height="4" y="48" fill="#e1dbf1" rx="2"/><rect width="14" height="4" x="40" y="36" fill="#fef0e9" rx="2"/><use fill="#fee1d3" xlink:href="#h"/><rect width="7" height="4" x="27" y="36" fill="#6b4fbb" rx="2"/><rect width="7" height="4" x="20" y="48" fill="#fee1d3" rx="2"/><use fill="#fc6d26" xlink:href="#i"/><rect width="21" height="4" x="13" y="24" fill="#e1dbf1" rx="2"/><rect width="21" height="4" y="36" fill="#eee" rx="2"/><use fill="#6b4fbb" xlink:href="#j"/><g transform="translate(98)"><use fill="#fee1d3" xlink:href="#e"/><rect width="14" height="4" x="20" fill="#f0edf8" rx="2"/><use fill="#fc6d26" xlink:href="#f"/><rect width="14" height="4" y="12" fill="#fef0e9" rx="2" id="k"/><use fill="#e1dbf1" xlink:href="#g"/><rect width="14" height="4" y="48" fill="#f0edf8" rx="2"/><rect width="14" height="4" x="40" y="36" fill="#fee1d3" rx="2"/><use fill="#fc6d26" xlink:href="#h"/><rect width="7" height="4" x="27" y="36" fill="#6b4fbb" rx="2"/><rect width="7" height="4" x="20" y="48" fill="#fc6d26" rx="2"/><use fill="#6b4fbb" xlink:href="#i"/><rect width="21" height="4" x="13" y="24" fill="#fee1d3" rx="2"/><rect width="21" height="4" y="36" fill="#fef0e9" rx="2"/><use fill="#6b4fbb" xlink:href="#j"/></g><g transform="translate(0 60)"><use fill="#f0edf8" xlink:href="#e"/><rect width="14" height="4" x="20" fill="#6b4fbb" rx="2"/><use fill="#e1dbf1" xlink:href="#f"/><use xlink:href="#k"/><use fill="#fee1d3" xlink:href="#g"/><use fill="#eee" xlink:href="#h"/><use fill="#6b4fbb" xlink:href="#i"/><rect width="21" height="4" x="13" y="24" fill="#fef0e9" rx="2"/><use fill="#fc6d26" xlink:href="#j"/></g><rect width="4" height="63" x="74" y="13" fill="#eee" rx="2"/></g><rect width="230" height="4" y="27" fill="#eee" rx="2"/></g><g transform="rotate(7.999 -1289.786 1797.583)"><use fill="#fff" xlink:href="#l"/><circle cx="31" cy="31" r="29" stroke="#eee" stroke-width="4"/><path fill="#fc6d26" d="M29 29h-6a2 2 0 1 0 0 4h6v6a2 2 0 1 0 4 0v-6h6a2 2 0 1 0 0-4h-6v-6a2 2 0 1 0-4 0v6"/></g></g></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="374" height="268" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><circle id="a" cx="44" cy="44" r="44"/><circle id="b" cx="31" cy="31" r="31"/><circle id="c" cx="35" cy="35" r="35"/><rect id="d" width="230" height="176" rx="10"/><circle id="e" cx="31" cy="31" r="31"/></defs><g fill="none" fill-rule="evenodd"><g transform="translate(4 98)"><circle cx="53" cy="53" r="44" fill="#F9F9F9"/><g transform="translate(6 6)"><use fill="#FFF" xlink:href="#a"/><circle cx="44" cy="44" r="42" stroke="#EEE" stroke-width="4"/><path fill="#FEE1D3" fill-rule="nonzero" d="M34.394 55.736A4 4 0 0 1 36.706 55H56a6 6 0 0 0 6-6V35a6 6 0 0 0-6-6H34a6 6 0 0 0-6 6v25.265l6.394-4.53zM36.706 59l-7.972 5.647A3 3 0 0 1 24 62.199V35c0-5.523 4.477-10 10-10h22c5.523 0 10 4.477 10 10v14c0 5.523-4.477 10-10 10H36.706z"/><path fill="#FC6D26" d="M38 40a2 2 0 1 1 0 4 2 2 0 0 1 0-4zm7 0a2 2 0 1 1 0 4 2 2 0 0 1 0-4zm7 0a2 2 0 1 1 0 4 2 2 0 0 1 0-4z"/></g></g><g transform="translate(50 2)"><circle cx="39" cy="39" r="31" fill="#F9F9F9"/><g transform="translate(5 5)"><use fill="#FFF" xlink:href="#b"/><circle cx="31" cy="31" r="29" stroke="#EEE" stroke-width="4"/><rect width="20" height="4" x="21" y="29" fill="#6B4FBB" rx="2"/></g></g><path fill="#F9F9F9" d="M235.58 229H102c-6.627 0-12-5.373-12-12V65c0-6.627 5.373-12 12-12h206c6.627 0 12 5.373 12 12v18.399A34.842 34.842 0 0 1 337 79c19.33 0 35 15.67 35 35s-15.67 35-35 35a34.842 34.842 0 0 1-17-4.399V217c0 6.627-5.373 12-12 12h-11.58c.38 1.941.58 3.947.58 6 0 17.12-13.88 31-31 31s-31-13.88-31-31c0-2.053.2-4.059.58-6z"/><g transform="translate(87 50)"><g transform="translate(212 26)"><use fill="#FFF" xlink:href="#c"/><circle cx="35" cy="35" r="33" stroke="#EEE" stroke-width="4"/><g transform="translate(20 19)"><circle cx="15" cy="16" r="15" fill="#F4F1FA" stroke="#6B4FBB" stroke-width="3"/><path fill="#6B4FBB" d="M19.419 6.996h-.007L16.959 4l-2.454 2.997h-.006L12.045 4 9.59 6.998h-.003L7.132 4 4.676 7H2c2.605-4.204 7.23-7 12.502-7C19.771 0 24.394 2.793 27 6.994h-2.676L21.872 4l-2.453 2.996z"/><circle cx="9.5" cy="17.5" r="1.5" fill="#6B4FBB"/><circle cx="20.5" cy="17.5" r="1.5" fill="#6B4FBB"/></g></g><use fill="#FFF" xlink:href="#d"/><rect width="226" height="172" x="2" y="2" stroke="#EEE" stroke-width="4" rx="10"/><rect width="4" height="122" x="33" y="42" fill="#EEE" rx="2"/><g transform="translate(13 59)"><rect width="10" height="4" fill="#FEE1D3" rx="2"/><rect width="10" height="4" y="12" fill="#F0EDF8" rx="2"/><rect width="10" height="4" y="24" fill="#FEF0E9" rx="2"/><rect width="10" height="4" y="36" fill="#FEE1D3" rx="2"/><rect width="10" height="4" y="48" fill="#E1DBF1" rx="2"/><rect width="10" height="4" y="60" fill="#F0EDF8" rx="2"/><rect width="10" height="4" y="72" fill="#FEF0E9" rx="2"/><rect width="10" height="4" y="84" fill="#FEE1D3" rx="2"/></g><g transform="translate(55 59)"><rect width="14" height="4" fill="#6B4FBB" rx="2"/><rect width="14" height="4" x="20" fill="#F0EDF8" rx="2"/><rect width="14" height="4" x="40" fill="#FEF0E9" rx="2"/><rect width="14" height="4" y="12" fill="#F0EDF8" rx="2"/><rect width="14" height="4" x="40" y="24" fill="#FEF0E9" rx="2"/><rect width="14" height="4" y="48" fill="#E1DBF1" rx="2"/><rect width="14" height="4" x="40" y="36" fill="#FEF0E9" rx="2"/><rect width="7" height="4" x="20" y="12" fill="#FEE1D3" rx="2"/><rect width="7" height="4" x="27" y="36" fill="#6B4FBB" rx="2"/><rect width="7" height="4" x="20" y="48" fill="#FEE1D3" rx="2"/><rect width="7" height="4" y="24" fill="#FC6D26" rx="2"/><rect width="21" height="4" x="13" y="24" fill="#E1DBF1" rx="2"/><rect width="21" height="4" y="36" fill="#EEE" rx="2"/><rect width="7" height="4" x="33" y="12" fill="#6B4FBB" rx="2"/><g transform="translate(98)"><rect width="14" height="4" fill="#FEE1D3" rx="2"/><rect width="14" height="4" x="20" fill="#F0EDF8" rx="2"/><rect width="14" height="4" x="40" fill="#FC6D26" rx="2"/><rect width="14" height="4" y="12" fill="#FEF0E9" rx="2"/><rect width="14" height="4" x="40" y="24" fill="#E1DBF1" rx="2"/><rect width="14" height="4" y="48" fill="#F0EDF8" rx="2"/><rect width="14" height="4" x="40" y="36" fill="#FEE1D3" rx="2"/><rect width="7" height="4" x="20" y="12" fill="#FC6D26" rx="2"/><rect width="7" height="4" x="27" y="36" fill="#6B4FBB" rx="2"/><rect width="7" height="4" x="20" y="48" fill="#FC6D26" rx="2"/><rect width="7" height="4" y="24" fill="#6B4FBB" rx="2"/><rect width="21" height="4" x="13" y="24" fill="#FEE1D3" rx="2"/><rect width="21" height="4" y="36" fill="#FEF0E9" rx="2"/><rect width="7" height="4" x="33" y="12" fill="#6B4FBB" rx="2"/></g><g transform="translate(0 60)"><rect width="14" height="4" fill="#F0EDF8" rx="2"/><rect width="14" height="4" x="20" fill="#6B4FBB" rx="2"/><rect width="14" height="4" x="40" fill="#E1DBF1" rx="2"/><rect width="14" height="4" y="12" fill="#FEF0E9" rx="2"/><rect width="14" height="4" x="40" y="24" fill="#FEE1D3" rx="2"/><rect width="7" height="4" x="20" y="12" fill="#EEE" rx="2"/><rect width="7" height="4" y="24" fill="#6B4FBB" rx="2"/><rect width="21" height="4" x="13" y="24" fill="#FEF0E9" rx="2"/><rect width="7" height="4" x="33" y="12" fill="#FC6D26" rx="2"/></g><rect width="4" height="63" x="74" y="13" fill="#EEE" rx="2"/></g><rect width="230" height="4" y="27" fill="#EEE" rx="2"/></g><g transform="translate(233 201)"><use fill="#FFF" xlink:href="#e"/><circle cx="31" cy="31" r="29" stroke="#EEE" stroke-width="4"/><path fill="#FC6D26" d="M29 29v-6a2 2 0 1 1 4 0v6h6a2 2 0 1 1 0 4h-6v6a2 2 0 1 1-4 0v-6h-6a2 2 0 1 1 0-4h6z"/></g></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/service_desk_callout.svg b/app/assets/images/illustrations/service_desk_callout.svg new file mode 100644 index 00000000000..2886388279e --- /dev/null +++ b/app/assets/images/illustrations/service_desk_callout.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82"><g fill="none" fill-rule="evenodd"><path fill="#F9F9F9" d="M2.12 42c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3C74.353 61.032 58.425 76 39 76 19.575 76 3.647 61.032 2.12 42z"/><path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/><rect width="7" height="1" x="59" y="38" fill="#E1DBF2" rx=".5"/><path fill="#6B4FBB" d="M60.5 42a3.5 3.5 0 0 0 0-7v7z"/><rect width="7" height="1" x="12" y="38" fill="#E1DBF2" transform="matrix(-1 0 0 1 31 0)" rx=".5"/><path fill="#6B4FBB" d="M17.5 42a3.5 3.5 0 0 1 0-7v7z"/><path fill="#E1DBF1" fill-rule="nonzero" d="M39 58c10.493 0 19-8.507 19-19s-8.507-19-19-19-19 8.507-19 19 8.507 19 19 19zm0 4c-12.703 0-23-10.297-23-23s10.297-23 23-23 23 10.297 23 23-10.297 23-23 23z"/><path fill="#6B4FBB" d="M35 56a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/><path fill="#E1DBF1" fill-rule="nonzero" d="M26.5 40c0 4.143 3.355 7.5 7.494 7.5h10.012A7.497 7.497 0 0 0 51.5 40c0-4.143-3.355-7.5-7.494-7.5H33.994A7.497 7.497 0 0 0 26.5 40zm-3 0c0-5.799 4.698-10.5 10.494-10.5h10.012C49.802 29.5 54.5 34.2 54.5 40c0 5.799-4.698 10.5-10.494 10.5H33.994C28.198 50.5 23.5 45.8 23.5 40z"/><path fill="#6B4FBB" fill-rule="nonzero" d="M35.255 42.406a1 1 0 1 1 1.872-.703 2.001 2.001 0 0 0 3.76-.038 1 1 0 1 1 1.886.665 4 4 0 0 1-7.518.076zM31.5 40a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm15 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z"/><path fill="#6B4FBB" d="M38 22h2a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2zm0 3h2a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2z" style="mix-blend-mode:multiply"/></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/service_desk_empty.svg b/app/assets/images/illustrations/service_desk_empty.svg new file mode 100644 index 00000000000..daaaeae6a17 --- /dev/null +++ b/app/assets/images/illustrations/service_desk_empty.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="226" height="178" viewBox="0 0 226 178"><g fill="none" fill-rule="evenodd"><path fill="#EEE" fill-rule="nonzero" d="M109.496 165.895a78.17 78.17 0 0 0 6.158.08 2 2 0 0 0-.11-4c-1.94.053-3.886.028-5.84-.074a2 2 0 0 0-2.1 1.893 1.996 1.996 0 0 0 1.89 2.102zm18.408-1.245a76 76 0 0 0 6-1.4 2 2 0 1 0-1.064-3.856c-1.875.52-3.772.96-5.686 1.327a2.001 2.001 0 0 0 .75 3.93zm17.572-5.636a76.28 76.28 0 0 0 5.486-2.803 2 2 0 1 0-1.962-3.485 72.42 72.42 0 0 1-5.2 2.656 2.003 2.003 0 0 0 1.676 3.635zm44.342-74.897a75.786 75.786 0 0 0-.674-6.127 2.002 2.002 0 0 0-3.956.598c.29 1.92.505 3.857.64 5.805a1.998 1.998 0 0 0 2.133 1.857 2 2 0 0 0 1.858-2.133zm-3.505-18.144a76.141 76.141 0 0 0-2.13-5.78 2.001 2.001 0 0 0-3.695 1.534 72.381 72.381 0 0 1 2.02 5.476 1.999 1.999 0 1 0 3.805-1.229zm-7.754-16.73a77.053 77.053 0 0 0-3.454-5.1 1.998 1.998 0 0 0-2.797-.423 1.998 1.998 0 0 0-.424 2.796 73.06 73.06 0 0 1 3.273 4.835c.58.94 1.814 1.23 2.753.647a2.001 2.001 0 0 0 .646-2.754zm-11.582-14.446a76.37 76.37 0 0 0-4.572-4.128 1.999 1.999 0 1 0-2.559 3.073 72.633 72.633 0 0 1 4.334 3.913 2.001 2.001 0 1 0 2.798-2.86zm-101.422-4.91a77.634 77.634 0 0 0-4.64 4.05 2.001 2.001 0 0 0 2.749 2.906 72.611 72.611 0 0 1 4.4-3.84 2 2 0 1 0-2.509-3.115zM52.7 43.062a75.962 75.962 0 0 0-3.546 5.04 2 2 0 1 0 3.363 2.168 72.314 72.314 0 0 1 3.36-4.777 2 2 0 0 0-3.177-2.432zm-9.373 15.924c-.82 1.882-1.56 3.8-2.226 5.745a2 2 0 1 0 3.787 1.294 72.253 72.253 0 0 1 2.108-5.443 1.998 1.998 0 0 0-1.036-2.63 2.001 2.001 0 0 0-2.633 1.036zm-5.26 17.74a76.33 76.33 0 0 0-.777 6.11 2 2 0 0 0 3.985.347c.17-1.947.415-3.88.737-5.793a2 2 0 0 0-3.945-.664zM74.87 155.55a76.028 76.028 0 0 0 5.437 2.897 2 2 0 1 0 1.737-3.603 71.34 71.34 0 0 1-5.152-2.745 1.998 1.998 0 0 0-2.737.714 2.002 2.002 0 0 0 .715 2.738zm16.97 7.34a76.606 76.606 0 0 0 5.975 1.498 2 2 0 1 0 .816-3.916 72.52 72.52 0 0 1-5.662-1.42 1.999 1.999 0 1 0-1.129 3.837z"/><path fill="#F9F9F9" d="M2.12 130c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3-1.527 19.032-17.455 34-36.88 34-19.425 0-35.353-14.968-36.88-34z"/><path fill="#EEE" fill-rule="nonzero" d="M39 166c-21.54 0-39-17.46-39-39s17.46-39 39-39 39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 92 39 92 4 107.67 4 127s15.67 35 35 35z"/><path fill="#FDC4A8" fill-rule="nonzero" d="M53.925 116.226A1.995 1.995 0 0 0 53 116H25a1.99 1.99 0 0 0-.898.212l14.663 13.406c.39.357.99.348 1.37-.02l13.79-13.372zm1.075 4.53L42.92 132.47a5 5 0 0 1-6.854.1L23 120.624V138a2 2 0 0 0 2 2h28a2 2 0 0 0 2-2v-17.244zM25 112h28a6 6 0 0 1 6 6v20a6 6 0 0 1-6 6H25a6 6 0 0 1-6-6v-20a6 6 0 0 1 6-6z"/><path fill="#F9F9F9" d="M150.12 131c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3-1.527 19.032-17.455 34-36.88 34-19.425 0-35.353-14.968-36.88-34z"/><path fill="#EEE" fill-rule="nonzero" d="M187 167c-21.54 0-39-17.46-39-39s17.46-39 39-39 39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35s-15.67-35-35-35-35 15.67-35 35 15.67 35 35 35z"/><path fill="#E1DBF1" fill-rule="nonzero" d="M180.51 137H199a2 2 0 0 0 2-2v-16a2 2 0 0 0-2-2h-24a2 2 0 0 0-2 2v22.743l7.51-4.743zm1.157 4l-9.6 6.062a2 2 0 0 1-3.067-1.69V119a6 6 0 0 1 6-6h24a6 6 0 0 1 6 6v16a6 6 0 0 1-6 6h-17.333z"/><path fill="#6B4FBB" d="M180 129a2 2 0 1 1-.001-3.999A2 2 0 0 1 180 129zm7 0a2 2 0 1 1-.001-3.999A2 2 0 0 1 187 129zm7 0a2 2 0 1 1-.001-3.999A2 2 0 0 1 194 129z"/><g><path fill="#F9F9F9" d="M76.12 42c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3-1.527 19.032-17.455 34-36.88 34-19.425 0-35.353-14.968-36.88-34z"/><path fill="#EEE" fill-rule="nonzero" d="M113 78c-21.54 0-39-17.46-39-39S91.46 0 113 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S132.33 4 113 4 78 19.67 78 39s15.67 35 35 35z"/><g transform="translate(133 35)"><rect width="7" height="1" y="3" fill="#E1DBF2" rx=".5"/><path fill="#6B4FBB" d="M1.5 7a3.5 3.5 0 1 0 0-7v7z"/></g><g transform="matrix(-1 0 0 1 93 35)"><rect width="7" height="1" y="3" fill="#E1DBF2" rx=".5"/><path fill="#6B4FBB" d="M1.5 7a3.5 3.5 0 1 0 0-7v7z"/></g><path fill="#E1DBF1" fill-rule="nonzero" d="M113 58c10.493 0 19-8.507 19-19s-8.507-19-19-19-19 8.507-19 19 8.507 19 19 19zm0 4c-12.703 0-23-10.297-23-23s10.297-23 23-23 23 10.297 23 23-10.297 23-23 23z"/><path fill="#6B4FBB" d="M109 56a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/><path fill="#E1DBF1" fill-rule="nonzero" d="M97.5 40c0-5.8 4.698-10.5 10.494-10.5h10.012c5.796 0 10.494 4.7 10.494 10.5s-4.698 10.5-10.494 10.5h-10.012C102.198 50.5 97.5 45.8 97.5 40zm3 0c0 4.143 3.355 7.5 7.494 7.5h10.012A7.496 7.496 0 0 0 125.5 40c0-4.143-3.355-7.5-7.494-7.5h-10.012A7.496 7.496 0 0 0 100.5 40z"/><path fill="#6B4FBB" fill-rule="nonzero" d="M109.255 42.406a.998.998 0 0 1 .584-1.287.997.997 0 0 1 1.287.583 2 2 0 0 0 3.76-.038 1 1 0 0 1 1.886.665 4.001 4.001 0 0 1-7.518.076zM105.5 40a1.5 1.5 0 1 1 .001-3.001A1.5 1.5 0 0 1 105.5 40zm15 0a1.5 1.5 0 1 1 .001-3.001A1.5 1.5 0 0 1 120.5 40z"/><path fill="#6B4FBB" d="M112 22h2a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2zm0 3h2a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2z" style="mix-blend-mode:multiply"/></g></g></svg>
\ No newline at end of file diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index d963101028a..21d8c790e90 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -1,4 +1,5 @@ import $ from 'jquery'; +import axios from './lib/utils/axios_utils'; const Api = { groupsPath: '/api/:version/groups.json', @@ -6,6 +7,7 @@ const Api = { namespacesPath: '/api/:version/namespaces.json', groupProjectsPath: '/api/:version/groups/:id/projects.json', projectsPath: '/api/:version/projects.json', + projectPath: '/api/:version/projects/:id', projectLabelsPath: '/:namespace_path/:project_path/labels', groupLabelsPath: '/groups/:namespace_path/labels', licensePath: '/api/:version/templates/licenses/:key', @@ -76,6 +78,14 @@ const Api = { .done(projects => callback(projects)); }, + // Return single project + project(projectPath) { + const url = Api.buildUrl(Api.projectPath) + .replace(':id', encodeURIComponent(projectPath)); + + return axios.get(url); + }, + newLabel(namespacePath, projectPath, data, callback) { let url; @@ -115,7 +125,7 @@ const Api = { commitMultiple(id, data) { // see https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions const url = Api.buildUrl(Api.commitPath) - .replace(':id', id); + .replace(':id', encodeURIComponent(id)); return this.wrapAjaxCall({ url, type: 'POST', @@ -127,7 +137,7 @@ const Api = { branchSingle(id, branch) { const url = Api.buildUrl(Api.branchSinglePath) - .replace(':id', id) + .replace(':id', encodeURIComponent(id)) .replace(':branch', branch); return this.wrapAjaxCall({ diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue index f54ea7df522..cbce9205e75 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue @@ -2,6 +2,7 @@ import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; import limitWarning from './limit_warning_component.vue'; import totalTime from './total_time_component.vue'; + import icon from '../../vue_shared/components/icon.vue'; export default { props: { @@ -12,6 +13,7 @@ userAvatarImage, totalTime, limitWarning, + icon, }, }; </script> @@ -52,7 +54,10 @@ </template> <template v-else> <span class="merge-request-branch" v-if="mergeRequest.branch"> - <i class= "fa fa-code-fork"></i> + <icon + name="fork" + :size="16"> + </icon> <a :href="mergeRequest.branch.url">{{ mergeRequest.branch.name }}</a> </span> </template> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue index 5d95ddcd90e..508a411e599 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue @@ -3,6 +3,7 @@ import iconBranch from '../svg/icon_branch.svg'; import limitWarning from './limit_warning_component.vue'; import totalTime from './total_time_component.vue'; + import icon from '../../vue_shared/components/icon.vue'; export default { props: { @@ -13,6 +14,7 @@ userAvatarImage, totalTime, limitWarning, + icon, }, computed: { iconBranch() { @@ -37,7 +39,10 @@ <user-avatar-image :img-src="build.author.avatarUrl"/> <h5 class="item-title"> <a :href="build.url" class="pipeline-id">#{{ build.id }}</a> - <i class="fa fa-code-fork"></i> + <icon + name="fork" + :size="16"> + </icon> <a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a> <span class="icon-branch" v-html="iconBranch"></span> <a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue index 04d5440b77b..88fa6b073ca 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue @@ -3,6 +3,7 @@ import iconBranch from '../svg/icon_branch.svg'; import limitWarning from './limit_warning_component.vue'; import totalTime from './total_time_component.vue'; + import icon from '../../vue_shared/components/icon.vue'; export default { props: { @@ -12,6 +13,7 @@ components: { totalTime, limitWarning, + icon, }, computed: { iconBuildStatus() { @@ -40,7 +42,10 @@ <a :href="build.url" class="item-build-name">{{ build.name }}</a> · <a :href="build.url" class="pipeline-id">#{{ build.id }}</a> - <i class="fa fa-code-fork"></i> + <icon + name="fork" + :size="16"> + </icon> <a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a> <span class="icon-branch" v-html="iconBranch"></span> <a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a> diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 62867c56214..118437b82a3 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -73,7 +73,6 @@ import initLegacyFilters from './init_legacy_filters'; import initIssuableSidebar from './init_issuable_sidebar'; import initProjectVisibilitySelector from './project_visibility'; import GpgBadges from './gpg_badges'; -import UserFeatureHelper from './helpers/user_feature_helper'; import initChangesDropdown from './init_changes_dropdown'; import NewGroupChild from './groups/new_group_child'; import AbuseReports from './abuse_reports'; @@ -111,6 +110,8 @@ import Activities from './activities'; return false; } + const fail = () => Flash('Error loading dynamic module'); + path = page.split(':'); shortcut_handler = null; @@ -447,9 +448,6 @@ import Activities from './activities'; break; case 'projects:tree:show': shortcut_handler = new ShortcutsNavigation(); - - if (UserFeatureHelper.isNewRepoEnabled()) break; - new TreeView(); new BlobViewer(); new NewCommitForm($('.js-create-dir-form')); @@ -468,7 +466,6 @@ import Activities from './activities'; shortcut_handler = true; break; case 'projects:blob:show': - if (UserFeatureHelper.isNewRepoEnabled()) break; new BlobViewer(); initBlob(); break; @@ -545,7 +542,7 @@ import Activities from './activities'; new CILintEditor(); break; case 'users:show': - new UserCallout(); + import('./pages/users/show').then(m => m.default()).catch(fail); break; case 'admin:conversational_development_index:show': new UserCallout(); diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js index 6110d961609..abb04d77f8f 100644 --- a/app/assets/javascripts/fly_out_nav.js +++ b/app/assets/javascripts/fly_out_nav.js @@ -161,13 +161,16 @@ export default () => { const items = [...sidebar.querySelectorAll('.sidebar-top-level-items > li')]; - sidebar.querySelector('.sidebar-top-level-items').addEventListener('mouseleave', () => { - clearTimeout(timeoutId); - - timeoutId = setTimeout(() => { - if (currentOpenMenu) hideMenu(currentOpenMenu); - }, getHideSubItemsInterval()); - }); + const topItems = sidebar.querySelector('.sidebar-top-level-items'); + if (topItems) { + sidebar.querySelector('.sidebar-top-level-items').addEventListener('mouseleave', () => { + clearTimeout(timeoutId); + + timeoutId = setTimeout(() => { + if (currentOpenMenu) hideMenu(currentOpenMenu); + }, getHideSubItemsInterval()); + }); + } headerHeight = document.querySelector('.nav-sidebar').offsetTop; diff --git a/app/assets/javascripts/helpers/user_feature_helper.js b/app/assets/javascripts/helpers/user_feature_helper.js deleted file mode 100644 index 638118a5204..00000000000 --- a/app/assets/javascripts/helpers/user_feature_helper.js +++ /dev/null @@ -1,7 +0,0 @@ -import Cookies from 'js-cookie'; - -export default { - isNewRepoEnabled() { - return Cookies.get('new_repo') === 'true'; - }, -}; diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue new file mode 100644 index 00000000000..704dff981df --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue @@ -0,0 +1,66 @@ +<script> + import { mapState } from 'vuex'; + import icon from '../../../vue_shared/components/icon.vue'; + import listItem from './list_item.vue'; + import listCollapsed from './list_collapsed.vue'; + + export default { + components: { + icon, + listItem, + listCollapsed, + }, + props: { + title: { + type: String, + required: true, + }, + fileList: { + type: Array, + required: true, + }, + }, + computed: { + ...mapState([ + 'currentProjectId', + 'currentBranchId', + 'rightPanelCollapsed', + ]), + }, + methods: { + toggleCollapsed() { + this.$emit('toggleCollapsed'); + }, + }, + + }; +</script> + +<template> + <div class="multi-file-commit-list"> + <list-collapsed + v-if="rightPanelCollapsed" + /> + <template v-else> + <ul + v-if="fileList.length" + class="list-unstyled append-bottom-0" + > + <li + v-for="file in fileList" + :key="file.key" + > + <list-item + :file="file" + /> + </li> + </ul> + <div + v-else + class="help-block prepend-top-0" + > + No changes + </div> + </template> + </div> +</template> diff --git a/app/assets/javascripts/repo/components/commit_sidebar/list_collapsed.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue index 6a0262f271b..6a0262f271b 100644 --- a/app/assets/javascripts/repo/components/commit_sidebar/list_collapsed.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue diff --git a/app/assets/javascripts/repo/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue index 742f746e02f..742f746e02f 100644 --- a/app/assets/javascripts/repo/components/commit_sidebar/list_item.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue new file mode 100644 index 00000000000..7f29a355eca --- /dev/null +++ b/app/assets/javascripts/ide/components/ide.vue @@ -0,0 +1,73 @@ +<script> +import { mapState, mapGetters } from 'vuex'; +import ideSidebar from './ide_side_bar.vue'; +import ideContextbar from './ide_context_bar.vue'; +import repoTabs from './repo_tabs.vue'; +import repoFileButtons from './repo_file_buttons.vue'; +import ideStatusBar from './ide_status_bar.vue'; +import repoPreview from './repo_preview.vue'; +import repoEditor from './repo_editor.vue'; + +export default { + computed: { + ...mapState([ + 'currentBlobView', + 'selectedFile', + ]), + ...mapGetters([ + 'changedFiles', + 'activeFile', + ]), + }, + components: { + ideSidebar, + ideContextbar, + repoTabs, + repoFileButtons, + ideStatusBar, + repoEditor, + repoPreview, + }, + mounted() { + const returnValue = 'Are you sure you want to lose unsaved changes?'; + window.onbeforeunload = (e) => { + if (!this.changedFiles.length) return undefined; + + Object.assign(e, { + returnValue, + }); + return returnValue; + }; + }, +}; +</script> + +<template> + <div + class="ide-view" + > + <ide-sidebar/> + <div + class="multi-file-edit-pane" + > + <template + v-if="activeFile"> + <repo-tabs/> + <component + class="multi-file-edit-pane-content" + :is="currentBlobView" + /> + <repo-file-buttons/> + <ide-status-bar + :file="selectedFile"/> + </template> + <template + v-else> + <div class="ide-empty-state"> + <h2 class="clgray">Welcome to the GitLab IDE</h2> + </div> + </template> + </div> + <ide-contextbar/> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_context_bar.vue b/app/assets/javascripts/ide/components/ide_context_bar.vue new file mode 100644 index 00000000000..5a08718e386 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_context_bar.vue @@ -0,0 +1,75 @@ +<script> +import { mapGetters, mapState, mapActions } from 'vuex'; +import repoCommitSection from './repo_commit_section.vue'; +import icon from '../../vue_shared/components/icon.vue'; + +export default { + components: { + repoCommitSection, + icon, + }, + computed: { + ...mapState([ + 'rightPanelCollapsed', + ]), + ...mapGetters([ + 'changedFiles', + ]), + currentIcon() { + return this.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right'; + }, + }, + methods: { + ...mapActions([ + 'setPanelCollapsedStatus', + ]), + toggleCollapsed() { + this.setPanelCollapsedStatus({ + side: 'right', + collapsed: !this.rightPanelCollapsed, + }); + }, + }, +}; +</script> + +<template> + <div + class="multi-file-commit-panel" + :class="{ + 'is-collapsed': rightPanelCollapsed, + }" + > + <div + class="multi-file-commit-panel-section"> + <header + class="multi-file-commit-panel-header" + :class="{ + 'is-collapsed': rightPanelCollapsed, + }" + > + <div + class="multi-file-commit-panel-header-title" + v-if="!rightPanelCollapsed"> + <icon + name="list-bulleted" + :size="18" + /> + Staged + </div> + <button + type="button" + class="btn btn-transparent multi-file-commit-panel-collapse-btn" + @click="toggleCollapsed" + > + <icon + :name="currentIcon" + :size="18" + /> + </button> + </header> + <repo-commit-section + class=""/> + </div> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_project_branches_tree.vue b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue new file mode 100644 index 00000000000..bd3a521ff43 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue @@ -0,0 +1,47 @@ +<script> +import repoTree from './ide_repo_tree.vue'; +import icon from '../../vue_shared/components/icon.vue'; +import newDropdown from './new_dropdown/index.vue'; + +export default { + components: { + repoTree, + icon, + newDropdown, + }, + props: { + projectId: { + type: String, + required: true, + }, + branch: { + type: Object, + required: true, + }, + }, +}; +</script> + +<template> + <div class="branch-container"> + <div class="branch-header"> + <div class="branch-header-title"> + <icon + name="branch" + :size="12"> + </icon> + {{ branch.name }} + </div> + <div class="branch-header-btns"> + <new-dropdown + :project-id="projectId" + :branch="branch.name" + path=""/> + </div> + </div> + <div> + <repo-tree + :treeId="branch.treeId"/> + </div> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_project_tree.vue b/app/assets/javascripts/ide/components/ide_project_tree.vue new file mode 100644 index 00000000000..61daba6d176 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_project_tree.vue @@ -0,0 +1,47 @@ +<script> +import branchesTree from './ide_project_branches_tree.vue'; +import projectAvatarImage from '../../vue_shared/components/project_avatar/image.vue'; + +export default { + components: { + branchesTree, + projectAvatarImage, + }, + props: { + project: { + type: Object, + required: true, + }, + }, +}; +</script> + +<template> + <div class="projects-sidebar"> + <div class="context-header"> + <a + :title="project.name" + :href="project.web_url"> + <div class="avatar-container s40 project-avatar"> + <project-avatar-image + class="avatar-container project-avatar" + :link-href="project.path" + :img-src="project.avatar_url" + :img-alt="project.name" + :img-size="40" + /> + </div> + <div class="sidebar-context-title"> + {{ project.name }} + </div> + </a> + </div> + <div class="multi-file-commit-panel-inner-scroll"> + <branches-tree + v-for="(branch, index) in project.branches" + :key="branch.name" + :project-id="project.path_with_namespace" + :branch="branch"/> + </div> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_repo_tree.vue b/app/assets/javascripts/ide/components/ide_repo_tree.vue new file mode 100644 index 00000000000..b6b089e6b25 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_repo_tree.vue @@ -0,0 +1,66 @@ +<script> +import { mapState } from 'vuex'; +import RepoPreviousDirectory from './repo_prev_directory.vue'; +import RepoFile from './repo_file.vue'; +import RepoLoadingFile from './repo_loading_file.vue'; +import { treeList } from '../stores/utils'; + +export default { + components: { + 'repo-previous-directory': RepoPreviousDirectory, + 'repo-file': RepoFile, + 'repo-loading-file': RepoLoadingFile, + }, + props: { + treeId: { + type: String, + required: true, + }, + }, + computed: { + ...mapState([ + 'loading', + 'isRoot', + ]), + ...mapState({ + projectName(state) { + return state.project.name; + }, + }), + fetchedList() { + return treeList(this.$store.state, this.treeId); + }, + hasPreviousDirectory() { + return !this.isRoot && this.fetchedList.length; + }, + showLoading() { + return this.loading; + }, + }, +}; +</script> + +<template> +<div> + <div class="ide-file-list"> + <table class="table"> + <tbody + v-if="treeId"> + <repo-previous-directory + v-if="hasPreviousDirectory" + /> + <repo-loading-file + v-if="showLoading" + v-for="n in 5" + :key="n" + /> + <repo-file + v-for="file in fetchedList" + :key="file.key" + :file="file" + /> + </tbody> + </table> + </div> +</div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue new file mode 100644 index 00000000000..535398d98c2 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_side_bar.vue @@ -0,0 +1,62 @@ +<script> +import { mapState, mapActions } from 'vuex'; +import projectTree from './ide_project_tree.vue'; +import icon from '../../vue_shared/components/icon.vue'; + +export default { + components: { + projectTree, + icon, + }, + computed: { + ...mapState([ + 'projects', + 'leftPanelCollapsed', + ]), + currentIcon() { + return this.leftPanelCollapsed ? 'angle-double-right' : 'angle-double-left'; + }, + }, + methods: { + ...mapActions([ + 'setPanelCollapsedStatus', + ]), + toggleCollapsed() { + this.setPanelCollapsedStatus({ + side: 'left', + collapsed: !this.leftPanelCollapsed, + }); + }, + }, +}; +</script> + +<template> + <div + class="multi-file-commit-panel" + :class="{ + 'is-collapsed': leftPanelCollapsed, + }" + > + <div class="multi-file-commit-panel-inner"> + <project-tree + v-for="(project, index) in projects" + :key="project.id" + :project="project"/> + </div> + <button + type="button" + class="btn btn-transparent left-collapse-btn" + @click="toggleCollapsed" + > + <icon + :name="currentIcon" + :size="18" + /> + <span + v-if="!leftPanelCollapsed" + class="collapse-text" + >Collapse sidebar</span> + </button> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue new file mode 100644 index 00000000000..a24abadd936 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_status_bar.vue @@ -0,0 +1,71 @@ +<script> +import { mapState } from 'vuex'; +import icon from '../../vue_shared/components/icon.vue'; +import tooltip from '../../vue_shared/directives/tooltip'; +import timeAgoMixin from '../../vue_shared/mixins/timeago'; + +export default { + props: { + file: { + type: Object, + required: true, + }, + }, + components: { + icon, + }, + directives: { + tooltip, + }, + mixins: [ + timeAgoMixin, + ], + computed: { + ...mapState([ + 'selectedFile', + ]), + }, +}; +</script> + +<template> + <div + class="ide-status-bar"> + <div> + <icon + name="branch" + :size="12"> + </icon> + {{ selectedFile.branchId }} + </div> + <div> + <div + v-if="selectedFile.lastCommit && selectedFile.lastCommit.id"> + Last commit: + <a + v-tooltip + :title="selectedFile.lastCommit.message" + :href="selectedFile.lastCommit.url"> + {{ timeFormated(selectedFile.lastCommit.updatedAt) }} by + {{ selectedFile.lastCommit.author }} + </a> + </div> + </div> + <div + class="text-right"> + {{ selectedFile.name }} + </div> + <div + class="text-right"> + {{ selectedFile.eol }} + </div> + <div + class="text-right"> + {{ file.editorRow }}:{{ file.editorColumn }} + </div> + <div + class="text-right"> + {{ selectedFile.fileLanguage }} + </div> + </div> +</template> diff --git a/app/assets/javascripts/repo/components/new_branch_form.vue b/app/assets/javascripts/ide/components/new_branch_form.vue index ba7090e4a9d..2119d373d31 100644 --- a/app/assets/javascripts/repo/components/new_branch_form.vue +++ b/app/assets/javascripts/ide/components/new_branch_form.vue @@ -44,7 +44,7 @@ this.branchName = ''; if (this.dropdownText) { - this.dropdownText.textContent = this.currentBranch; + this.dropdownText.textContent = this.currentBranchId; } this.toggleDropdown(); diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue new file mode 100644 index 00000000000..6e67e99a70f --- /dev/null +++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue @@ -0,0 +1,101 @@ +<script> + import newModal from './modal.vue'; + import upload from './upload.vue'; + import icon from '../../../vue_shared/components/icon.vue'; + + export default { + props: { + branch: { + type: String, + required: true, + }, + path: { + type: String, + required: true, + }, + parent: { + type: Object, + default: null, + }, + }, + components: { + icon, + newModal, + upload, + }, + data() { + return { + openModal: false, + modalType: '', + }; + }, + methods: { + createNewItem(type) { + this.modalType = type; + this.toggleModalOpen(); + }, + toggleModalOpen() { + this.openModal = !this.openModal; + }, + }, + }; +</script> + +<template> + <div class="repo-new-btn pull-right"> + <div class="dropdown"> + <button + type="button" + class="btn btn-sm btn-default dropdown-toggle add-to-tree" + data-toggle="dropdown" + aria-label="Create new file or directory" + > + <icon + name="plus" + :size="12" + css-classes="pull-left" + /> + <icon + name="arrow-down" + :size="12" + css-classes="pull-left" + /> + </button> + <ul class="dropdown-menu dropdown-menu-right"> + <li> + <a + href="#" + role="button" + @click.prevent="createNewItem('blob')" + > + {{ __('New file') }} + </a> + </li> + <li> + <upload + :branch-id="branch" + :path="path" + :parent="parent" + /> + </li> + <li> + <a + href="#" + role="button" + @click.prevent="createNewItem('tree')" + > + {{ __('New directory') }} + </a> + </li> + </ul> + </div> + <new-modal + v-if="openModal" + :type="modalType" + :branch-id="branch" + :path="path" + :parent="parent" + @toggle="toggleModalOpen" + /> + </div> +</template> diff --git a/app/assets/javascripts/repo/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue index c191af7dec3..a0650d37690 100644 --- a/app/assets/javascripts/repo/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -1,10 +1,18 @@ <script> - import { mapActions } from 'vuex'; + import { mapActions, mapState } from 'vuex'; import { __ } from '../../../locale'; import modal from '../../../vue_shared/components/modal.vue'; export default { props: { + branchId: { + type: String, + required: true, + }, + parent: { + type: Object, + default: null, + }, type: { type: String, required: true, @@ -28,6 +36,9 @@ ]), createEntryInStore() { this.createTempEntry({ + projectId: this.currentProjectId, + branchId: this.branchId, + parent: this.parent, name: this.entryName.replace(new RegExp(`^${this.path}/`), ''), type: this.type, }); @@ -39,6 +50,9 @@ }, }, computed: { + ...mapState([ + 'currentProjectId', + ]), modalTitle() { if (this.type === 'tree') { return __('Create new directory'); diff --git a/app/assets/javascripts/repo/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue index 14ad32f4ae0..2a2f2a241fc 100644 --- a/app/assets/javascripts/repo/components/new_dropdown/upload.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue @@ -1,12 +1,22 @@ <script> - import { mapActions } from 'vuex'; + import { mapActions, mapState } from 'vuex'; export default { props: { - path: { + branchId: { type: String, required: true, }, + parent: { + type: Object, + default: null, + }, + }, + computed: { + ...mapState([ + 'trees', + 'currentProjectId', + ]), }, methods: { ...mapActions([ @@ -22,6 +32,9 @@ this.createTempEntry({ name, + projectId: this.currentProjectId, + branchId: this.branchId, + parent: this.parent, type: 'blob', content: result, base64: !isText, @@ -42,6 +55,9 @@ openFile() { Array.from(this.$refs.fileUpload.files).forEach(file => this.readFile(file)); }, + startFileUpload() { + this.$refs.fileUpload.click(); + }, }, mounted() { this.$refs.fileUpload.addEventListener('change', this.openFile); @@ -53,16 +69,19 @@ </script> <template> - <label - role="button" - class="menu-item" - > - {{ __('Upload file') }} + <div> + <a + href="#" + role="button" + @click.prevent="startFileUpload" + > + {{ __('Upload file') }} + </a> <input id="file-upload" type="file" class="hidden" ref="fileUpload" /> - </label> + </div> </template> diff --git a/app/assets/javascripts/repo/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index 4e0178072cb..470db2c9650 100644 --- a/app/assets/javascripts/repo/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -20,12 +20,13 @@ export default { submitCommitsLoading: false, startNewMR: false, commitMessage: '', - collapsed: true, }; }, computed: { ...mapState([ - 'currentBranch', + 'currentProjectId', + 'currentBranchId', + 'rightPanelCollapsed', ]), ...mapGetters([ 'changedFiles', @@ -42,12 +43,13 @@ export default { 'checkCommitStatus', 'commitChanges', 'getTreeData', + 'setPanelCollapsedStatus', ]), makeCommit(newBranch = false) { const createNewBranch = newBranch || this.startNewMR; const payload = { - branch: createNewBranch ? `${this.currentBranch}-${new Date().getTime().toString()}` : this.currentBranch, + branch: createNewBranch ? `${this.currentBranchId}-${new Date().getTime().toString()}` : this.currentBranchId, commit_message: this.commitMessage, actions: this.changedFiles.map(f => ({ action: f.tempFile ? 'create' : 'update', @@ -55,7 +57,7 @@ export default { content: f.content, encoding: f.base64 ? 'base64' : 'text', })), - start_branch: createNewBranch ? this.currentBranch : undefined, + start_branch: createNewBranch ? this.currentBranchId : undefined, }; this.showNewBranchModal = false; @@ -64,7 +66,12 @@ export default { this.commitChanges({ payload, newMr: this.startNewMR }) .then(() => { this.submitCommitsLoading = false; - this.getTreeData(); + this.$store.dispatch('getTreeData', { + projectId: this.currentProjectId, + branch: this.currentBranchId, + endpoint: `/tree/${this.currentBranchId}`, + force: true, + }); }) .catch(() => { this.submitCommitsLoading = false; @@ -86,19 +93,17 @@ export default { }); }, toggleCollapsed() { - this.collapsed = !this.collapsed; + this.setPanelCollapsedStatus({ + side: 'right', + collapsed: !this.rightPanelCollapsed, + }); }, }, }; </script> <template> -<div - class="multi-file-commit-panel" - :class="{ - 'is-collapsed': collapsed, - }" -> +<div class="multi-file-commit-panel-section"> <modal v-if="showNewBranchModal" :primary-button-label="__('Create new branch')" @@ -108,28 +113,16 @@ export default { @toggle="showNewBranchModal = false" @submit="makeCommit(true)" /> - <button - v-if="collapsed" - type="button" - class="btn btn-transparent multi-file-commit-panel-collapse-btn is-collapsed prepend-top-10 append-bottom-10" - @click="toggleCollapsed" - > - <i - aria-hidden="true" - class="fa fa-angle-double-left" - > - </i> - </button> <commit-files-list title="Staged" :file-list="changedFiles" - :collapsed="collapsed" + :collapsed="rightPanelCollapsed" @toggleCollapsed="toggleCollapsed" /> <form class="form-horizontal multi-file-commit-form" @submit.prevent="tryCommit" - v-if="!collapsed" + v-if="!rightPanelCollapsed" > <div class="multi-file-commit-fieldset"> <textarea diff --git a/app/assets/javascripts/repo/components/repo_edit_button.vue b/app/assets/javascripts/ide/components/repo_edit_button.vue index 37bd9003e96..37bd9003e96 100644 --- a/app/assets/javascripts/repo/components/repo_edit_button.vue +++ b/app/assets/javascripts/ide/components/repo_edit_button.vue diff --git a/app/assets/javascripts/repo/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index f37cbd1e961..221be4b9074 100644 --- a/app/assets/javascripts/repo/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -1,6 +1,6 @@ <script> /* global monaco */ -import { mapGetters, mapActions } from 'vuex'; +import { mapState, mapGetters, mapActions } from 'vuex'; import flash from '../../flash'; import monacoLoader from '../monaco_loader'; import Editor from '../lib/editor'; @@ -24,6 +24,9 @@ export default { ...mapActions([ 'getRawFileData', 'changeFileContent', + 'setFileLanguage', + 'setEditorPosition', + 'setFileEOL', ]), initMonaco() { if (this.shouldHideEditor) return; @@ -43,12 +46,36 @@ export default { const model = this.editor.createModel(this.activeFile); this.editor.attachModel(model); + model.onChange((m) => { this.changeFileContent({ file: this.activeFile, content: m.getValue(), }); }); + + // Handle Cursor Position + this.editor.onPositionChange((instance, e) => { + this.setEditorPosition({ + editorRow: e.position.lineNumber, + editorColumn: e.position.column, + }); + }); + + this.editor.setPosition({ + lineNumber: this.activeFile.editorRow, + column: this.activeFile.editorColumn, + }); + + // Handle File Language + this.setFileLanguage({ + fileLanguage: model.language, + }); + + // Get File eol + this.setFileEOL({ + eol: model.eol, + }); }, }, watch: { @@ -57,12 +84,22 @@ export default { this.initMonaco(); } }, + leftPanelCollapsed() { + this.editor.updateDimensions(); + }, + rightPanelCollapsed() { + this.editor.updateDimensions(); + }, }, computed: { ...mapGetters([ 'activeFile', 'activeFileExtension', ]), + ...mapState([ + 'leftPanelCollapsed', + 'rightPanelCollapsed', + ]), shouldHideEditor() { return this.activeFile.binary && !this.activeFile.raw; }, @@ -76,13 +113,14 @@ export default { class="blob-viewer-container blob-editor-container" > <div - v-show="shouldHideEditor" + v-if="shouldHideEditor" v-html="activeFile.html" > </div> <div v-show="!shouldHideEditor" ref="editor" + class="multi-file-editor-holder" > </div> </div> diff --git a/app/assets/javascripts/repo/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue index 75787ad6103..09ca11531b1 100644 --- a/app/assets/javascripts/repo/components/repo_file.vue +++ b/app/assets/javascripts/ide/components/repo_file.vue @@ -1,7 +1,8 @@ <script> - import { mapActions, mapGetters } from 'vuex'; + import { mapState } from 'vuex'; import timeAgoMixin from '../../vue_shared/mixins/timeago'; import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue'; + import newDropdown from './new_dropdown/index.vue'; export default { mixins: [ @@ -9,20 +10,22 @@ ], components: { skeletonLoadingContainer, + newDropdown, }, props: { file: { type: Object, required: true, }, + showExtraColumns: { + type: Boolean, + default: false, + }, }, computed: { - ...mapGetters([ - 'isCollapsed', + ...mapState([ + 'leftPanelCollapsed', ]), - isSubmodule() { - return this.file.type === 'submodule'; - }, fileIcon() { return { 'fa-spinner fa-spin': this.file.loading, @@ -30,6 +33,12 @@ 'fa-folder-open': !this.file.loading && this.file.opened, }; }, + isSubmodule() { + return this.file.type === 'submodule'; + }, + isTree() { + return this.file.type === 'tree'; + }, levelIndentation() { return { marginLeft: `${this.file.level * 16}px`, @@ -39,13 +48,39 @@ return this.file.id.substr(0, 8); }, submoduleColSpan() { - return !this.isCollapsed && this.isSubmodule ? 3 : 1; + return !this.leftPanelCollapsed && this.isSubmodule ? 3 : 1; + }, + fileClass() { + if (this.file.type === 'blob') { + if (this.file.active) { + return 'file-open file-active'; + } + return this.file.opened ? 'file-open' : ''; + } + return ''; + }, + changedClass() { + return { + 'fa-circle unsaved-icon': this.file.changed || this.file.tempFile, + }; }, }, methods: { - ...mapActions([ - 'clickedTreeRow', - ]), + clickFile(row) { + // Manual Action if a tree is selected/opened + if (this.file.type === 'tree' && this.$router.currentRoute.path === `/project${row.url}`) { + this.$store.dispatch('toggleTreeOpen', { + endpoint: this.file.url, + tree: this.file, + }); + } + this.$router.push(`/project${row.url}`); + }, + }, + updated() { + if (this.file.type === 'blob' && this.file.active) { + this.$el.scrollIntoView(); + } }, }; </script> @@ -53,7 +88,8 @@ <template> <tr class="file" - @click.prevent="clickedTreeRow(file)"> + :class="fileClass" + @click="clickFile(file)"> <td class="multi-file-table-name" :colspan="submoduleColSpan" @@ -66,11 +102,23 @@ > </i> <a - :href="file.url" class="repo-file-name" > {{ file.name }} </a> + <new-dropdown + v-if="isTree" + :project-id="file.projectId" + :branch="file.branchId" + :path="file.path" + :parent="file"/> + <i + class="fa" + v-if="changedClass" + :class="changedClass" + aria-hidden="true" + > + </i> <template v-if="isSubmodule && file.id"> @ <span class="commit-sha"> @@ -84,7 +132,7 @@ </template> </td> - <template v-if="!isCollapsed && !isSubmodule"> + <template v-if="showExtraColumns && !isSubmodule"> <td class="multi-file-table-col-commit-message hidden-sm hidden-xs"> <a v-if="file.lastCommit.message" diff --git a/app/assets/javascripts/repo/components/repo_file_buttons.vue b/app/assets/javascripts/ide/components/repo_file_buttons.vue index 34f0d51819a..34f0d51819a 100644 --- a/app/assets/javascripts/repo/components/repo_file_buttons.vue +++ b/app/assets/javascripts/ide/components/repo_file_buttons.vue diff --git a/app/assets/javascripts/repo/components/repo_loading_file.vue b/app/assets/javascripts/ide/components/repo_loading_file.vue index 8fa637d771f..7eb840c7608 100644 --- a/app/assets/javascripts/repo/components/repo_loading_file.vue +++ b/app/assets/javascripts/ide/components/repo_loading_file.vue @@ -1,5 +1,5 @@ <script> - import { mapGetters } from 'vuex'; + import { mapState } from 'vuex'; import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue'; export default { @@ -7,8 +7,8 @@ skeletonLoadingContainer, }, computed: { - ...mapGetters([ - 'isCollapsed', + ...mapState([ + 'leftPanelCollapsed', ]), }, }; @@ -24,7 +24,7 @@ :small="true" /> </td> - <template v-if="!isCollapsed"> + <template v-if="!leftPanelCollapsed"> <td class="hidden-sm hidden-xs"> <skeleton-loading-container diff --git a/app/assets/javascripts/repo/components/repo_prev_directory.vue b/app/assets/javascripts/ide/components/repo_prev_directory.vue index a2b305bbd05..7cd359ea4ed 100644 --- a/app/assets/javascripts/repo/components/repo_prev_directory.vue +++ b/app/assets/javascripts/ide/components/repo_prev_directory.vue @@ -1,16 +1,14 @@ <script> - import { mapGetters, mapState, mapActions } from 'vuex'; + import { mapState, mapActions } from 'vuex'; export default { computed: { ...mapState([ 'parentTreeUrl', - ]), - ...mapGetters([ - 'isCollapsed', + 'leftPanelCollapsed', ]), colSpanCondition() { - return this.isCollapsed ? undefined : 3; + return this.leftPanelCollapsed ? undefined : 3; }, }, methods: { diff --git a/app/assets/javascripts/repo/components/repo_preview.vue b/app/assets/javascripts/ide/components/repo_preview.vue index 3d1e0297bd5..3d1e0297bd5 100644 --- a/app/assets/javascripts/repo/components/repo_preview.vue +++ b/app/assets/javascripts/ide/components/repo_preview.vue diff --git a/app/assets/javascripts/repo/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue index fb29a60df66..5bd63ac9ec5 100644 --- a/app/assets/javascripts/repo/components/repo_tab.vue +++ b/app/assets/javascripts/ide/components/repo_tab.vue @@ -27,16 +27,18 @@ export default { methods: { ...mapActions([ - 'setFileActive', 'closeFile', ]), + clickFile(tab) { + this.$router.push(`/project${tab.url}`); + }, }, }; </script> <template> <li - @click="setFileActive(tab)" + @click="clickFile(tab)" > <button type="button" diff --git a/app/assets/javascripts/repo/components/repo_tabs.vue b/app/assets/javascripts/ide/components/repo_tabs.vue index ab0bef4f0ac..ab0bef4f0ac 100644 --- a/app/assets/javascripts/repo/components/repo_tabs.vue +++ b/app/assets/javascripts/ide/components/repo_tabs.vue diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js new file mode 100644 index 00000000000..a9cbf8e370f --- /dev/null +++ b/app/assets/javascripts/ide/ide_router.js @@ -0,0 +1,101 @@ +import Vue from 'vue'; +import VueRouter from 'vue-router'; +import store from './stores'; +import flash from '../flash'; +import { + getTreeEntry, +} from './stores/utils'; + +Vue.use(VueRouter); + +/** + * Routes below /-/ide/: + +/project/h5bp/html5-boilerplate/blob/master +/project/h5bp/html5-boilerplate/blob/master/app/js/test.js + +/project/h5bp/html5-boilerplate/mr/123 +/project/h5bp/html5-boilerplate/mr/123/app/js/test.js + +/workspace/123 +/workspace/project/h5bp/html5-boilerplate/blob/my-special-branch +/workspace/project/h5bp/html5-boilerplate/mr/123 + +/ = /workspace + +/settings +*/ + +// Unfortunately Vue Router doesn't work without at least a fake component +// If you do only data handling +const EmptyRouterComponent = { + render(createElement) { + return createElement('div'); + }, +}; + +const router = new VueRouter({ + mode: 'history', + base: `${gon.relative_url_root}/-/ide/`, + routes: [ + { + path: '/project/:namespace/:project', + component: EmptyRouterComponent, + children: [ + { + path: ':targetmode/:branch/*', + component: EmptyRouterComponent, + }, + { + path: 'mr/:mrid', + component: EmptyRouterComponent, + }, + ], + }, + ], +}); + +router.beforeEach((to, from, next) => { + if (to.params.namespace && to.params.project) { + store.dispatch('getProjectData', { + namespace: to.params.namespace, + projectId: to.params.project, + }) + .then(() => { + const fullProjectId = `${to.params.namespace}/${to.params.project}`; + + if (to.params.branch) { + store.dispatch('getBranchData', { + projectId: fullProjectId, + branchId: to.params.branch, + }); + + store.dispatch('getTreeData', { + projectId: fullProjectId, + branch: to.params.branch, + endpoint: `/tree/${to.params.branch}`, + }) + .then(() => { + if (to.params[0]) { + const treeEntry = getTreeEntry(store, `${to.params.namespace}/${to.params.project}/${to.params.branch}`, to.params[0]); + if (treeEntry) { + store.dispatch('handleTreeEntryAction', treeEntry); + } + } + }) + .catch((e) => { + flash('Error while loading the branch files. Please try again.'); + throw e; + }); + } + }) + .catch((e) => { + flash('Error while loading the project data. Please try again.'); + throw e; + }); + } + + next(); +}); + +export default router; diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js new file mode 100644 index 00000000000..a96bd339f51 --- /dev/null +++ b/app/assets/javascripts/ide/index.js @@ -0,0 +1,55 @@ +import Vue from 'vue'; +import { mapActions } from 'vuex'; +import { convertPermissionToBoolean } from '../lib/utils/common_utils'; +import ide from './components/ide.vue'; + +import store from './stores'; +import router from './ide_router'; +import Translate from '../vue_shared/translate'; +import ContextualSidebar from '../contextual_sidebar'; + +function initIde(el) { + if (!el) return null; + + return new Vue({ + el, + store, + router, + components: { + ide, + }, + methods: { + ...mapActions([ + 'setInitialData', + ]), + }, + created() { + const data = el.dataset; + + this.setInitialData({ + endpoints: { + rootEndpoint: data.url, + newMergeRequestUrl: data.newMergeRequestUrl, + rootUrl: data.rootUrl, + }, + canCommit: convertPermissionToBoolean(data.canCommit), + onTopOfBranch: convertPermissionToBoolean(data.onTopOfBranch), + path: data.currentPath, + isRoot: convertPermissionToBoolean(data.root), + isInitialRoot: convertPermissionToBoolean(data.root), + }); + }, + render(createElement) { + return createElement('ide'); + }, + }); +} + +const ideElement = document.getElementById('ide'); + +Vue.use(Translate); + +initIde(ideElement); + +const contextualSidebar = new ContextualSidebar(); +contextualSidebar.bindEvents(); diff --git a/app/assets/javascripts/repo/lib/common/disposable.js b/app/assets/javascripts/ide/lib/common/disposable.js index 84b29bdb600..84b29bdb600 100644 --- a/app/assets/javascripts/repo/lib/common/disposable.js +++ b/app/assets/javascripts/ide/lib/common/disposable.js diff --git a/app/assets/javascripts/repo/lib/common/model.js b/app/assets/javascripts/ide/lib/common/model.js index 23c4811e6c0..14d9fe4771e 100644 --- a/app/assets/javascripts/repo/lib/common/model.js +++ b/app/assets/javascripts/ide/lib/common/model.js @@ -28,6 +28,14 @@ export default class Model { return this.model.uri.toString(); } + get language() { + return this.model.getModeId(); + } + + get eol() { + return this.model.getEOL() === '\n' ? 'LF' : 'CRLF'; + } + get path() { return this.file.path; } diff --git a/app/assets/javascripts/repo/lib/common/model_manager.js b/app/assets/javascripts/ide/lib/common/model_manager.js index fd462252795..fd462252795 100644 --- a/app/assets/javascripts/repo/lib/common/model_manager.js +++ b/app/assets/javascripts/ide/lib/common/model_manager.js diff --git a/app/assets/javascripts/repo/lib/decorations/controller.js b/app/assets/javascripts/ide/lib/decorations/controller.js index 0954b7973c4..0954b7973c4 100644 --- a/app/assets/javascripts/repo/lib/decorations/controller.js +++ b/app/assets/javascripts/ide/lib/decorations/controller.js diff --git a/app/assets/javascripts/repo/lib/diff/controller.js b/app/assets/javascripts/ide/lib/diff/controller.js index dc0b1c95e59..dc0b1c95e59 100644 --- a/app/assets/javascripts/repo/lib/diff/controller.js +++ b/app/assets/javascripts/ide/lib/diff/controller.js diff --git a/app/assets/javascripts/repo/lib/diff/diff.js b/app/assets/javascripts/ide/lib/diff/diff.js index 0e37f5c4704..0e37f5c4704 100644 --- a/app/assets/javascripts/repo/lib/diff/diff.js +++ b/app/assets/javascripts/ide/lib/diff/diff.js diff --git a/app/assets/javascripts/repo/lib/diff/diff_worker.js b/app/assets/javascripts/ide/lib/diff/diff_worker.js index e74c4046330..e74c4046330 100644 --- a/app/assets/javascripts/repo/lib/diff/diff_worker.js +++ b/app/assets/javascripts/ide/lib/diff/diff_worker.js diff --git a/app/assets/javascripts/repo/lib/editor.js b/app/assets/javascripts/ide/lib/editor.js index db499444402..51e202b9348 100644 --- a/app/assets/javascripts/repo/lib/editor.js +++ b/app/assets/javascripts/ide/lib/editor.js @@ -22,6 +22,11 @@ export default class Editor { this.modelManager = new ModelManager(this.monaco), this.decorationsController = new DecorationsController(this), ); + + this.debouncedUpdate = _.debounce(() => { + this.updateDimensions(); + }, 200); + window.addEventListener('resize', this.debouncedUpdate, false); } createInstance(domElement) { @@ -32,6 +37,9 @@ export default class Editor { readOnly: false, contextmenu: true, scrollBeyondLastLine: false, + minimap: { + enabled: false, + }, }), this.dirtyDiffController = new DirtyDiffController( this.modelManager, this.decorationsController, @@ -70,10 +78,32 @@ export default class Editor { dispose() { this.disposable.dispose(); + window.removeEventListener('resize', this.debouncedUpdate); // dispose main monaco instance if (this.instance) { this.instance = null; } } + + updateDimensions() { + this.instance.layout(); + } + + setPosition({ lineNumber, column }) { + this.instance.revealPositionInCenter({ + lineNumber, + column, + }); + this.instance.setPosition({ + lineNumber, + column, + }); + } + + onPositionChange(cb) { + this.disposable.add( + this.instance.onDidChangeCursorPosition(e => cb(this.instance, e)), + ); + } } diff --git a/app/assets/javascripts/repo/lib/editor_options.js b/app/assets/javascripts/ide/lib/editor_options.js index 701affc466e..701affc466e 100644 --- a/app/assets/javascripts/repo/lib/editor_options.js +++ b/app/assets/javascripts/ide/lib/editor_options.js diff --git a/app/assets/javascripts/repo/monaco_loader.js b/app/assets/javascripts/ide/monaco_loader.js index af83a1ec0b4..af83a1ec0b4 100644 --- a/app/assets/javascripts/repo/monaco_loader.js +++ b/app/assets/javascripts/ide/monaco_loader.js diff --git a/app/assets/javascripts/repo/services/index.js b/app/assets/javascripts/ide/services/index.js index 994d325e991..1fb24e93f2e 100644 --- a/app/assets/javascripts/repo/services/index.js +++ b/app/assets/javascripts/ide/services/index.js @@ -23,8 +23,11 @@ export default { return Vue.http.get(file.rawPath, { params: { format: 'json' } }) .then(res => res.text()); }, - getBranchData(projectId, currentBranch) { - return Api.branchSingle(projectId, currentBranch); + getProjectData(namespace, project) { + return Api.project(`${namespace}/${project}`); + }, + getBranchData(projectId, currentBranchId) { + return Api.branchSingle(projectId, currentBranchId); }, createBranch(projectId, payload) { const url = Api.buildUrl(Api.createBranchPath).replace(':id', projectId); diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js new file mode 100644 index 00000000000..c01046c8c76 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions.js @@ -0,0 +1,179 @@ +import Vue from 'vue'; +import { visitUrl } from '../../lib/utils/url_utility'; +import flash from '../../flash'; +import service from '../services'; +import * as types from './mutation_types'; + +export const redirectToUrl = (_, url) => visitUrl(url); + +export const setInitialData = ({ commit }, data) => + commit(types.SET_INITIAL_DATA, data); + +export const closeDiscardPopup = ({ commit }) => + commit(types.TOGGLE_DISCARD_POPUP, false); + +export const discardAllChanges = ({ commit, getters, dispatch }) => { + const changedFiles = getters.changedFiles; + + changedFiles.forEach((file) => { + commit(types.DISCARD_FILE_CHANGES, file); + + if (file.tempFile) { + dispatch('closeFile', { file, force: true }); + } + }); +}; + +export const closeAllFiles = ({ state, dispatch }) => { + state.openFiles.forEach(file => dispatch('closeFile', { file })); +}; + +export const toggleEditMode = ( + { state, commit, getters, dispatch }, + force = false, +) => { + const changedFiles = getters.changedFiles; + + if (changedFiles.length && !force) { + commit(types.TOGGLE_DISCARD_POPUP, true); + } else { + commit(types.TOGGLE_EDIT_MODE); + commit(types.TOGGLE_DISCARD_POPUP, false); + dispatch('toggleBlobView'); + + if (!state.editMode) { + dispatch('discardAllChanges'); + } + } +}; + +export const toggleBlobView = ({ commit, state }) => { + if (state.editMode) { + commit(types.SET_EDIT_MODE); + } else { + commit(types.SET_PREVIEW_MODE); + } +}; + +export const setPanelCollapsedStatus = ({ commit }, { side, collapsed }) => { + if (side === 'left') { + commit(types.SET_LEFT_PANEL_COLLAPSED, collapsed); + } else { + commit(types.SET_RIGHT_PANEL_COLLAPSED, collapsed); + } +}; + +export const checkCommitStatus = ({ state }) => + service + .getBranchData(state.currentProjectId, state.currentBranchId) + .then((data) => { + const { id } = data.commit; + const selectedBranch = + state.projects[state.currentProjectId].branches[state.currentBranchId]; + + if (selectedBranch.workingReference !== id) { + return true; + } + + return false; + }) + .catch(() => flash('Error checking branch data. Please try again.')); + +export const commitChanges = ( + { commit, state, dispatch, getters }, + { payload, newMr }, +) => + service + .commit(state.currentProjectId, payload) + .then((data) => { + const { branch } = payload; + if (!data.short_id) { + flash(data.message); + return; + } + + const selectedProject = state.projects[state.currentProjectId]; + const lastCommit = { + commit_path: `${selectedProject.web_url}/commit/${data.id}`, + commit: { + message: data.message, + authored_date: data.committed_date, + }, + }; + + flash( + `Your changes have been committed. Commit ${data.short_id} with ${ + data.stats.additions + } additions, ${data.stats.deletions} deletions.`, + 'notice', + ); + + if (newMr) { + dispatch( + 'redirectToUrl', + `${ + selectedProject.web_url + }/merge_requests/new?merge_request%5Bsource_branch%5D=${branch}`, + ); + } else { + commit(types.SET_BRANCH_WORKING_REFERENCE, { + projectId: state.currentProjectId, + branchId: state.currentBranchId, + reference: data.id, + }); + + getters.changedFiles.forEach((entry) => { + commit(types.SET_LAST_COMMIT_DATA, { + entry, + lastCommit, + }); + }); + + dispatch('discardAllChanges'); + dispatch('closeAllFiles'); + + window.scrollTo(0, 0); + } + }) + .catch(() => flash('Error committing changes. Please try again.')); + +export const createTempEntry = ( + { state, dispatch }, + { projectId, branchId, parent, name, type, content = '', base64 = false }, +) => { + const selectedParent = parent || state.trees[`${projectId}/${branchId}`]; + if (type === 'tree') { + dispatch('createTempTree', { + projectId, + branchId, + parent: selectedParent, + name, + }); + } else if (type === 'blob') { + dispatch('createTempFile', { + projectId, + branchId, + parent: selectedParent, + name, + base64, + content, + }); + } +}; + +export const scrollToTab = () => { + Vue.nextTick(() => { + const tabs = document.getElementById('tabs'); + + if (tabs) { + const tabEl = tabs.querySelector('.active .repo-tab'); + + tabEl.focus(); + } + }); +}; + +export * from './actions/tree'; +export * from './actions/file'; +export * from './actions/project'; +export * from './actions/branch'; diff --git a/app/assets/javascripts/ide/stores/actions/branch.js b/app/assets/javascripts/ide/stores/actions/branch.js new file mode 100644 index 00000000000..32bdf7fec22 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions/branch.js @@ -0,0 +1,43 @@ +import service from '../../services'; +import flash from '../../../flash'; +import * as types from '../mutation_types'; + +export const getBranchData = ( + { commit, state, dispatch }, + { projectId, branchId, force = false } = {}, +) => new Promise((resolve, reject) => { + if ((typeof state.projects[`${projectId}`] === 'undefined' || + !state.projects[`${projectId}`].branches[branchId]) + || force) { + service.getBranchData(`${projectId}`, branchId) + .then((data) => { + const { id } = data.commit; + commit(types.SET_BRANCH, { projectPath: `${projectId}`, branchName: branchId, branch: data }); + commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id }); + resolve(data); + }) + .catch(() => { + flash('Error loading branch data. Please try again.'); + reject(new Error(`Branch not loaded - ${projectId}/${branchId}`)); + }); + } else { + resolve(state.projects[`${projectId}`].branches[branchId]); + } +}); + +export const createNewBranch = ({ state, commit }, branch) => service.createBranch( + state.currentProjectId, + { + branch, + ref: state.currentBranchId, + }, +) +.then(res => res.json()) +.then((data) => { + const branchName = data.name; + const url = location.href.replace(state.currentBranchId, branchName); + + if (this.$router) this.$router.push(url); + + commit(types.SET_CURRENT_BRANCH, branchName); +}); diff --git a/app/assets/javascripts/repo/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index 5bae4fa826a..0f27d5bf1c3 100644 --- a/app/assets/javascripts/repo/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -2,9 +2,9 @@ import { normalizeHeaders } from '../../../lib/utils/common_utils'; import flash from '../../../flash'; import service from '../../services'; import * as types from '../mutation_types'; +import router from '../../ide_router'; import { findEntry, - pushState, setPageTitle, createTemp, findIndexOfFile, @@ -25,7 +25,7 @@ export const closeFile = ({ commit, state, dispatch }, { file, force = false }) dispatch('setFileActive', nextFileToOpen); } else if (!state.openFiles.length) { - pushState(file.parentTreeUrl); + router.push(`/project/${file.projectId}/tree/${file.branchId}/`); } dispatch('getLastCommitData'); @@ -45,6 +45,9 @@ export const setFileActive = ({ commit, state, getters, dispatch }, file) => { // reset hash for line highlighting location.hash = ''; + + commit(types.SET_CURRENT_PROJECT, file.projectId); + commit(types.SET_CURRENT_BRANCH, file.branchId); }; export const getFileData = ({ state, commit, dispatch }, file) => { @@ -63,8 +66,6 @@ export const getFileData = ({ state, commit, dispatch }, file) => { commit(types.TOGGLE_FILE_OPEN, file); dispatch('setFileActive', file); commit(types.TOGGLE_LOADING, file); - - pushState(file.url); }) .catch(() => { commit(types.TOGGLE_LOADING, file); @@ -82,21 +83,39 @@ export const changeFileContent = ({ commit }, { file, content }) => { commit(types.UPDATE_FILE_CONTENT, { file, content }); }; -export const createTempFile = ({ state, commit, dispatch }, { tree, name, content = '', base64 = '' }) => { +export const setFileLanguage = ({ state, commit }, { fileLanguage }) => { + commit(types.SET_FILE_LANGUAGE, { file: state.selectedFile, fileLanguage }); +}; + +export const setFileEOL = ({ state, commit }, { eol }) => { + commit(types.SET_FILE_EOL, { file: state.selectedFile, eol }); +}; + +export const setEditorPosition = ({ state, commit }, { editorRow, editorColumn }) => { + commit(types.SET_FILE_POSITION, { file: state.selectedFile, editorRow, editorColumn }); +}; + +export const createTempFile = ({ state, commit, dispatch }, { projectId, branchId, parent, name, content = '', base64 = '' }) => { + const path = parent.path !== undefined ? parent.path : ''; + // We need to do the replacement otherwise the web_url + file.url duplicate + const newUrl = `/${projectId}/blob/${branchId}/${path}${path ? '/' : ''}${name}`; const file = createTemp({ - name: name.replace(`${state.path}/`, ''), - path: tree.path, + projectId, + branchId, + name: name.replace(`${path}/`, ''), + path, type: 'blob', - level: tree.level !== undefined ? tree.level + 1 : 0, + level: parent.level !== undefined ? parent.level + 1 : 0, changed: true, content, base64, + url: newUrl, }); - if (findEntry(tree, 'blob', file.name)) return flash(`The name "${file.name}" is already taken in this directory.`); + if (findEntry(parent.tree, 'blob', file.name)) return flash(`The name "${file.name}" is already taken in this directory.`); commit(types.CREATE_TMP_FILE, { - parent: tree, + parent, file, }); commit(types.TOGGLE_FILE_OPEN, file); @@ -106,5 +125,7 @@ export const createTempFile = ({ state, commit, dispatch }, { tree, name, conten dispatch('toggleEditMode', true); } + router.push(`/project${file.url}`); + return Promise.resolve(file); }; diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js new file mode 100644 index 00000000000..75e332090cb --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions/project.js @@ -0,0 +1,25 @@ +import service from '../../services'; +import flash from '../../../flash'; +import * as types from '../mutation_types'; + +// eslint-disable-next-line import/prefer-default-export +export const getProjectData = ( + { commit, state, dispatch }, + { namespace, projectId, force = false } = {}, +) => new Promise((resolve, reject) => { + if (!state.projects[`${namespace}/${projectId}`] || force) { + service.getProjectData(namespace, projectId) + .then(res => res.data) + .then((data) => { + commit(types.SET_PROJECT, { projectPath: `${namespace}/${projectId}`, project: data }); + if (!state.currentProjectId) commit(types.SET_CURRENT_PROJECT, `${namespace}/${projectId}`); + resolve(data); + }) + .catch(() => { + flash('Error loading project data. Please try again.'); + reject(new Error(`Project not loaded ${namespace}/${projectId}`)); + }); + } else { + resolve(state.projects[`${namespace}/${projectId}`]); + } +}); diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js new file mode 100644 index 00000000000..25909400a75 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions/tree.js @@ -0,0 +1,188 @@ +import { visitUrl } from '../../../lib/utils/url_utility'; +import { normalizeHeaders } from '../../../lib/utils/common_utils'; +import flash from '../../../flash'; +import service from '../../services'; +import * as types from '../mutation_types'; +import router from '../../ide_router'; +import { + setPageTitle, + findEntry, + createTemp, + createOrMergeEntry, +} from '../utils'; + +export const getTreeData = ( + { commit, state, dispatch }, + { endpoint, tree = null, projectId, branch, force = false } = {}, +) => new Promise((resolve, reject) => { + // We already have the base tree so we resolve immediately + if (!tree && state.trees[`${projectId}/${branch}`] && !force) { + resolve(); + } else { + if (tree) commit(types.TOGGLE_LOADING, tree); + const selectedProject = state.projects[projectId]; + // We are merging the web_url that we got on the project info with the endpoint + // we got on the tree entry, as both contain the projectId, we replace it in the tree endpoint + const completeEndpoint = selectedProject.web_url + (endpoint).replace(projectId, ''); + if (completeEndpoint && (!tree || !tree.tempFile)) { + service.getTreeData(completeEndpoint) + .then((res) => { + const pageTitle = decodeURI(normalizeHeaders(res.headers)['PAGE-TITLE']); + + setPageTitle(pageTitle); + + return res.json(); + }) + .then((data) => { + if (!state.isInitialRoot) { + commit(types.SET_ROOT, data.path === '/'); + } + + dispatch('updateDirectoryData', { data, tree, projectId, branch }); + const selectedTree = tree || state.trees[`${projectId}/${branch}`]; + + commit(types.SET_PARENT_TREE_URL, data.parent_tree_url); + commit(types.SET_LAST_COMMIT_URL, { tree: selectedTree, url: data.last_commit_path }); + if (tree) commit(types.TOGGLE_LOADING, selectedTree); + + const prevLastCommitPath = selectedTree.lastCommitPath; + if (prevLastCommitPath !== null) { + dispatch('getLastCommitData', selectedTree); + } + resolve(data); + }) + .catch((e) => { + flash('Error loading tree data. Please try again.'); + if (tree) commit(types.TOGGLE_LOADING, tree); + reject(e); + }); + } else { + resolve(); + } + } +}); + +export const toggleTreeOpen = ({ commit, dispatch }, { endpoint, tree }) => { + if (tree.opened) { + // send empty data to clear the tree + const data = { trees: [], blobs: [], submodules: [] }; + + dispatch('updateDirectoryData', { data, tree, projectId: tree.projectId, branchId: tree.branchId }); + } else { + dispatch('getTreeData', { endpoint, tree, projectId: tree.projectId, branch: tree.branchId }); + } + + commit(types.TOGGLE_TREE_OPEN, tree); +}; + +export const handleTreeEntryAction = ({ commit, dispatch }, row) => { + if (row.type === 'tree') { + dispatch('toggleTreeOpen', { + endpoint: row.url, + tree: row, + }); + } else if (row.type === 'submodule') { + commit(types.TOGGLE_LOADING, row); + visitUrl(row.url); + } else if (row.type === 'blob' && row.opened) { + dispatch('setFileActive', row); + } else { + dispatch('getFileData', row); + } +}; + +export const createTempTree = ( + { state, commit, dispatch }, + { projectId, branchId, parent, name }, +) => { + let selectedTree = parent; + const dirNames = name.replace(new RegExp(`^${state.path}/`), '').split('/'); + + dirNames.forEach((dirName) => { + const foundEntry = findEntry(selectedTree.tree, 'tree', dirName); + + if (!foundEntry) { + const path = selectedTree.path !== undefined ? selectedTree.path : ''; + const tmpEntry = createTemp({ + projectId, + branchId, + name: dirName, + path, + type: 'tree', + level: selectedTree.level !== undefined ? selectedTree.level + 1 : 0, + tree: [], + url: `/${projectId}/blob/${branchId}/${path}${path ? '/' : ''}${dirName}`, + }); + + commit(types.CREATE_TMP_TREE, { + parent: selectedTree, + tmpEntry, + }); + commit(types.TOGGLE_TREE_OPEN, tmpEntry); + + router.push(`/project${tmpEntry.url}`); + + selectedTree = tmpEntry; + } else { + selectedTree = foundEntry; + } + }); +}; + +export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = state) => { + if (!tree || tree.lastCommitPath === null || !tree.lastCommitPath) return; + + service.getTreeLastCommit(tree.lastCommitPath) + .then((res) => { + const lastCommitPath = normalizeHeaders(res.headers)['MORE-LOGS-URL'] || null; + + commit(types.SET_LAST_COMMIT_URL, { tree, url: lastCommitPath }); + + return res.json(); + }) + .then((data) => { + data.forEach((lastCommit) => { + const entry = findEntry(tree.tree, lastCommit.type, lastCommit.file_name); + + if (entry) { + commit(types.SET_LAST_COMMIT_DATA, { entry, lastCommit }); + } + }); + + dispatch('getLastCommitData', tree); + }) + .catch(() => flash('Error fetching log data.')); +}; + +export const updateDirectoryData = ( + { commit, state }, + { data, tree, projectId, branch }, +) => { + if (!tree) { + const existingTree = state.trees[`${projectId}/${branch}`]; + if (!existingTree) { + commit(types.CREATE_TREE, { treePath: `${projectId}/${branch}` }); + } + } + + const selectedTree = tree || state.trees[`${projectId}/${branch}`]; + const level = selectedTree.level !== undefined ? selectedTree.level + 1 : 0; + const parentTreeUrl = data.parent_tree_url ? `${data.parent_tree_url}${data.path}` : state.endpoints.rootUrl; + const createEntry = (entry, type) => createOrMergeEntry({ + tree: selectedTree, + projectId: `${projectId}`, + branchId: branch, + entry, + level, + type, + parentTreeUrl, + }); + + const formattedData = [ + ...data.trees.map(t => createEntry(t, 'tree')), + ...data.submodules.map(m => createEntry(m, 'submodule')), + ...data.blobs.map(b => createEntry(b, 'blob')), + ]; + + commit(types.SET_DIRECTORY_DATA, { tree: selectedTree, data: formattedData }); +}; diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js new file mode 100644 index 00000000000..6b51ccff817 --- /dev/null +++ b/app/assets/javascripts/ide/stores/getters.js @@ -0,0 +1,19 @@ +export const changedFiles = state => state.openFiles.filter(file => file.changed); + +export const activeFile = state => state.openFiles.find(file => file.active) || null; + +export const activeFileExtension = (state) => { + const file = activeFile(state); + return file ? `.${file.path.split('.').pop()}` : ''; +}; + +export const canEditFile = (state) => { + const currentActiveFile = activeFile(state); + + return state.canCommit && + (currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary); +}; + +export const addedFiles = state => changedFiles(state).filter(f => f.tempFile); + +export const modifiedFiles = state => changedFiles(state).filter(f => !f.tempFile); diff --git a/app/assets/javascripts/repo/stores/index.js b/app/assets/javascripts/ide/stores/index.js index 6ac9bfd8189..6ac9bfd8189 100644 --- a/app/assets/javascripts/repo/stores/index.js +++ b/app/assets/javascripts/ide/stores/index.js diff --git a/app/assets/javascripts/repo/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index bc3390f1506..4e3c10972ba 100644 --- a/app/assets/javascripts/repo/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -1,16 +1,27 @@ export const SET_INITIAL_DATA = 'SET_INITIAL_DATA'; export const TOGGLE_LOADING = 'TOGGLE_LOADING'; -export const SET_COMMIT_REF = 'SET_COMMIT_REF'; export const SET_PARENT_TREE_URL = 'SET_PARENT_TREE_URL'; export const SET_ROOT = 'SET_ROOT'; -export const SET_PREVIOUS_URL = 'SET_PREVIOUS_URL'; export const SET_LAST_COMMIT_DATA = 'SET_LAST_COMMIT_DATA'; +export const SET_LEFT_PANEL_COLLAPSED = 'SET_LEFT_PANEL_COLLAPSED'; +export const SET_RIGHT_PANEL_COLLAPSED = 'SET_RIGHT_PANEL_COLLAPSED'; + +// Project Mutation Types +export const SET_PROJECT = 'SET_PROJECT'; +export const SET_CURRENT_PROJECT = 'SET_CURRENT_PROJECT'; +export const TOGGLE_PROJECT_OPEN = 'TOGGLE_PROJECT_OPEN'; + +// Branch Mutation Types +export const SET_BRANCH = 'SET_BRANCH'; +export const SET_BRANCH_WORKING_REFERENCE = 'SET_BRANCH_WORKING_REFERENCE'; +export const TOGGLE_BRANCH_OPEN = 'TOGGLE_BRANCH_OPEN'; // Tree mutation types export const SET_DIRECTORY_DATA = 'SET_DIRECTORY_DATA'; export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN'; export const CREATE_TMP_TREE = 'CREATE_TMP_TREE'; export const SET_LAST_COMMIT_URL = 'SET_LAST_COMMIT_URL'; +export const CREATE_TREE = 'CREATE_TREE'; // File mutation types export const SET_FILE_DATA = 'SET_FILE_DATA'; @@ -18,6 +29,9 @@ export const TOGGLE_FILE_OPEN = 'TOGGLE_FILE_OPEN'; export const SET_FILE_ACTIVE = 'SET_FILE_ACTIVE'; export const SET_FILE_RAW_DATA = 'SET_FILE_RAW_DATA'; export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT'; +export const SET_FILE_LANGUAGE = 'SET_FILE_LANGUAGE'; +export const SET_FILE_POSITION = 'SET_FILE_POSITION'; +export const SET_FILE_EOL = 'SET_FILE_EOL'; export const DISCARD_FILE_CHANGES = 'DISCARD_FILE_CHANGES'; export const CREATE_TMP_FILE = 'CREATE_TMP_FILE'; @@ -28,3 +42,4 @@ export const TOGGLE_EDIT_MODE = 'TOGGLE_EDIT_MODE'; export const TOGGLE_DISCARD_POPUP = 'TOGGLE_DISCARD_POPUP'; export const SET_CURRENT_BRANCH = 'SET_CURRENT_BRANCH'; + diff --git a/app/assets/javascripts/repo/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index ae2ba5bedf7..2fed9019cb6 100644 --- a/app/assets/javascripts/repo/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -1,4 +1,5 @@ import * as types from './mutation_types'; +import projectMutations from './mutations/project'; import fileMutations from './mutations/file'; import treeMutations from './mutations/tree'; import branchMutations from './mutations/branch'; @@ -32,29 +33,32 @@ export default { discardPopupOpen, }); }, - [types.SET_COMMIT_REF](state, ref) { - Object.assign(state, { - currentRef: ref, - }); - }, [types.SET_ROOT](state, isRoot) { Object.assign(state, { isRoot, isInitialRoot: isRoot, }); }, - [types.SET_PREVIOUS_URL](state, previousUrl) { + [types.SET_LEFT_PANEL_COLLAPSED](state, collapsed) { + Object.assign(state, { + leftPanelCollapsed: collapsed, + }); + }, + [types.SET_RIGHT_PANEL_COLLAPSED](state, collapsed) { Object.assign(state, { - previousUrl, + rightPanelCollapsed: collapsed, }); }, [types.SET_LAST_COMMIT_DATA](state, { entry, lastCommit }) { Object.assign(entry.lastCommit, { + id: lastCommit.commit.id, url: lastCommit.commit_path, message: lastCommit.commit.message, + author: lastCommit.commit.author_name, updatedAt: lastCommit.commit.authored_date, }); }, + ...projectMutations, ...fileMutations, ...treeMutations, ...branchMutations, diff --git a/app/assets/javascripts/ide/stores/mutations/branch.js b/app/assets/javascripts/ide/stores/mutations/branch.js new file mode 100644 index 00000000000..04b9582c5bb --- /dev/null +++ b/app/assets/javascripts/ide/stores/mutations/branch.js @@ -0,0 +1,28 @@ +import * as types from '../mutation_types'; + +export default { + [types.SET_CURRENT_BRANCH](state, currentBranchId) { + Object.assign(state, { + currentBranchId, + }); + }, + [types.SET_BRANCH](state, { projectPath, branchName, branch }) { + // Add client side properties + Object.assign(branch, { + treeId: `${projectPath}/${branchName}`, + active: true, + workingReference: '', + }); + + Object.assign(state.projects[projectPath], { + branches: { + [branchName]: branch, + }, + }); + }, + [types.SET_BRANCH_WORKING_REFERENCE](state, { projectId, branchId, reference }) { + Object.assign(state.projects[projectId].branches[branchId], { + workingReference: reference, + }); + }, +}; diff --git a/app/assets/javascripts/repo/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js index f9ba80b9dc2..5f3655b0092 100644 --- a/app/assets/javascripts/repo/stores/mutations/file.js +++ b/app/assets/javascripts/ide/stores/mutations/file.js @@ -6,6 +6,10 @@ export default { Object.assign(file, { active, }); + + Object.assign(state, { + selectedFile: file, + }); }, [types.TOGGLE_FILE_OPEN](state, file) { Object.assign(file, { @@ -42,6 +46,22 @@ export default { changed, }); }, + [types.SET_FILE_LANGUAGE](state, { file, fileLanguage }) { + Object.assign(file, { + fileLanguage, + }); + }, + [types.SET_FILE_EOL](state, { file, eol }) { + Object.assign(file, { + eol, + }); + }, + [types.SET_FILE_POSITION](state, { file, editorRow, editorColumn }) { + Object.assign(file, { + editorRow, + editorColumn, + }); + }, [types.DISCARD_FILE_CHANGES](state, file) { Object.assign(file, { content: '', diff --git a/app/assets/javascripts/ide/stores/mutations/project.js b/app/assets/javascripts/ide/stores/mutations/project.js new file mode 100644 index 00000000000..2816562a919 --- /dev/null +++ b/app/assets/javascripts/ide/stores/mutations/project.js @@ -0,0 +1,23 @@ +import * as types from '../mutation_types'; + +export default { + [types.SET_CURRENT_PROJECT](state, currentProjectId) { + Object.assign(state, { + currentProjectId, + }); + }, + [types.SET_PROJECT](state, { projectPath, project }) { + // Add client side properties + Object.assign(project, { + tree: [], + branches: {}, + active: true, + }); + + Object.assign(state, { + projects: Object.assign({}, state.projects, { + [projectPath]: project, + }), + }); + }, +}; diff --git a/app/assets/javascripts/repo/stores/mutations/tree.js b/app/assets/javascripts/ide/stores/mutations/tree.js index 130221c9fda..4fe438ab465 100644 --- a/app/assets/javascripts/repo/stores/mutations/tree.js +++ b/app/assets/javascripts/ide/stores/mutations/tree.js @@ -6,6 +6,15 @@ export default { opened: !tree.opened, }); }, + [types.CREATE_TREE](state, { treePath }) { + Object.assign(state, { + trees: Object.assign({}, state.trees, { + [treePath]: { + tree: [], + }, + }), + }); + }, [types.SET_DIRECTORY_DATA](state, { data, tree }) { Object.assign(tree, { tree: data, diff --git a/app/assets/javascripts/repo/stores/state.js b/app/assets/javascripts/ide/stores/state.js index 0068834831e..539e382830f 100644 --- a/app/assets/javascripts/repo/stores/state.js +++ b/app/assets/javascripts/ide/stores/state.js @@ -1,10 +1,10 @@ export default () => ({ canCommit: false, - currentBranch: '', - currentBlobView: 'repo-preview', - currentRef: '', + currentProjectId: '', + currentBranchId: '', + currentBlobView: 'repo-editor', discardPopupOpen: false, - editMode: false, + editMode: true, endpoints: {}, isRoot: false, isInitialRoot: false, @@ -12,13 +12,11 @@ export default () => ({ loading: false, onTopOfBranch: false, openFiles: [], + selectedFile: null, path: '', - project: { - id: 0, - name: '', - url: '', - }, parentTreeUrl: '', - previousUrl: '', - tree: [], + trees: {}, + projects: {}, + leftPanelCollapsed: false, + rightPanelCollapsed: true, }); diff --git a/app/assets/javascripts/repo/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index fae1f4439a9..29e3ab5d040 100644 --- a/app/assets/javascripts/repo/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -2,6 +2,8 @@ export const dataStructure = () => ({ id: '', key: '', type: '', + projectId: '', + branchId: '', name: '', url: '', path: '', @@ -15,9 +17,11 @@ export const dataStructure = () => ({ changed: false, lastCommitPath: '', lastCommit: { + id: '', url: '', message: '', updatedAt: '', + author: '', }, tree_url: '', blamePath: '', @@ -31,11 +35,17 @@ export const dataStructure = () => ({ parentTreeUrl: '', renderError: false, base64: false, + editorRow: 1, + editorColumn: 1, + fileLanguage: '', + eol: '', }); export const decorateData = (entity) => { const { id, + projectId, + branchId, type, url, name, @@ -56,6 +66,8 @@ export const decorateData = (entity) => { return { ...dataStructure(), id, + projectId, + branchId, key: `${name}-${type}-${id}`, type, name, @@ -75,24 +87,51 @@ export const decorateData = (entity) => { }; }; -export const findEntry = (state, type, name) => state.tree.find( +/* + Takes the multi-dimensional tree and returns a flattened array. + This allows for the table to recursively render the table rows but keeps the data + structure nested to make it easier to add new files/directories. +*/ +export const treeList = (state, treeId) => { + const baseTree = state.trees[treeId]; + if (baseTree) { + const mapTree = arr => (!arr.tree || !arr.tree.length ? + [] : _.map(arr.tree, a => [a, mapTree(a)])); + + return _.chain(baseTree.tree) + .map(arr => [arr, mapTree(arr)]) + .flatten() + .value(); + } + return []; +}; + +export const getTree = state => (namespace, projectId, branch) => state.trees[`${namespace}/${projectId}/${branch}`]; + +export const getTreeEntry = (store, treeId, path) => { + const fileList = treeList(store.state, treeId); + return fileList ? fileList.find(file => file.path === path) : null; +}; + +export const findEntry = (tree, type, name) => tree.find( f => f.type === type && f.name === name, ); + export const findIndexOfFile = (state, file) => state.findIndex(f => f.path === file.path); export const setPageTitle = (title) => { document.title = title; }; -export const pushState = (url) => { - history.pushState({ url }, '', url); -}; - -export const createTemp = ({ name, path, type, level, changed, content, base64 }) => { +export const createTemp = ({ + projectId, branchId, name, path, type, level, changed, content, base64, url, +}) => { const treePath = path ? `${path}/${name}` : name; return decorateData({ id: new Date().getTime().toString(), + projectId, + branchId, name, type, tempFile: true, @@ -104,11 +143,18 @@ export const createTemp = ({ name, path, type, level, changed, content, base64 } level, base64, renderError: base64, + url, }); }; -export const createOrMergeEntry = ({ tree, entry, type, parentTreeUrl, level }) => { - const found = findEntry(tree, type, entry.name); +export const createOrMergeEntry = ({ tree, + projectId, + branchId, + entry, + type, + parentTreeUrl, + level }) => { + const found = findEntry(tree.tree || tree, type, entry.name); if (found) { return Object.assign({}, found, { @@ -120,6 +166,8 @@ export const createOrMergeEntry = ({ tree, entry, type, parentTreeUrl, level }) return decorateData({ ...entry, + projectId, + branchId, type, parentTreeUrl, level, diff --git a/app/assets/javascripts/new_commit_form.js b/app/assets/javascripts/new_commit_form.js index 6e152497d20..a2f0a44863f 100644 --- a/app/assets/javascripts/new_commit_form.js +++ b/app/assets/javascripts/new_commit_form.js @@ -6,11 +6,12 @@ export default class NewCommitForm { this.branchName = form.find('.js-branch-name'); this.originalBranch = form.find('.js-original-branch'); this.createMergeRequest = form.find('.js-create-merge-request'); - this.createMergeRequestContainer = form.find('.js-create-merge-request-container'); + this.createMergeRequestContainer = form.find( + '.js-create-merge-request-container', + ); this.branchName.keyup(this.renderDestination); this.renderDestination(); } - renderDestination() { var different; different = this.branchName.val() !== this.originalBranch.val(); @@ -23,6 +24,6 @@ export default class NewCommitForm { this.createMergeRequestContainer.hide(); this.createMergeRequest.prop('checked', false); } - return this.wasDifferent = different; + return (this.wasDifferent = different); } } diff --git a/app/assets/javascripts/pages/users/show/index.js b/app/assets/javascripts/pages/users/show/index.js new file mode 100644 index 00000000000..f18f98b4e9a --- /dev/null +++ b/app/assets/javascripts/pages/users/show/index.js @@ -0,0 +1,3 @@ +import UserCallout from '~/user_callout'; + +export default () => new UserCallout(); diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue index 751a20991af..831aa92ac4f 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue @@ -1,5 +1,6 @@ <script> import tooltip from '../../vue_shared/directives/tooltip'; + import icon from '../../vue_shared/components/icon.vue'; export default { props: { @@ -11,6 +12,9 @@ directives: { tooltip, }, + components: { + icon, + }, }; </script> <template> @@ -24,10 +28,9 @@ data-placement="top" data-toggle="dropdown" aria-label="Artifacts"> - <i - class="fa fa-download" - aria-hidden="true"> - </i> + <icon + name="download"> + </icon> <i class="fa fa-caret-down" aria-hidden="true"> diff --git a/app/assets/javascripts/repo/components/commit_sidebar/list.vue b/app/assets/javascripts/repo/components/commit_sidebar/list.vue deleted file mode 100644 index fb862e7bf01..00000000000 --- a/app/assets/javascripts/repo/components/commit_sidebar/list.vue +++ /dev/null @@ -1,89 +0,0 @@ -<script> - import icon from '../../../vue_shared/components/icon.vue'; - import listItem from './list_item.vue'; - import listCollapsed from './list_collapsed.vue'; - - export default { - components: { - icon, - listItem, - listCollapsed, - }, - props: { - title: { - type: String, - required: true, - }, - fileList: { - type: Array, - required: true, - }, - collapsed: { - type: Boolean, - required: true, - }, - }, - methods: { - toggleCollapsed() { - this.$emit('toggleCollapsed'); - }, - }, - }; -</script> - -<template> - <div class="multi-file-commit-panel-section"> - <header - class="multi-file-commit-panel-header" - :class="{ - 'is-collapsed': collapsed, - }" - > - <icon - name="list-bulleted" - :size="18" - css-classes="append-right-default" - /> - <template v-if="!collapsed"> - {{ title }} - <button - type="button" - class="btn btn-transparent multi-file-commit-panel-collapse-btn" - @click="toggleCollapsed" - > - <i - aria-hidden="true" - class="fa fa-angle-double-right" - > - </i> - </button> - </template> - </header> - <div class="multi-file-commit-list"> - <list-collapsed - v-if="collapsed" - /> - <template v-else> - <ul - v-if="fileList.length" - class="list-unstyled append-bottom-0" - > - <li - v-for="file in fileList" - :key="file.key" - > - <list-item - :file="file" - /> - </li> - </ul> - <div - v-else - class="help-block prepend-top-0" - > - No changes - </div> - </template> - </div> - </div> -</template> diff --git a/app/assets/javascripts/repo/components/new_dropdown/index.vue b/app/assets/javascripts/repo/components/new_dropdown/index.vue deleted file mode 100644 index 781404cf8ca..00000000000 --- a/app/assets/javascripts/repo/components/new_dropdown/index.vue +++ /dev/null @@ -1,89 +0,0 @@ -<script> - import { mapState } from 'vuex'; - import newModal from './modal.vue'; - import upload from './upload.vue'; - import icon from '../../../vue_shared/components/icon.vue'; - - export default { - components: { - icon, - newModal, - upload, - }, - data() { - return { - openModal: false, - modalType: '', - }; - }, - computed: { - ...mapState([ - 'path', - ]), - }, - methods: { - createNewItem(type) { - this.modalType = type; - this.toggleModalOpen(); - }, - toggleModalOpen() { - this.openModal = !this.openModal; - }, - }, - }; -</script> - -<template> - <div> - <ul class="breadcrumb repo-breadcrumb"> - <li class="dropdown"> - <button - type="button" - class="btn btn-default dropdown-toggle add-to-tree" - data-toggle="dropdown" - aria-label="Create new file or directory" - > - <icon - name="plus" - css-classes="pull-left" - /> - <icon - name="arrow-down" - css-classes="pull-left" - /> - </button> - <ul class="dropdown-menu"> - <li> - <a - href="#" - role="button" - @click.prevent="createNewItem('blob')" - > - {{ __('New file') }} - </a> - </li> - <li> - <upload - :path="path" - /> - </li> - <li> - <a - href="#" - role="button" - @click.prevent="createNewItem('tree')" - > - {{ __('New directory') }} - </a> - </li> - </ul> - </li> - </ul> - <new-modal - v-if="openModal" - :type="modalType" - :path="path" - @toggle="toggleModalOpen" - /> - </div> -</template> diff --git a/app/assets/javascripts/repo/components/repo.vue b/app/assets/javascripts/repo/components/repo.vue deleted file mode 100644 index a00e1e9d809..00000000000 --- a/app/assets/javascripts/repo/components/repo.vue +++ /dev/null @@ -1,63 +0,0 @@ -<script> -import { mapState, mapGetters } from 'vuex'; -import RepoSidebar from './repo_sidebar.vue'; -import RepoCommitSection from './repo_commit_section.vue'; -import RepoTabs from './repo_tabs.vue'; -import RepoFileButtons from './repo_file_buttons.vue'; -import RepoPreview from './repo_preview.vue'; -import repoEditor from './repo_editor.vue'; - -export default { - computed: { - ...mapState([ - 'currentBlobView', - ]), - ...mapGetters([ - 'isCollapsed', - 'changedFiles', - ]), - }, - components: { - RepoSidebar, - RepoTabs, - RepoFileButtons, - repoEditor, - RepoCommitSection, - RepoPreview, - }, - mounted() { - const returnValue = 'Are you sure you want to lose unsaved changes?'; - window.onbeforeunload = (e) => { - if (!this.changedFiles.length) return undefined; - - Object.assign(e, { - returnValue, - }); - return returnValue; - }; - }, -}; -</script> - -<template> - <div - class="multi-file" - :class="{ - 'is-collapsed': isCollapsed - }" - > - <repo-sidebar/> - <div - v-if="isCollapsed" - class="multi-file-edit-pane" - > - <repo-tabs /> - <component - class="multi-file-edit-pane-content" - :is="currentBlobView" - /> - <repo-file-buttons /> - </div> - <repo-commit-section /> - </div> -</template> diff --git a/app/assets/javascripts/repo/components/repo_sidebar.vue b/app/assets/javascripts/repo/components/repo_sidebar.vue deleted file mode 100644 index 4ea21913129..00000000000 --- a/app/assets/javascripts/repo/components/repo_sidebar.vue +++ /dev/null @@ -1,85 +0,0 @@ -<script> -import { mapState, mapGetters, mapActions } from 'vuex'; -import RepoPreviousDirectory from './repo_prev_directory.vue'; -import RepoFile from './repo_file.vue'; -import RepoLoadingFile from './repo_loading_file.vue'; - -export default { - components: { - 'repo-previous-directory': RepoPreviousDirectory, - 'repo-file': RepoFile, - 'repo-loading-file': RepoLoadingFile, - }, - created() { - window.addEventListener('popstate', this.popHistoryState); - }, - destroyed() { - window.removeEventListener('popstate', this.popHistoryState); - }, - mounted() { - this.getTreeData(); - }, - computed: { - ...mapState([ - 'loading', - 'isRoot', - ]), - ...mapState({ - projectName(state) { - return state.project.name; - }, - }), - ...mapGetters([ - 'treeList', - 'isCollapsed', - ]), - }, - methods: { - ...mapActions([ - 'getTreeData', - 'popHistoryState', - ]), - }, -}; -</script> - -<template> -<div class="ide-file-list"> - <table class="table"> - <thead> - <tr> - <th - v-if="isCollapsed" - > - </th> - <template v-else> - <th class="name multi-file-table-name"> - Name - </th> - <th class="hidden-sm hidden-xs last-commit"> - Last commit - </th> - <th class="hidden-xs last-update text-right"> - Last update - </th> - </template> - </tr> - </thead> - <tbody> - <repo-previous-directory - v-if="!isRoot && treeList.length" - /> - <repo-loading-file - v-if="!treeList.length && loading" - v-for="n in 5" - :key="n" - /> - <repo-file - v-for="file in treeList" - :key="file.key" - :file="file" - /> - </tbody> - </table> -</div> -</template> diff --git a/app/assets/javascripts/repo/index.js b/app/assets/javascripts/repo/index.js deleted file mode 100644 index b6801af7fcb..00000000000 --- a/app/assets/javascripts/repo/index.js +++ /dev/null @@ -1,106 +0,0 @@ -import Vue from 'vue'; -import { mapActions } from 'vuex'; -import { convertPermissionToBoolean } from '../lib/utils/common_utils'; -import Repo from './components/repo.vue'; -import RepoEditButton from './components/repo_edit_button.vue'; -import newBranchForm from './components/new_branch_form.vue'; -import newDropdown from './components/new_dropdown/index.vue'; -import store from './stores'; -import Translate from '../vue_shared/translate'; - -function initRepo(el) { - if (!el) return null; - - return new Vue({ - el, - store, - components: { - repo: Repo, - }, - methods: { - ...mapActions([ - 'setInitialData', - ]), - }, - created() { - const data = el.dataset; - - this.setInitialData({ - project: { - id: data.projectId, - name: data.projectName, - url: data.projectUrl, - }, - endpoints: { - rootEndpoint: data.url, - newMergeRequestUrl: data.newMergeRequestUrl, - rootUrl: data.rootUrl, - }, - canCommit: convertPermissionToBoolean(data.canCommit), - onTopOfBranch: convertPermissionToBoolean(data.onTopOfBranch), - currentRef: data.ref, - path: data.currentPath, - currentBranch: data.currentBranch, - isRoot: convertPermissionToBoolean(data.root), - isInitialRoot: convertPermissionToBoolean(data.root), - }); - }, - render(createElement) { - return createElement('repo'); - }, - }); -} - -function initRepoEditButton(el) { - return new Vue({ - el, - store, - components: { - repoEditButton: RepoEditButton, - }, - render(createElement) { - return createElement('repo-edit-button'); - }, - }); -} - -function initNewDropdown(el) { - return new Vue({ - el, - store, - components: { - newDropdown, - }, - render(createElement) { - return createElement('new-dropdown'); - }, - }); -} - -function initNewBranchForm() { - const el = document.querySelector('.js-new-branch-dropdown'); - - if (!el) return null; - - return new Vue({ - el, - components: { - newBranchForm, - }, - store, - render(createElement) { - return createElement('new-branch-form'); - }, - }); -} - -const repo = document.getElementById('repo'); -const editButton = document.querySelector('.editable-mode'); -const newDropdownHolder = document.querySelector('.js-new-dropdown'); - -Vue.use(Translate); - -initRepo(repo); -initRepoEditButton(editButton); -initNewBranchForm(); -initNewDropdown(newDropdownHolder); diff --git a/app/assets/javascripts/repo/stores/actions.js b/app/assets/javascripts/repo/stores/actions.js deleted file mode 100644 index af5dcf054ef..00000000000 --- a/app/assets/javascripts/repo/stores/actions.js +++ /dev/null @@ -1,146 +0,0 @@ -import Vue from 'vue'; -import { visitUrl } from '../../lib/utils/url_utility'; -import flash from '../../flash'; -import service from '../services'; -import * as types from './mutation_types'; - -export const redirectToUrl = (_, url) => visitUrl(url); - -export const setInitialData = ({ commit }, data) => commit(types.SET_INITIAL_DATA, data); - -export const closeDiscardPopup = ({ commit }) => commit(types.TOGGLE_DISCARD_POPUP, false); - -export const discardAllChanges = ({ commit, getters, dispatch }) => { - const changedFiles = getters.changedFiles; - - changedFiles.forEach((file) => { - commit(types.DISCARD_FILE_CHANGES, file); - - if (file.tempFile) { - dispatch('closeFile', { file, force: true }); - } - }); -}; - -export const closeAllFiles = ({ state, dispatch }) => { - state.openFiles.forEach(file => dispatch('closeFile', { file })); -}; - -export const toggleEditMode = ({ state, commit, getters, dispatch }, force = false) => { - const changedFiles = getters.changedFiles; - - if (changedFiles.length && !force) { - commit(types.TOGGLE_DISCARD_POPUP, true); - } else { - commit(types.TOGGLE_EDIT_MODE); - commit(types.TOGGLE_DISCARD_POPUP, false); - dispatch('toggleBlobView'); - - if (!state.editMode) { - dispatch('discardAllChanges'); - } - } -}; - -export const toggleBlobView = ({ commit, state }) => { - if (state.editMode) { - commit(types.SET_EDIT_MODE); - } else { - commit(types.SET_PREVIEW_MODE); - } -}; - -export const checkCommitStatus = ({ state }) => service.getBranchData( - state.project.id, - state.currentBranch, -) - .then((data) => { - const { id } = data.commit; - - if (state.currentRef !== id) { - return true; - } - - return false; - }) - .catch(() => flash('Error checking branch data. Please try again.')); - -export const commitChanges = ({ commit, state, dispatch, getters }, { payload, newMr }) => - service.commit(state.project.id, payload) - .then((data) => { - const { branch } = payload; - if (!data.short_id) { - flash(data.message); - return; - } - - const lastCommit = { - commit_path: `${state.project.url}/commit/${data.id}`, - commit: { - message: data.message, - authored_date: data.committed_date, - }, - }; - - flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice'); - - if (newMr) { - dispatch('redirectToUrl', `${state.endpoints.newMergeRequestUrl}${branch}`); - } else { - commit(types.SET_COMMIT_REF, data.id); - - getters.changedFiles.forEach((entry) => { - commit(types.SET_LAST_COMMIT_DATA, { - entry, - lastCommit, - }); - }); - - dispatch('discardAllChanges'); - dispatch('closeAllFiles'); - dispatch('toggleEditMode'); - - window.scrollTo(0, 0); - } - }) - .catch(() => flash('Error committing changes. Please try again.')); - -export const createTempEntry = ({ state, dispatch }, { name, type, content = '', base64 = false }) => { - if (type === 'tree') { - dispatch('createTempTree', name); - } else if (type === 'blob') { - dispatch('createTempFile', { - tree: state, - name, - base64, - content, - }); - } -}; - -export const popHistoryState = ({ state, dispatch, getters }) => { - const treeList = getters.treeList; - const tree = treeList.find(file => file.url === state.previousUrl); - - if (!tree) return; - - if (tree.type === 'tree') { - dispatch('toggleTreeOpen', { endpoint: tree.url, tree }); - } -}; - -export const scrollToTab = () => { - Vue.nextTick(() => { - const tabs = document.getElementById('tabs'); - - if (tabs) { - const tabEl = tabs.querySelector('.active .repo-tab'); - - tabEl.focus(); - } - }); -}; - -export * from './actions/tree'; -export * from './actions/file'; -export * from './actions/branch'; diff --git a/app/assets/javascripts/repo/stores/actions/branch.js b/app/assets/javascripts/repo/stores/actions/branch.js deleted file mode 100644 index 61d9a5af3e3..00000000000 --- a/app/assets/javascripts/repo/stores/actions/branch.js +++ /dev/null @@ -1,20 +0,0 @@ -import service from '../../services'; -import * as types from '../mutation_types'; -import { pushState } from '../utils'; - -// eslint-disable-next-line import/prefer-default-export -export const createNewBranch = ({ state, commit }, branch) => service.createBranch( - state.project.id, - { - branch, - ref: state.currentBranch, - }, -).then(res => res.json()) -.then((data) => { - const branchName = data.name; - const url = location.href.replace(state.currentBranch, branchName); - - pushState(url); - - commit(types.SET_CURRENT_BRANCH, branchName); -}); diff --git a/app/assets/javascripts/repo/stores/actions/tree.js b/app/assets/javascripts/repo/stores/actions/tree.js deleted file mode 100644 index 7c251e26bed..00000000000 --- a/app/assets/javascripts/repo/stores/actions/tree.js +++ /dev/null @@ -1,163 +0,0 @@ -import { visitUrl } from '../../../lib/utils/url_utility'; -import { normalizeHeaders } from '../../../lib/utils/common_utils'; -import flash from '../../../flash'; -import service from '../../services'; -import * as types from '../mutation_types'; -import { - pushState, - setPageTitle, - findEntry, - createTemp, - createOrMergeEntry, -} from '../utils'; - -export const getTreeData = ( - { commit, state, dispatch }, - { endpoint = state.endpoints.rootEndpoint, tree = state } = {}, -) => { - commit(types.TOGGLE_LOADING, tree); - - service.getTreeData(endpoint) - .then((res) => { - const pageTitle = decodeURI(normalizeHeaders(res.headers)['PAGE-TITLE']); - - setPageTitle(pageTitle); - - return res.json(); - }) - .then((data) => { - const prevLastCommitPath = tree.lastCommitPath; - if (!state.isInitialRoot) { - commit(types.SET_ROOT, data.path === '/'); - } - - dispatch('updateDirectoryData', { data, tree }); - commit(types.SET_PARENT_TREE_URL, data.parent_tree_url); - commit(types.SET_LAST_COMMIT_URL, { tree, url: data.last_commit_path }); - commit(types.TOGGLE_LOADING, tree); - - if (prevLastCommitPath !== null) { - dispatch('getLastCommitData', tree); - } - - pushState(endpoint); - }) - .catch(() => { - flash('Error loading tree data. Please try again.'); - commit(types.TOGGLE_LOADING, tree); - }); -}; - -export const toggleTreeOpen = ({ commit, dispatch }, { endpoint, tree }) => { - if (tree.opened) { - // send empty data to clear the tree - const data = { trees: [], blobs: [], submodules: [] }; - - pushState(tree.parentTreeUrl); - - commit(types.SET_PREVIOUS_URL, tree.parentTreeUrl); - dispatch('updateDirectoryData', { data, tree }); - } else { - commit(types.SET_PREVIOUS_URL, endpoint); - dispatch('getTreeData', { endpoint, tree }); - } - - commit(types.TOGGLE_TREE_OPEN, tree); -}; - -export const clickedTreeRow = ({ commit, dispatch }, row) => { - if (row.type === 'tree') { - dispatch('toggleTreeOpen', { - endpoint: row.url, - tree: row, - }); - } else if (row.type === 'submodule') { - commit(types.TOGGLE_LOADING, row); - - visitUrl(row.url); - } else if (row.type === 'blob' && row.opened) { - dispatch('setFileActive', row); - } else { - dispatch('getFileData', row); - } -}; - -export const createTempTree = ({ state, commit, dispatch }, name) => { - let tree = state; - const dirNames = name.replace(new RegExp(`^${state.path}/`), '').split('/'); - - dirNames.forEach((dirName) => { - const foundEntry = findEntry(tree, 'tree', dirName); - - if (!foundEntry) { - const tmpEntry = createTemp({ - name: dirName, - path: tree.path, - type: 'tree', - level: tree.level !== undefined ? tree.level + 1 : 0, - }); - - commit(types.CREATE_TMP_TREE, { - parent: tree, - tmpEntry, - }); - commit(types.TOGGLE_TREE_OPEN, tmpEntry); - - tree = tmpEntry; - } else { - tree = foundEntry; - } - }); - - if (tree.tempFile) { - dispatch('createTempFile', { - tree, - name: '.gitkeep', - }); - } -}; - -export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = state) => { - if (tree.lastCommitPath === null || getters.isCollapsed) return; - - service.getTreeLastCommit(tree.lastCommitPath) - .then((res) => { - const lastCommitPath = normalizeHeaders(res.headers)['MORE-LOGS-URL'] || null; - - commit(types.SET_LAST_COMMIT_URL, { tree, url: lastCommitPath }); - - return res.json(); - }) - .then((data) => { - data.forEach((lastCommit) => { - const entry = findEntry(tree, lastCommit.type, lastCommit.file_name); - - if (entry) { - commit(types.SET_LAST_COMMIT_DATA, { entry, lastCommit }); - } - }); - - dispatch('getLastCommitData', tree); - }) - .catch(() => flash('Error fetching log data.')); -}; - -export const updateDirectoryData = ({ commit, state }, { data, tree }) => { - const level = tree.level !== undefined ? tree.level + 1 : 0; - const parentTreeUrl = data.parent_tree_url ? `${data.parent_tree_url}${data.path}` : state.endpoints.rootUrl; - const createEntry = (entry, type) => createOrMergeEntry({ - tree, - entry, - level, - type, - parentTreeUrl, - }); - - const formattedData = [ - ...data.trees.map(t => createEntry(t, 'tree')), - ...data.submodules.map(m => createEntry(m, 'submodule')), - ...data.blobs.map(b => createEntry(b, 'blob')), - ]; - - commit(types.SET_DIRECTORY_DATA, { tree, data: formattedData }); -}; diff --git a/app/assets/javascripts/repo/stores/getters.js b/app/assets/javascripts/repo/stores/getters.js deleted file mode 100644 index 5ce9f449905..00000000000 --- a/app/assets/javascripts/repo/stores/getters.js +++ /dev/null @@ -1,40 +0,0 @@ -import _ from 'underscore'; - -/* - Takes the multi-dimensional tree and returns a flattened array. - This allows for the table to recursively render the table rows but keeps the data - structure nested to make it easier to add new files/directories. -*/ -export const treeList = (state) => { - const mapTree = arr => (!arr.tree.length ? [] : _.map(arr.tree, a => [a, mapTree(a)])); - - return _.chain(state.tree) - .map(arr => [arr, mapTree(arr)]) - .flatten() - .value(); -}; - -export const changedFiles = state => state.openFiles.filter(file => file.changed); - -export const activeFile = state => state.openFiles.find(file => file.active); - -export const activeFileExtension = (state) => { - const file = activeFile(state); - return file ? `.${file.path.split('.').pop()}` : ''; -}; - -export const isCollapsed = state => !!state.openFiles.length; - -export const canEditFile = (state) => { - const currentActiveFile = activeFile(state); - const openedFiles = state.openFiles; - - return state.canCommit && - state.onTopOfBranch && - openedFiles.length && - (currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary); -}; - -export const addedFiles = state => changedFiles(state).filter(f => f.tempFile); - -export const modifiedFiles = state => changedFiles(state).filter(f => !f.tempFile); diff --git a/app/assets/javascripts/repo/stores/mutations/branch.js b/app/assets/javascripts/repo/stores/mutations/branch.js deleted file mode 100644 index d8229e8a620..00000000000 --- a/app/assets/javascripts/repo/stores/mutations/branch.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as types from '../mutation_types'; - -export default { - [types.SET_CURRENT_BRANCH](state, currentBranch) { - Object.assign(state, { - currentBranch, - }); - }, -}; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js index 13e4cb5717e..85bfd03a3cf 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js @@ -1,5 +1,6 @@ import tooltip from '../../vue_shared/directives/tooltip'; import { pluralize } from '../../lib/utils/text_utility'; +import icon from '../../vue_shared/components/icon.vue'; export default { name: 'MRWidgetHeader', @@ -9,6 +10,9 @@ export default { directives: { tooltip, }, + components: { + icon, + }, computed: { shouldShowCommitsBehindText() { return this.mr.divergedCommitsCount > 0; @@ -81,10 +85,9 @@ export default { data-toggle="dropdown" aria-label="Download as" role="button"> - <i - class="fa fa-download" - aria-hidden="true"> - </i> + <icon + name="download"> + </icon> <i class="fa fa-caret-down" aria-hidden="true"> diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue index 52814de8b2d..59ca9a0a6d4 100644 --- a/app/assets/javascripts/vue_shared/components/commit.vue +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -2,13 +2,14 @@ import commitIconSvg from 'icons/_icon_commit.svg'; import userAvatarLink from './user_avatar/user_avatar_link.vue'; import tooltip from '../directives/tooltip'; + import Icon from '../../vue_shared/components/icon.vue'; export default { props: { /** * Indicates the existance of a tag. * Used to render the correct icon, if true will render `fa-tag` icon, - * if false will render `fa-code-fork` icon. + * if false will render a svg sprite fork icon */ tag: { type: Boolean, @@ -107,6 +108,7 @@ }, components: { userAvatarLink, + Icon, }, created() { this.commitIconSvg = commitIconSvg; @@ -123,11 +125,10 @@ class="fa fa-tag" aria-hidden="true"> </i> - <i + <icon v-if="!tag" - class="fa fa-code-fork" - aria-hidden="true"> - </i> + name="fork"> + </icon> </div> <a diff --git a/app/assets/javascripts/vue_shared/components/project_avatar/image.vue b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue new file mode 100644 index 00000000000..dce23bd65f6 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue @@ -0,0 +1,103 @@ +<script> + +/* This is a re-usable vue component for rendering a project avatar that + does not need to link to the project's profile. The image and an optional + tooltip can be configured by props passed to this component. + + Sample configuration: + + <project-avatar-image + :lazy="true" + :img-src="projectAvatarSrc" + :img-alt="tooltipText" + :tooltip-text="tooltipText" + tooltip-placement="top" + /> + +*/ + +import defaultAvatarUrl from 'images/no_avatar.png'; +import { placeholderImage } from '../../../lazy_loader'; +import tooltip from '../../directives/tooltip'; + +export default { + name: 'ProjectAvatarImage', + props: { + lazy: { + type: Boolean, + required: false, + default: false, + }, + imgSrc: { + type: String, + required: false, + default: defaultAvatarUrl, + }, + cssClasses: { + type: String, + required: false, + default: '', + }, + imgAlt: { + type: String, + required: false, + default: 'project avatar', + }, + size: { + type: Number, + required: false, + default: 20, + }, + tooltipText: { + type: String, + required: false, + default: '', + }, + tooltipPlacement: { + type: String, + required: false, + default: 'top', + }, + }, + directives: { + tooltip, + }, + computed: { + // API response sends null when gravatar is disabled and + // we provide an empty string when we use it inside project avatar link. + // In both cases we should render the defaultAvatarUrl + sanitizedSource() { + return this.imgSrc === '' || this.imgSrc === null ? defaultAvatarUrl : this.imgSrc; + }, + resultantSrcAttribute() { + return this.lazy ? placeholderImage : this.sanitizedSource; + }, + tooltipContainer() { + return this.tooltipText ? 'body' : null; + }, + avatarSizeClass() { + return `s${this.size}`; + }, + }, +}; +</script> + +<template> + <img + v-tooltip + class="avatar" + :class="{ + lazy, + [avatarSizeClass]: true, + [cssClasses]: true + }" + :src="resultantSrcAttribute" + :width="size" + :height="size" + :alt="imgAlt" + :data-src="sanitizedSource" + :data-container="tooltipContainer" + :data-placement="tooltipPlacement" + :title="tooltipText" + /> +</template> diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue index 710452bb3d3..33096b53cf8 100644 --- a/app/assets/javascripts/vue_shared/components/table_pagination.vue +++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue @@ -122,11 +122,17 @@ export default { return items; }, + showPagination() { + return this.pageInfo.totalPages > 1; + }, }, }; </script> <template> - <div class="gl-pagination"> + <div + v-if="showPagination" + class="gl-pagination" + > <ul class="pagination clearfix"> <li v-for="item in getItems" diff --git a/app/assets/stylesheets/framework/contextual-sidebar.scss b/app/assets/stylesheets/framework/contextual-sidebar.scss index 2e417315ed7..5da06b90113 100644 --- a/app/assets/stylesheets/framework/contextual-sidebar.scss +++ b/app/assets/stylesheets/framework/contextual-sidebar.scss @@ -23,7 +23,6 @@ .context-header { position: relative; margin-right: 2px; - width: $contextual-sidebar-width; a { transition: padding $sidebar-transition-duration; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index b84d6c140be..1d6c7a5c472 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -219,6 +219,7 @@ $gl-input-padding: 10px; $gl-vert-padding: 6px; $gl-padding-top: 10px; $gl-sidebar-padding: 22px; +$gl-bar-padding: 3px; /* * Misc diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss index 735fc4babd7..2f3a80daa90 100644 --- a/app/assets/stylesheets/framework/wells.scss +++ b/app/assets/stylesheets/framework/wells.scss @@ -16,6 +16,10 @@ .commit-sha, .commit-info { margin-left: 4px; + + .fork-svg { + margin-right: 4px; + } } .ref-name { @@ -79,7 +83,7 @@ } .limit-icon { - margin: 0 8px; + margin: 0 4px; } .limit-message { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index e75a35d78ad..f887a11004f 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -723,3 +723,7 @@ font-size: 16px; } } + +.fork-sprite { + margin-right: -5px; +} diff --git a/app/assets/stylesheets/pages/pipeline_schedules.scss b/app/assets/stylesheets/pages/pipeline_schedules.scss index 7e2297c283f..b698a4f9afa 100644 --- a/app/assets/stylesheets/pages/pipeline_schedules.scss +++ b/app/assets/stylesheets/pages/pipeline_schedules.scss @@ -39,6 +39,10 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + + svg { + vertical-align: middle; + } } .next-run-cell { diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 9805fc4f882..05c1033c5f7 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -143,6 +143,12 @@ fill: $gl-text-color-secondary; } + .sprite { + width: 12px; + height: 12px; + fill: $gl-text-color; + } + .fa { font-size: 12px; color: $gl-text-color; diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 6eb92c7baee..da3c2d7fa5d 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -22,9 +22,10 @@ } } -.multi-file { +.ide-view { display: flex; - height: calc(100vh - 145px); + height: calc(100vh - #{$header-height}); + color: $almost-black; border-top: 1px solid $white-dark; border-bottom: 1px solid $white-dark; @@ -35,12 +36,47 @@ } } +.with-performance-bar .ide-view { + height: calc(100vh - #{$header-height}); +} + .ide-file-list { flex: 1; - overflow: scroll; .file { cursor: pointer; + + &.file-open { + background: $white-normal; + } + + .repo-file-name { + white-space: nowrap; + text-overflow: ellipsis; + } + + .unsaved-icon { + color: $indigo-700; + float: right; + font-size: smaller; + line-height: 20px; + } + + .repo-new-btn { + display: none; + margin-top: -4px; + margin-bottom: -4px; + } + + &:hover { + .repo-new-btn { + display: block; + } + + .unsaved-icon { + display: none; + } + } } a { @@ -55,10 +91,9 @@ .multi-file-table-name, .multi-file-table-col-commit-message { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + overflow: visible; max-width: 0; + padding: 6px 12px; } .multi-file-table-name { @@ -66,6 +101,7 @@ } .multi-file-table-col-commit-message { + white-space: nowrap; width: 50%; } @@ -79,7 +115,7 @@ .multi-file-tabs { display: flex; - overflow: scroll; + overflow-x: auto; background-color: $white-normal; box-shadow: inset 0 -1px $white-dark; @@ -128,9 +164,38 @@ height: 0; } +.blob-editor-container { + flex: 1; + height: 0; + display: flex; + flex-direction: column; + justify-content: center; + + .vertical-center { + min-height: auto; + } +} + +.multi-file-editor-holder { + height: 100%; +} + .multi-file-editor-btn-group { - padding: $grid-size; + padding: $gl-bar-padding $gl-padding; border-top: 1px solid $white-dark; + border-bottom: 1px solid $white-dark; + background: $white-light; +} + +.ide-status-bar { + padding: $gl-bar-padding $gl-padding; + background: $white-light; + display: flex; + justify-content: space-between; + + svg { + vertical-align: middle; + } } // Not great, but this is to deal with our current output @@ -138,10 +203,6 @@ height: 100%; overflow: scroll; - .blob-viewer { - height: 100%; - } - .file-content.code { display: flex; @@ -162,18 +223,101 @@ } } +.file-content.blob-no-preview { + a { + margin-left: auto; + margin-right: auto; + } +} + .multi-file-commit-panel { display: flex; flex-direction: column; height: 100%; width: 290px; - padding: $gl-padding; + padding: 0; background-color: $gray-light; border-left: 1px solid $white-dark; + .projects-sidebar { + display: flex; + flex-direction: column; + } + + .multi-file-commit-panel-inner { + display: flex; + flex: 1; + flex-direction: column; + } + + .multi-file-commit-panel-inner-scroll { + display: flex; + flex: 1; + flex-direction: column; + overflow: auto; + } + &.is-collapsed { width: 60px; - padding: 0; + + .multi-file-commit-list { + padding-top: $gl-padding; + overflow: hidden; + } + + .multi-file-context-bar-icon { + align-items: center; + + svg { + float: none; + margin: 0; + } + } + } + + .branch-container { + border-left: 4px solid $indigo-700; + margin-bottom: $gl-bar-padding; + } + + .branch-header { + background: $white-dark; + display: flex; + } + + .branch-header-title { + flex: 1; + padding: $grid-size $gl-padding; + color: $indigo-700; + font-weight: $gl-font-weight-bold; + + svg { + vertical-align: middle; + } + } + + .branch-header-btns { + padding: $gl-vert-padding $gl-padding; + } + + .left-collapse-btn { + display: none; + background: $gray-light; + text-align: left; + border-top: 1px solid $white-dark; + + svg { + vertical-align: middle; + } + } +} + +.multi-file-context-bar-icon { + padding: 10px; + + svg { + margin-right: 10px; + float: left; } } @@ -186,9 +330,9 @@ .multi-file-commit-panel-header { display: flex; align-items: center; - padding: 0 0 12px; margin-bottom: 12px; border-bottom: 1px solid $white-dark; + padding: $gl-btn-padding 0; &.is-collapsed { border-bottom: 1px solid $white-dark; @@ -197,23 +341,33 @@ margin-left: auto; margin-right: auto; } + + .multi-file-commit-panel-collapse-btn { + margin-right: auto; + margin-left: auto; + border-left: 0; + } } } -.multi-file-commit-panel-collapse-btn { - padding-top: 0; - padding-bottom: 0; - margin-left: auto; - font-size: 20px; +.multi-file-commit-panel-header-title { + display: flex; + flex: 1; + padding: $gl-btn-padding; - &.is-collapsed { - margin-right: auto; + svg { + margin-right: $gl-btn-padding; } } +.multi-file-commit-panel-collapse-btn { + border-left: 1px solid $white-dark; +} + .multi-file-commit-list { flex: 1; - overflow: scroll; + overflow: auto; + padding: $gl-padding; } .multi-file-commit-list-item { @@ -244,7 +398,7 @@ } .multi-file-commit-form { - padding-top: 12px; + padding: $gl-padding; border-top: 1px solid $white-dark; } @@ -295,3 +449,40 @@ } } } + +.ide-loading { + display: flex; + height: 100vh; + align-items: center; + justify-content: center; +} + +.ide-empty-state { + display: flex; + height: 100vh; + align-items: center; + justify-content: center; +} + +.repo-new-btn { + .dropdown-toggle svg { + margin-top: -2px; + margin-bottom: 2px; + } + + .dropdown-menu { + left: auto; + right: 0; + + label { + font-weight: $gl-font-weight-normal; + padding: 5px 8px; + margin-bottom: 0; + } + } +} + +.ide-flash-container.flash-container { + margin-top: $header-height; + margin-bottom: 0; +} diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb new file mode 100644 index 00000000000..1ff25a45398 --- /dev/null +++ b/app/controllers/ide_controller.rb @@ -0,0 +1,6 @@ +class IdeController < ApplicationController + layout 'nav_only' + + def index + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4754a67450f..d13407a06c8 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -306,7 +306,7 @@ module ApplicationHelper cookies["sidebar_collapsed"] == "true" end - def show_new_repo? + def show_new_ide? cookies["new_repo"] == "true" && body_data_page != 'projects:show' end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 556ed233ccf..f9dcb32f7c4 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -8,7 +8,7 @@ module BlobHelper %w(credits changelog news copying copyright license authors) end - def edit_path(project = @project, ref = @ref, path = @path, options = {}) + def edit_blob_path(project = @project, ref = @ref, path = @path, options = {}) project_edit_blob_path(project, tree_join(ref, path), options[:link_opts]) @@ -26,10 +26,10 @@ module BlobHelper button_tag 'Edit', class: "#{common_classes} disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' } # This condition applies to anonymous or users who can edit directly elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) - link_to 'Edit', edit_path(project, ref, path, options), class: "#{common_classes} btn-sm" + link_to 'Edit', edit_blob_path(project, ref, path, options), class: "#{common_classes} btn-sm" elsif current_user && can?(current_user, :fork_project, project) continue_params = { - to: edit_path(project, ref, path, options), + to: edit_blob_path(project, ref, path, options), notice: edit_in_new_fork_notice, notice_now: edit_in_new_fork_notice_now } @@ -41,6 +41,43 @@ module BlobHelper end end + def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) + "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}" + end + + def ide_edit_text + "#{_('Multi Edit')} <span class='label label-primary'>#{_('Beta')}</span>".html_safe + end + + def ide_blob_link(project = @project, ref = @ref, path = @path, options = {}) + return unless show_new_ide? + + blob = options.delete(:blob) + blob ||= project.repository.blob_at(ref, path) rescue nil + + return unless blob && blob.readable_text? + + common_classes = "btn js-edit-ide #{options[:extra_class]}" + + if !on_top_of_branch?(project, ref) + button_tag ide_edit_text, class: "#{common_classes} disabled has-tooltip", title: _('You can only edit files when you are on a branch'), data: { container: 'body' } + # This condition applies to anonymous or users who can edit directly + elsif current_user && can_modify_blob?(blob, project, ref) + link_to ide_edit_text, ide_edit_path(project, ref, path, options), class: "#{common_classes} btn-sm" + elsif current_user && can?(current_user, :fork_project, project) + continue_params = { + to: ide_edit_path(project, ref, path, options), + notice: edit_in_new_fork_notice, + notice_now: edit_in_new_fork_notice_now + } + fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) + + button_tag ide_edit_text, + class: common_classes, + data: { fork_path: fork_path } + end + end + def modify_file_link(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:) return unless current_user @@ -232,7 +269,7 @@ module BlobHelper return if blob.empty? if blob.raw_binary? || blob.stored_externally? - icon = icon('download') + icon = sprite_icon('download') title = 'Download' else icon = icon('file-code-o') diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 2d304f7eb91..0333c29e2fd 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -63,7 +63,7 @@ module CommitsHelper # Returns a link formatted as a commit branch link def commit_branch_link(url, text) link_to(url, class: 'label label-gray ref-name branch-link') do - icon('code-fork', class: 'append-right-5') + "#{text}" + sprite_icon('fork', size: 16, css_class: 'fork-svg') + "#{text}" end end diff --git a/app/models/identity.rb b/app/models/identity.rb index 99d99bc6deb..b3fa7d8176a 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -8,6 +8,8 @@ class Identity < ActiveRecord::Base validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider, case_sensitive: false } validates :user_id, uniqueness: { scope: :provider } + before_save :ensure_normalized_extern_uid, if: :extern_uid_changed? + scope :with_provider, ->(provider) { where(provider: provider) } scope :with_extern_uid, ->(provider, extern_uid) do iwhere(extern_uid: normalize_uid(provider, extern_uid)).with_provider(provider) @@ -24,4 +26,12 @@ class Identity < ActiveRecord::Base uid.to_s end end + + private + + def ensure_normalized_extern_uid + return if extern_uid.nil? + + self.extern_uid = Identity.normalize_uid(self.provider, self.extern_uid) + end end diff --git a/app/serializers/merge_request_serializer.rb b/app/serializers/merge_request_serializer.rb index 52eb30d688a..caf193bdae3 100644 --- a/app/serializers/merge_request_serializer.rb +++ b/app/serializers/merge_request_serializer.rb @@ -7,7 +7,7 @@ class MergeRequestSerializer < BaseSerializer case opts[:serializer] when 'basic', 'sidebar' MergeRequestBasicEntity - when 'widget' + else # It's 'widget' MergeRequestWidgetEntity end diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb index ff188102b62..92a32e703af 100644 --- a/app/services/search/global_service.rb +++ b/app/services/search/global_service.rb @@ -1,13 +1,16 @@ module Search class GlobalService attr_accessor :current_user, :params + attr_reader :default_project_filter def initialize(user, params) @current_user, @params = user, params.dup + @default_project_filter = true end def execute - Gitlab::SearchResults.new(current_user, projects, params[:search]) + Gitlab::SearchResults.new(current_user, projects, params[:search], + default_project_filter: default_project_filter) end def projects diff --git a/app/services/search/group_service.rb b/app/services/search/group_service.rb index 29478e3251f..b4efba68715 100644 --- a/app/services/search/group_service.rb +++ b/app/services/search/group_service.rb @@ -5,6 +5,7 @@ module Search def initialize(user, group, params) super(user, params) + @default_project_filter = false @group = group end diff --git a/app/views/ide/index.html.haml b/app/views/ide/index.html.haml new file mode 100644 index 00000000000..8368e7a4563 --- /dev/null +++ b/app/views/ide/index.html.haml @@ -0,0 +1,12 @@ +- page_title 'IDE' + +- content_for :page_specific_javascripts do + = webpack_bundle_tag 'common_vue' + = webpack_bundle_tag 'ide' + +.ide-flash-container.flash-container + +#ide.ide-loading + .text-center + = icon('spinner spin 2x') + %h2.clgray= _('IDE Loading ...') diff --git a/app/views/layouts/nav_only.html.haml b/app/views/layouts/nav_only.html.haml new file mode 100644 index 00000000000..6fa4b39dc10 --- /dev/null +++ b/app/views/layouts/nav_only.html.haml @@ -0,0 +1,13 @@ +!!! 5 +%html{ lang: I18n.locale, class: page_class } + = render "layouts/head" + %body{ class: "#{user_application_theme} #{@body_class}", data: { page: body_data_page } } + = render 'peek/bar' + = render "layouts/header/default" + = render 'shared/outdated_browser' + .mobile-overlay + .alert-wrapper + = render "layouts/broadcast" + = yield :flash_message + = render "layouts/flash" + = yield diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml index 3a7a99462a6..79530e78154 100644 --- a/app/views/projects/_files.html.haml +++ b/app/views/projects/_files.html.haml @@ -7,7 +7,7 @@ .nav-block = render 'projects/tree/tree_header', tree: @tree - - if !show_new_repo? && commit + - if commit = render 'shared/commit_well', commit: commit, ref: ref, project: project = render 'projects/tree/tree_content', tree: @tree, content_url: content_url diff --git a/app/views/projects/_last_push.html.haml b/app/views/projects/_last_push.html.haml index 56eecece54c..b68eb47c6b4 100644 --- a/app/views/projects/_last_push.html.haml +++ b/app/views/projects/_last_push.html.haml @@ -1,18 +1,19 @@ - event = last_push_event - if event && show_last_push_widget?(event) - .row-content-block.top-block.hidden-xs.white - .event-last-push - .event-last-push-text - %span= s_("LastPushEvent|You pushed to") - %strong - = link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name' + %div{ class: container_class } + .row-content-block.top-block.hidden-xs.white + .event-last-push + .event-last-push-text + %span= s_("LastPushEvent|You pushed to") + %strong + = link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name' - - if event.project != @project - %span= s_("LastPushEvent|at") - %strong= link_to_project event.project + - if event.project != @project + %span= s_("LastPushEvent|at") + %strong= link_to_project event.project - #{time_ago_with_tooltip(event.created_at)} + #{time_ago_with_tooltip(event.created_at)} - .pull-right - = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm" do - #{ _('Create merge request') } + .pull-right + = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm" do + #{ _('Create merge request') } diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml index fe02cbcbf95..7ff7466e561 100644 --- a/app/views/projects/artifacts/browse.html.haml +++ b/app/views/projects/artifacts/browse.html.haml @@ -18,7 +18,7 @@ .tree-controls = link_to download_project_job_artifacts_path(@project, @build), rel: 'nofollow', download: '', class: 'btn btn-default download' do - = icon('download') + = sprite_icon('download') Download artifacts archive .tree-content-holder diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index 7777f55ddd7..c9fa90acd11 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -3,7 +3,7 @@ .file-holder-bottom-radius.file-holder.file.append-bottom-default .js-file-title.file-title.clearfix{ data: { current_action: action } } .editor-ref - = icon('code-fork') + = sprite_icon('fork', size: 12) = ref %span.editor-file-name - if current_action?(:edit) || current_action?(:update) diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml index 281363d2e01..2a77dedd9a2 100644 --- a/app/views/projects/blob/_header.html.haml +++ b/app/views/projects/blob/_header.html.haml @@ -12,6 +12,7 @@ .btn-group{ role: "group" }< = edit_blob_link + = ide_blob_link - if current_user = replace_blob_link = delete_blob_link diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index c4712bf3736..4d358052d43 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -6,21 +6,14 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'blob' - - if show_new_repo? - = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'repo' - = render 'projects/last_push' %div{ class: container_class } - - if show_new_repo? - = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_blob_path(@project, @id) - - else - #tree-holder.tree-holder - = render 'blob', blob: @blob + #tree-holder.tree-holder + = render 'blob', blob: @blob - if can_modify_blob?(@blob) = render 'projects/blob/remove' - - title = "Replace #{@blob.name}" - = render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: project_update_blob_path(@project, @id), method: :put + - title = "Replace #{@blob.name}" + = render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: project_update_blob_path(@project, @id), method: :put diff --git a/app/views/projects/blob/viewers/_download.html.haml b/app/views/projects/blob/viewers/_download.html.haml index 253566c43be..f9b1da05a00 100644 --- a/app/views/projects/blob/viewers/_download.html.haml +++ b/app/views/projects/blob/viewers/_download.html.haml @@ -2,6 +2,6 @@ .center.render-error.vertical-center = link_to blob_raw_path do %h1.light - = icon('download') + = sprite_icon('download') %h4 Download (#{number_to_human_size(viewer.blob.raw_size)}) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 573050e597d..acf67b83890 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -8,7 +8,8 @@ %li{ class: "js-branch-#{branch.name}" } %div = link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated ref-name' do - = icon('code-fork', class: 'append-right-5') + "#{branch.name}" + = sprite_icon('fork', size: 12) + = branch.name - if branch.name == @repository.root_ref %span.label.label-primary default diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index fa6f6d0b588..fa9a9bfc8f7 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -3,7 +3,7 @@ - if !project.empty_repo? && can?(current_user, :download_code, project) .project-action-button.dropdown.inline> %button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download') } - = icon('download') + = sprite_icon('download') = icon("caret-down") %span.sr-only= _('Select Archive Format') %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index 86510b8ab93..0cd2d45c74b 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -22,7 +22,7 @@ - if ref - if job.ref .icon-container - = job.tag? ? icon('tag') : icon('code-fork') + = job.tag? ? icon('tag') : sprite_icon('fork', css_class: 'sprite') = link_to job.ref, project_ref_path(job.project, job.ref), class: "ref-name" - else .light none @@ -96,7 +96,7 @@ .pull-right - if can?(current_user, :read_build, job) && job.artifacts? = link_to download_project_job_artifacts_path(job.project, job), rel: 'nofollow', download: '', title: 'Download artifacts', class: 'btn btn-build' do - = icon('download') + = sprite_icon('download') - if can?(current_user, :update_build, job) - if job.active? = link_to cancel_project_job_path(job.project, job, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do diff --git a/app/views/projects/commit/_limit_exceeded_message.html.haml b/app/views/projects/commit/_limit_exceeded_message.html.haml index 84a52d49487..a264f3517c4 100644 --- a/app/views/projects/commit/_limit_exceeded_message.html.haml +++ b/app/views/projects/commit/_limit_exceeded_message.html.haml @@ -1,7 +1,7 @@ .has-tooltip{ class: "limit-box limit-box-#{objects} prepend-left-5", data: { title: "Project has too many #{label_for_message} to search"} } .limit-icon - if objects == :branch - = icon('code-fork') + = sprite_icon('fork', size: 12) - else = icon('tag') .limit-message diff --git a/app/views/projects/deployments/_commit.html.haml b/app/views/projects/deployments/_commit.html.haml index 014486be868..c7ac687e4a6 100644 --- a/app/views/projects/deployments/_commit.html.haml +++ b/app/views/projects/deployments/_commit.html.haml @@ -2,7 +2,7 @@ .branch-commit - if deployment.ref %span.icon-container - = deployment.tag? ? icon('tag') : icon('code-fork') + = deployment.tag? ? icon('tag') : sprite_icon('fork', css_class: 'sprite') = link_to deployment.ref, project_ref_path(@project, deployment.ref), class: "ref-name" .icon-container.commit-icon = custom_icon("icon_commit") diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml index d365bcd4ecc..e8a89b8c6fc 100644 --- a/app/views/projects/forks/error.html.haml +++ b/app/views/projects/forks/error.html.haml @@ -2,7 +2,7 @@ - if @forked_project && !@forked_project.saved? .alert.alert-danger.alert-block %h4 - %i.fa.fa-code-fork + = sprite_icon('fork', size: 16) Fork Error! %p You tried to fork @@ -21,5 +21,4 @@ %p = link_to new_project_fork_path(@project), title: "Fork", class: "btn" do - %i.fa.fa-code-fork Try to fork again diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index b98dc09534f..2599ce5c4b8 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -19,7 +19,7 @@ - if ref - if generic_commit_status.ref .icon-container - = generic_commit_status.tags.any? ? icon('tag') : icon('code-fork') + = generic_commit_status.tags.any? ? icon('tag') : sprite_icon('fork', size: 10) = link_to generic_commit_status.ref, project_commits_path(generic_commit_status.project, generic_commit_status.ref) - else .light none diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 2b5e8711b0a..f45a000833b 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -30,7 +30,7 @@ %span.project-ref-path = link_to project_ref_path(merge_request.project, merge_request.target_branch), class: 'ref-name' do - = icon('code-fork') + = sprite_icon('fork', size: 12, css_class: 'fork-sprite') = merge_request.target_branch - if merge_request.labels.any? diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml index f8c4005a9e0..800e234275c 100644 --- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml +++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml @@ -3,7 +3,7 @@ %td = pipeline_schedule.description %td.branch-name-cell - = icon('code-fork') + = sprite_icon('fork', size: 12) - if pipeline_schedule.ref.present? = link_to pipeline_schedule.ref, project_ref_path(@project, pipeline_schedule.ref), class: "ref-name" %td diff --git a/app/views/projects/tree/_old_tree_content.html.haml b/app/views/projects/tree/_old_tree_content.html.haml deleted file mode 100644 index 6ea78851b8d..00000000000 --- a/app/views/projects/tree/_old_tree_content.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -.tree-content-holder.js-tree-content{ 'data-logs-path': @logs_path } - .table-holder - %table.table#tree-slider{ class: "table_#{@hex_path} tree-table" } - %thead - %tr - %th= s_('ProjectFileTree|Name') - %th.hidden-xs - .pull-left= _('Last commit') - %th.text-right= _('Last update') - - if @path.present? - %tr.tree-item - %td.tree-item-file-name - = link_to "..", project_tree_path(@project, up_dir_path), class: 'prepend-left-10' - %td - %td.hidden-xs - - = render_tree(tree) - - - if tree.readme - = render "projects/tree/readme", readme: tree.readme - -- if can_edit_tree? - = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post - = render 'projects/blob/new_dir' diff --git a/app/views/projects/tree/_old_tree_header.html.haml b/app/views/projects/tree/_old_tree_header.html.haml deleted file mode 100644 index 7f636b7e0e8..00000000000 --- a/app/views/projects/tree/_old_tree_header.html.haml +++ /dev/null @@ -1,64 +0,0 @@ -- if on_top_of_branch? - - addtotree_toggle_attributes = { href: '#', 'data-toggle': 'dropdown', 'data-target': '.add-to-tree-dropdown' } -- else - - addtotree_toggle_attributes = { title: _("You can only add files when you are on a branch"), data: { container: 'body' }, class: 'disabled has-tooltip' } - -%ul.breadcrumb.repo-breadcrumb - %li - = link_to project_tree_path(@project, @ref) do - = @project.path - - path_breadcrumbs do |title, path| - %li - = link_to truncate(title, length: 40), project_tree_path(@project, tree_join(@ref, path)) - - - if current_user - %li - %a.btn.add-to-tree{ addtotree_toggle_attributes } - = sprite_icon('plus', size: 16, css_class: 'pull-left') - = sprite_icon('arrow-down', size: 16, css_class: 'pull-left') - - if on_top_of_branch? - .add-to-tree-dropdown - %ul.dropdown-menu - - if can_edit_tree? - %li - = link_to project_new_blob_path(@project, @id) do - #{ _('New file') } - %li - = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do - #{ _('Upload file') } - %li - = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do - #{ _('New directory') } - - elsif can?(current_user, :fork_project, @project) - %li - - continue_params = { to: project_new_blob_path(@project, @id), - notice: edit_in_new_fork_notice, - notice_now: edit_in_new_fork_notice_now } - - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, - continue: continue_params) - = link_to fork_path, method: :post do - #{ _('New file') } - %li - - continue_params = { to: request.fullpath, - notice: edit_in_new_fork_notice + " Try to upload a file again.", - notice_now: edit_in_new_fork_notice_now } - - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, - continue: continue_params) - = link_to fork_path, method: :post do - #{ _('Upload file') } - %li - - continue_params = { to: request.fullpath, - notice: edit_in_new_fork_notice + " Try to create a new directory again.", - notice_now: edit_in_new_fork_notice_now } - - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, - continue: continue_params) - = link_to fork_path, method: :post do - #{ _('New directory') } - - %li.divider - %li - = link_to new_project_branch_path(@project) do - #{ _('New branch') } - %li - = link_to new_project_tag_path(@project) do - #{ _('New tag') } diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index a4bdd67209d..6ea78851b8d 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -1,5 +1,24 @@ -- content_url = local_assigns.fetch(:content_url, nil) -- if show_new_repo? - = render 'shared/repo/repo', project: @project, content_url: content_url -- else - = render 'projects/tree/old_tree_content', tree: tree +.tree-content-holder.js-tree-content{ 'data-logs-path': @logs_path } + .table-holder + %table.table#tree-slider{ class: "table_#{@hex_path} tree-table" } + %thead + %tr + %th= s_('ProjectFileTree|Name') + %th.hidden-xs + .pull-left= _('Last commit') + %th.text-right= _('Last update') + - if @path.present? + %tr.tree-item + %td.tree-item-file-name + = link_to "..", project_tree_path(@project, up_dir_path), class: 'prepend-left-10' + %td + %td.hidden-xs + + = render_tree(tree) + + - if tree.readme + = render "projects/tree/readme", readme: tree.readme + +- if can_edit_tree? + = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post + = render 'projects/blob/new_dir' diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index c02f7ee37ed..d1ecef39475 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -2,16 +2,78 @@ .tree-ref-holder = render 'shared/ref_switcher', destination: 'tree', path: @path, show_create: true - - if show_new_repo? && can_push_branch?(@project, @ref) - .js-new-dropdown - - else - = render 'projects/tree/old_tree_header' + - if on_top_of_branch? + - addtotree_toggle_attributes = { href: '#', 'data-toggle': 'dropdown', 'data-target': '.add-to-tree-dropdown' } + - else + - addtotree_toggle_attributes = { title: _("You can only add files when you are on a branch"), data: { container: 'body' }, class: 'disabled has-tooltip' } + + %ul.breadcrumb.repo-breadcrumb + %li + = link_to project_tree_path(@project, @ref) do + = @project.path + - path_breadcrumbs do |title, path| + %li + = link_to truncate(title, length: 40), project_tree_path(@project, tree_join(@ref, path)) + + - if current_user + %li + %a.btn.add-to-tree{ addtotree_toggle_attributes } + = sprite_icon('plus', size: 16, css_class: 'pull-left') + = sprite_icon('arrow-down', size: 16, css_class: 'pull-left') + - if on_top_of_branch? + .add-to-tree-dropdown + %ul.dropdown-menu + - if can_edit_tree? + %li + = link_to project_new_blob_path(@project, @id) do + #{ _('New file') } + %li + = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do + #{ _('Upload file') } + %li + = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do + #{ _('New directory') } + - elsif can?(current_user, :fork_project, @project) + %li + - continue_params = { to: project_new_blob_path(@project, @id), + notice: edit_in_new_fork_notice, + notice_now: edit_in_new_fork_notice_now } + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, + continue: continue_params) + = link_to fork_path, method: :post do + #{ _('New file') } + %li + - continue_params = { to: request.fullpath, + notice: edit_in_new_fork_notice + " Try to upload a file again.", + notice_now: edit_in_new_fork_notice_now } + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, + continue: continue_params) + = link_to fork_path, method: :post do + #{ _('Upload file') } + %li + - continue_params = { to: request.fullpath, + notice: edit_in_new_fork_notice + " Try to create a new directory again.", + notice_now: edit_in_new_fork_notice_now } + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, + continue: continue_params) + = link_to fork_path, method: :post do + #{ _('New directory') } + + %li.divider + %li + = link_to new_project_branch_path(@project) do + #{ _('New branch') } + %li + = link_to new_project_tag_path(@project) do + #{ _('New tag') } .tree-controls - - if show_new_repo? - .editable-mode - - else - = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' + - if show_new_ide? + = succeed " " do + = link_to ide_edit_path(@project, @id), class: 'btn btn-default' do + = ide_edit_text + + = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' = render 'projects/find_file_link' diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index 64cc70053ef..709be20e00f 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -6,11 +6,7 @@ = content_for :meta_tags do = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") -- if show_new_repo? - - content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'repo' += render 'projects/last_push' -%div{ class: [(container_class unless show_new_repo?), ("limit-container-width" unless fluid_layout)] } - = render 'projects/last_push' +%div{ class: [(container_class), ("limit-container-width" unless fluid_layout)] } = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id) diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml index f4a4bfaec54..479bd2cdb38 100644 --- a/app/views/shared/_ref_switcher.html.haml +++ b/app/views/shared/_ref_switcher.html.haml @@ -1,6 +1,6 @@ - show_create = local_assigns.fetch(:show_create, false) -- show_new_branch_form = show_new_repo? && show_create && can?(current_user, :push_code, @project) +- show_new_branch_form = show_new_ide? && show_create && can?(current_user, :push_code, @project) - dropdown_toggle_text = @ref || @project.default_branch = form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do = hidden_field_tag :destination, destination diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 003f5fa52eb..63aa4e29ec9 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -52,7 +52,7 @@ = render_project_pipeline_status(project.pipeline_status) - if forks %span.prepend-left-10 - = icon('code-fork') + = sprite_icon('fork') = number_with_delimiter(project.forks_count) - if stars %span.prepend-left-10 diff --git a/app/views/shared/repo/_repo.html.haml b/app/views/shared/repo/_repo.html.haml deleted file mode 100644 index 87e8c416194..00000000000 --- a/app/views/shared/repo/_repo.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -- @no_container = true; -#repo{ data: { root: @path.empty?.to_s, - root_url: project_tree_path(project), - url: content_url, - current_branch: @ref, - ref: @commit.id, - project_name: project.name, - project_url: project_path(project), - project_id: project.id, - new_merge_request_url: namespace_project_new_merge_request_path(project.namespace, project, merge_request: { source_branch: '' }), - can_commit: (!!can_push_branch?(project, @ref)).to_s, - on_top_of_branch: (!!on_top_of_branch?(project, @ref)).to_s, - current_path: @path } } diff --git a/changelogs/unreleased/13634-broadcast-message.yml b/changelogs/unreleased/13634-broadcast-message.yml deleted file mode 100644 index 26c4c133443..00000000000 --- a/changelogs/unreleased/13634-broadcast-message.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix broadcast message not showing up on login page -merge_request: 15578 -author: -type: fixed diff --git a/changelogs/unreleased/15588-fix-empty-dropdown-on-create-new-pipeline-in-case-of-validation-errors.yml b/changelogs/unreleased/15588-fix-empty-dropdown-on-create-new-pipeline-in-case-of-validation-errors.yml deleted file mode 100644 index a4934c8896d..00000000000 --- a/changelogs/unreleased/15588-fix-empty-dropdown-on-create-new-pipeline-in-case-of-validation-errors.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Initializes the branches dropdown when the 'Start new pipeline' failed due to validation errors -merge_request: 15588 -author: Christiaan Van den Poel -type: fixed diff --git a/changelogs/unreleased/15774-fix-39233-500-in-merge-request.yml b/changelogs/unreleased/15774-fix-39233-500-in-merge-request.yml deleted file mode 100644 index 1179b3f20e6..00000000000 --- a/changelogs/unreleased/15774-fix-39233-500-in-merge-request.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 'fix #39233 - 500 in merge request' -merge_request: 15774 -author: Martin Nowak -type: fixed diff --git a/changelogs/unreleased/15955-improve-search-query.yml b/changelogs/unreleased/15955-improve-search-query.yml new file mode 100644 index 00000000000..80cb8af617f --- /dev/null +++ b/changelogs/unreleased/15955-improve-search-query.yml @@ -0,0 +1,5 @@ +--- +title: Improve search query for merge requests. +merge_request: +author: +type: performance diff --git a/changelogs/unreleased/18040-rubocop-line-break-after-guard-clause.yml b/changelogs/unreleased/18040-rubocop-line-break-after-guard-clause.yml deleted file mode 100644 index e3c7ffc8046..00000000000 --- a/changelogs/unreleased/18040-rubocop-line-break-after-guard-clause.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adds Rubocop rule for line break after guard clause -merge_request: 15188 -author: Jacopo Beschi @jacopo-beschi -type: added diff --git a/changelogs/unreleased/21143-customize-branch-name-when-using-create-branch-in-an-issue.yml b/changelogs/unreleased/21143-customize-branch-name-when-using-create-branch-in-an-issue.yml deleted file mode 100644 index d4c209aaf2b..00000000000 --- a/changelogs/unreleased/21143-customize-branch-name-when-using-create-branch-in-an-issue.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add an ability to use a custom branch name on creation from issues -merge_request: 13884 -author: Vitaliy @blackst0ne Klachkov -type: added diff --git a/changelogs/unreleased/22680-unlabel-slash-command-limit-autocomplete-to-applied-labels.yml b/changelogs/unreleased/22680-unlabel-slash-command-limit-autocomplete-to-applied-labels.yml deleted file mode 100644 index 6d7f8655282..00000000000 --- a/changelogs/unreleased/22680-unlabel-slash-command-limit-autocomplete-to-applied-labels.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Limit autocomplete menu to applied labels -merge_request: 11110 -author: Vitaliy @blackst0ne Klachkov -type: added diff --git a/changelogs/unreleased/28377-add-edit-button-to-mobile-file-view.yml b/changelogs/unreleased/28377-add-edit-button-to-mobile-file-view.yml deleted file mode 100644 index b6646379b8d..00000000000 --- a/changelogs/unreleased/28377-add-edit-button-to-mobile-file-view.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add edit button to mobile file view -merge_request: 15199 -author: Travis Miller -type: added diff --git a/changelogs/unreleased/29483-no-feedback-when-checking-on-checklist-if-potential-spam-was-detected.yml b/changelogs/unreleased/29483-no-feedback-when-checking-on-checklist-if-potential-spam-was-detected.yml deleted file mode 100644 index 6bfcc5e70de..00000000000 --- a/changelogs/unreleased/29483-no-feedback-when-checking-on-checklist-if-potential-spam-was-detected.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add recaptcha modal to issue updates detected as spam -merge_request: 15408 -author: -type: fixed diff --git a/changelogs/unreleased/32057_support_ordering_project_notes_in_notes_api.yml b/changelogs/unreleased/32057_support_ordering_project_notes_in_notes_api.yml deleted file mode 100644 index cd19d24e035..00000000000 --- a/changelogs/unreleased/32057_support_ordering_project_notes_in_notes_api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: added support for ordering and sorting in notes api -merge_request: 15342 -author: haseebeqx -type: added diff --git a/changelogs/unreleased/32098-pipelines-navigation.yml b/changelogs/unreleased/32098-pipelines-navigation.yml deleted file mode 100644 index 925c92b6be8..00000000000 --- a/changelogs/unreleased/32098-pipelines-navigation.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Stop reloading the page when using pagination and tabs - use API calls - in - Pipelines table -merge_request: -author: -type: other diff --git a/changelogs/unreleased/32878-merge-request-from-email.yml b/changelogs/unreleased/32878-merge-request-from-email.yml deleted file mode 100644 index 2df148d5a81..00000000000 --- a/changelogs/unreleased/32878-merge-request-from-email.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Allow creation of merge request from email -merge_request: 13817 -author: janp -type: added diff --git a/changelogs/unreleased/33338-internationalization-support-for-prometheus-service-configuration.yml b/changelogs/unreleased/33338-internationalization-support-for-prometheus-service-configuration.yml deleted file mode 100644 index d61bbf2e355..00000000000 --- a/changelogs/unreleased/33338-internationalization-support-for-prometheus-service-configuration.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add internationalization support for the prometheus integration -merge_request: 33338 -author: -type: other diff --git a/changelogs/unreleased/33609-hide-pagination.yml b/changelogs/unreleased/33609-hide-pagination.yml new file mode 100644 index 00000000000..3586b091cb1 --- /dev/null +++ b/changelogs/unreleased/33609-hide-pagination.yml @@ -0,0 +1,5 @@ +--- +title: Disable Vue pagination when only one page of content is available +merge_request: 15999 +author: Mario de la Ossa +type: fixed diff --git a/changelogs/unreleased/34600-performance-wiki-pages.yml b/changelogs/unreleased/34600-performance-wiki-pages.yml deleted file mode 100644 index 541ae8f8e60..00000000000 --- a/changelogs/unreleased/34600-performance-wiki-pages.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Performance issues when loading large number of wiki pages -merge_request: 15276 -author: -type: performance diff --git a/changelogs/unreleased/35385-allow-git-pull-push-on-project-redirects.yml b/changelogs/unreleased/35385-allow-git-pull-push-on-project-redirects.yml deleted file mode 100644 index 31450287caf..00000000000 --- a/changelogs/unreleased/35385-allow-git-pull-push-on-project-redirects.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Allow git pull/push on group/user/project redirects -merge_request: 15670 -author: -type: added diff --git a/changelogs/unreleased/35616-move-k8-to-cluster-page.yml b/changelogs/unreleased/35616-move-k8-to-cluster-page.yml deleted file mode 100644 index 032a39608ce..00000000000 --- a/changelogs/unreleased/35616-move-k8-to-cluster-page.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Create a new form to add Existing Kubernetes Cluster -merge_request: 14805 -author: -type: added diff --git a/changelogs/unreleased/35724-animate-sidebar.yml b/changelogs/unreleased/35724-animate-sidebar.yml deleted file mode 100644 index 5d0b46a23c8..00000000000 --- a/changelogs/unreleased/35724-animate-sidebar.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Animate contextual sidebar on collapse/expand -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/36400-trigger-job.yml b/changelogs/unreleased/36400-trigger-job.yml deleted file mode 100644 index 27243813dc8..00000000000 --- a/changelogs/unreleased/36400-trigger-job.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Prevent 500 error when inspecting job after trigger was removed -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/37354-pipelines-update.yml b/changelogs/unreleased/37354-pipelines-update.yml deleted file mode 100644 index 2b6ddfe95ed..00000000000 --- a/changelogs/unreleased/37354-pipelines-update.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Make sure head pippeline always corresponds with the head sha of an MR -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/38032-deploy-markers-should-be-more-verbose.yml b/changelogs/unreleased/38032-deploy-markers-should-be-more-verbose.yml deleted file mode 100644 index a1f28b3ba0f..00000000000 --- a/changelogs/unreleased/38032-deploy-markers-should-be-more-verbose.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Changed the deploy markers on the prometheus dashboard to be more verbose -merge_request: 38032 -author: -type: changed diff --git a/changelogs/unreleased/38075_allow_refernce_integer_labels.yml b/changelogs/unreleased/38075_allow_refernce_integer_labels.yml deleted file mode 100644 index b5342d4adf8..00000000000 --- a/changelogs/unreleased/38075_allow_refernce_integer_labels.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix errors when selecting numeric-only labels in the labels autocomplete selector -merge_request: 14607 -author: haseebeqx -type: fixed diff --git a/changelogs/unreleased/38393-Milestone-duration-error-message-is-not-accurate-enough.yml b/changelogs/unreleased/38393-Milestone-duration-error-message-is-not-accurate-enough.yml deleted file mode 100644 index c73cf8bf60b..00000000000 --- a/changelogs/unreleased/38393-Milestone-duration-error-message-is-not-accurate-enough.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Changed validation error message on wrong milestone dates -merge_request: -author: Xurxo Méndez Pérez -type: fixed diff --git a/changelogs/unreleased/38822-oauth-search-case-insensitive.yml b/changelogs/unreleased/38822-oauth-search-case-insensitive.yml deleted file mode 100644 index d84360b4c5c..00000000000 --- a/changelogs/unreleased/38822-oauth-search-case-insensitive.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: OAuth identity lookups case-insensitive -merge_request: 15312 -author: -type: fixed diff --git a/changelogs/unreleased/38862-email-notifications-not-sent-as-expected.yml b/changelogs/unreleased/38862-email-notifications-not-sent-as-expected.yml deleted file mode 100644 index 6b1b309ab14..00000000000 --- a/changelogs/unreleased/38862-email-notifications-not-sent-as-expected.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fix sending notification emails to users with the mention level set who were - mentioned in an issue or merge request description -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/38869-templates.yml b/changelogs/unreleased/38869-templates.yml deleted file mode 100644 index 957b5f27bd0..00000000000 --- a/changelogs/unreleased/38869-templates.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Remove template selector from global namespace -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/38877-disable-autocomplete-in-filtered-search.yml b/changelogs/unreleased/38877-disable-autocomplete-in-filtered-search.yml deleted file mode 100644 index 07439a860ec..00000000000 --- a/changelogs/unreleased/38877-disable-autocomplete-in-filtered-search.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Disables autocomplete in filtered searc -merge_request: 15477 -author: Jacopo Beschi @jacopo-beschi -type: added diff --git a/changelogs/unreleased/38962-automatically-run-a-pipeline-when-auto-devops-is-turned-on-in-project-settings.yml b/changelogs/unreleased/38962-automatically-run-a-pipeline-when-auto-devops-is-turned-on-in-project-settings.yml deleted file mode 100644 index a4d703bc69f..00000000000 --- a/changelogs/unreleased/38962-automatically-run-a-pipeline-when-auto-devops-is-turned-on-in-project-settings.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add the option to automatically run a pipeline after updating AutoDevOps settings -merge_request: 15380 -author: -type: changed diff --git a/changelogs/unreleased/39335-add-time-spend-to-milestones.yml b/changelogs/unreleased/39335-add-time-spend-to-milestones.yml deleted file mode 100644 index 41a43418cbf..00000000000 --- a/changelogs/unreleased/39335-add-time-spend-to-milestones.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add total time spent to milestones -merge_request: 15116 -author: George Andrinopoulos -type: added diff --git a/changelogs/unreleased/39364-in-issue-board-url-doesn-t-take-in-account-hostname-settings.yml b/changelogs/unreleased/39364-in-issue-board-url-doesn-t-take-in-account-hostname-settings.yml deleted file mode 100644 index 9793c6a8e9c..00000000000 --- a/changelogs/unreleased/39364-in-issue-board-url-doesn-t-take-in-account-hostname-settings.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Change boards page boards_data absolute urls to paths -merge_request: 15703 -author: -type: fixed diff --git a/changelogs/unreleased/39367-fix-new-email-session-path.yml b/changelogs/unreleased/39367-fix-new-email-session-path.yml deleted file mode 100644 index 73485d9d1a9..00000000000 --- a/changelogs/unreleased/39367-fix-new-email-session-path.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Confirming email with invalid token should no longer generate an error -merge_request: 15726 -author: -type: fixed diff --git a/changelogs/unreleased/39436-pages-api-administrative.yml b/changelogs/unreleased/39436-pages-api-administrative.yml deleted file mode 100644 index f38bbbd479c..00000000000 --- a/changelogs/unreleased/39436-pages-api-administrative.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add administrative endpoint to list all pages domains -merge_request: 15160 -author: Travis Miller -type: added diff --git a/changelogs/unreleased/39455-clone-dropdown-should-not-have-a-tooltip.yml b/changelogs/unreleased/39455-clone-dropdown-should-not-have-a-tooltip.yml deleted file mode 100644 index cb522cb7611..00000000000 --- a/changelogs/unreleased/39455-clone-dropdown-should-not-have-a-tooltip.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Removed tooltip from clone dropdown -merge_request: 15334 -author: -type: other diff --git a/changelogs/unreleased/39497-inline-edit-issue-on-mobile.yml b/changelogs/unreleased/39497-inline-edit-issue-on-mobile.yml deleted file mode 100644 index fc7c024f95a..00000000000 --- a/changelogs/unreleased/39497-inline-edit-issue-on-mobile.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add inline editing to issues on mobile -merge_request: 15438 -author: -type: changed diff --git a/changelogs/unreleased/39601-create-issuable-destroy-service.yml b/changelogs/unreleased/39601-create-issuable-destroy-service.yml deleted file mode 100644 index b0463f02eba..00000000000 --- a/changelogs/unreleased/39601-create-issuable-destroy-service.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Create issuable destroy service -merge_request: 15604 -author: George Andrinopoulos -type: other diff --git a/changelogs/unreleased/39602-move-update-project-counter-caches-out-of-issues-merge-requests.yml b/changelogs/unreleased/39602-move-update-project-counter-caches-out-of-issues-merge-requests.yml deleted file mode 100644 index 056afe43010..00000000000 --- a/changelogs/unreleased/39602-move-update-project-counter-caches-out-of-issues-merge-requests.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move update_project_counter_caches? out of issue and merge request -merge_request: 15300 -author: George Andrinopoulos -type: other diff --git a/changelogs/unreleased/39720-group-milestone-sorting.yml b/changelogs/unreleased/39720-group-milestone-sorting.yml deleted file mode 100644 index 15ef87fa567..00000000000 --- a/changelogs/unreleased/39720-group-milestone-sorting.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add dropdown sort to group milestones -merge_request: 15230 -author: George Andrinopoulos -type: added diff --git a/changelogs/unreleased/39727-add-axios-to-common.yml b/changelogs/unreleased/39727-add-axios-to-common.yml deleted file mode 100644 index 688757d2486..00000000000 --- a/changelogs/unreleased/39727-add-axios-to-common.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add axios to common file -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/39821-fix-commits-list-with-multi-file-editor.yml b/changelogs/unreleased/39821-fix-commits-list-with-multi-file-editor.yml deleted file mode 100644 index 8b27c43d15b..00000000000 --- a/changelogs/unreleased/39821-fix-commits-list-with-multi-file-editor.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix commits page throwing 500 when the multi-file editor was enabled -merge_request: 15502 -author: -type: fixed diff --git a/changelogs/unreleased/39827-fix-projects-dropdown-overflow.yml b/changelogs/unreleased/39827-fix-projects-dropdown-overflow.yml deleted file mode 100644 index ebd7b582e8a..00000000000 --- a/changelogs/unreleased/39827-fix-projects-dropdown-overflow.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix item name and namespace text overflow in Projects dropdown -merge_request: 15451 -author: -type: fixed diff --git a/changelogs/unreleased/39869_show_closed_status_of_links_to_issues_on_wiki_pages.yml b/changelogs/unreleased/39869_show_closed_status_of_links_to_issues_on_wiki_pages.yml deleted file mode 100644 index cd31ed463a2..00000000000 --- a/changelogs/unreleased/39869_show_closed_status_of_links_to_issues_on_wiki_pages.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: show status of gitlab reference links in wiki -merge_request: 15694 -author: haseebeqx -type: added diff --git a/changelogs/unreleased/39884-fix-pipeline-transition-with-single-manual-action.yml b/changelogs/unreleased/39884-fix-pipeline-transition-with-single-manual-action.yml deleted file mode 100644 index 580b97241e7..00000000000 --- a/changelogs/unreleased/39884-fix-pipeline-transition-with-single-manual-action.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fix pipeline status transition for single manual job. This would also fix pipeline - duration becuse it is depending on status transition -merge_request: 15251 -author: -type: fixed diff --git a/changelogs/unreleased/39895-cant-set-mattermost-username-channel-from-api.yml b/changelogs/unreleased/39895-cant-set-mattermost-username-channel-from-api.yml deleted file mode 100644 index 358c007387e..00000000000 --- a/changelogs/unreleased/39895-cant-set-mattermost-username-channel-from-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix acceptance of username for Mattermost service update -merge_request: 15275 -author: -type: fixed diff --git a/changelogs/unreleased/39977-gitlab-shell-default-timeout.yml b/changelogs/unreleased/39977-gitlab-shell-default-timeout.yml deleted file mode 100644 index b7a974fd8d9..00000000000 --- a/changelogs/unreleased/39977-gitlab-shell-default-timeout.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Set the default gitlab-shell timeout to 3 hours -merge_request: 15292 -author: -type: fixed diff --git a/changelogs/unreleased/40016-log-header.yml b/changelogs/unreleased/40016-log-header.yml deleted file mode 100644 index f52c2d2a0d5..00000000000 --- a/changelogs/unreleased/40016-log-header.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Hide log size for mobile screens -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/40040-decouple-multi-file-editor-from-file-list.yml b/changelogs/unreleased/40040-decouple-multi-file-editor-from-file-list.yml new file mode 100644 index 00000000000..e2fade2bfd9 --- /dev/null +++ b/changelogs/unreleased/40040-decouple-multi-file-editor-from-file-list.yml @@ -0,0 +1,5 @@ +--- +title: Adds the multi file editor as a new beta feature +merge_request: 15430 +author: +type: feature diff --git a/changelogs/unreleased/40122-only-one-note-webhook-is-triggered-when-a-comment-with-time-spent-is-added.yml b/changelogs/unreleased/40122-only-one-note-webhook-is-triggered-when-a-comment-with-time-spent-is-added.yml deleted file mode 100644 index a2ae2059c47..00000000000 --- a/changelogs/unreleased/40122-only-one-note-webhook-is-triggered-when-a-comment-with-time-spent-is-added.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add total_time_spent to the `changes` hash in issuable Webhook payloads -merge_request: 15381 -author: -type: changed diff --git a/changelogs/unreleased/40146_fix_special_charecter_search_in_filenames.yml b/changelogs/unreleased/40146_fix_special_charecter_search_in_filenames.yml deleted file mode 100644 index 00f7dd7c0f0..00000000000 --- a/changelogs/unreleased/40146_fix_special_charecter_search_in_filenames.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix search results when a filename would contain a special character. -merge_request: 15606 -author: haseebeqx -type: fixed diff --git a/changelogs/unreleased/40161-extra-margin-on-svg-logo-in-header.yml b/changelogs/unreleased/40161-extra-margin-on-svg-logo-in-header.yml deleted file mode 100644 index fdaa90f0d5d..00000000000 --- a/changelogs/unreleased/40161-extra-margin-on-svg-logo-in-header.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Remove extra margin from wordmark in header -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/40286-hide-full-namespace-groups-tree.yml b/changelogs/unreleased/40286-hide-full-namespace-groups-tree.yml deleted file mode 100644 index cae02d0c2f6..00000000000 --- a/changelogs/unreleased/40286-hide-full-namespace-groups-tree.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Show only group name by default and put full namespace in tooltip in Groups - tree -merge_request: 15650 -author: -type: changed diff --git a/changelogs/unreleased/40290-remove-rake-gitlab-sidekiq-drop-post-receive.yml b/changelogs/unreleased/40290-remove-rake-gitlab-sidekiq-drop-post-receive.yml deleted file mode 100644 index 9c308321a19..00000000000 --- a/changelogs/unreleased/40290-remove-rake-gitlab-sidekiq-drop-post-receive.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Removed unused rake task, 'rake gitlab:sidekiq:drop_post_receive' -merge_request: 15493 -author: -type: fixed diff --git a/changelogs/unreleased/40373-fix-issue-note-submit-disabled-on-paste.yml b/changelogs/unreleased/40373-fix-issue-note-submit-disabled-on-paste.yml deleted file mode 100644 index e683e60397e..00000000000 --- a/changelogs/unreleased/40373-fix-issue-note-submit-disabled-on-paste.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fix Issue comment submit button being disabled when pasting content from another - GFM note -merge_request: 15530 -author: -type: fixed diff --git a/changelogs/unreleased/40481-bump-jquery-to-2-2-4.yml b/changelogs/unreleased/40481-bump-jquery-to-2-2-4.yml deleted file mode 100644 index e275c65e8c8..00000000000 --- a/changelogs/unreleased/40481-bump-jquery-to-2-2-4.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Upgrade jQuery to 2.2.4 -merge_request: 15570 -author: Takuya Noguchi -type: security diff --git a/changelogs/unreleased/40508-snippets-zen-mode.yml b/changelogs/unreleased/40508-snippets-zen-mode.yml deleted file mode 100644 index c205218575b..00000000000 --- a/changelogs/unreleased/40508-snippets-zen-mode.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Init zen mode in snippets pages -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/40530-merge-request-generates-wrong-diff-when-branch-and-tag-have-the-same-name.yml b/changelogs/unreleased/40530-merge-request-generates-wrong-diff-when-branch-and-tag-have-the-same-name.yml deleted file mode 100644 index e9fae6fe0d7..00000000000 --- a/changelogs/unreleased/40530-merge-request-generates-wrong-diff-when-branch-and-tag-have-the-same-name.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix merge requests where the source or target branch name matches a tag name -merge_request: 15591 -author: -type: fixed diff --git a/changelogs/unreleased/40561-environment-scope-value-is-not-trimmed.yml b/changelogs/unreleased/40561-environment-scope-value-is-not-trimmed.yml deleted file mode 100644 index e0e3ddbdaa8..00000000000 --- a/changelogs/unreleased/40561-environment-scope-value-is-not-trimmed.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Strip leading & trailing whitespaces in CI/CD secret variable keys -merge_request: 15615 -author: -type: fixed diff --git a/changelogs/unreleased/40568-bump-seed-fu-to-2-3-7.yml b/changelogs/unreleased/40568-bump-seed-fu-to-2-3-7.yml deleted file mode 100644 index 708269d5c83..00000000000 --- a/changelogs/unreleased/40568-bump-seed-fu-to-2-3-7.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Upgrade seed-fu to 2.3.7 -merge_request: 15607 -author: Takuya Noguchi -type: other diff --git a/changelogs/unreleased/40573-rename-gke-as-kubernetes-engine.yml b/changelogs/unreleased/40573-rename-gke-as-kubernetes-engine.yml deleted file mode 100644 index afbb869bdbb..00000000000 --- a/changelogs/unreleased/40573-rename-gke-as-kubernetes-engine.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Rename GKE as Kubernetes Engine -merge_request: 15608 -author: Takuya Noguchi -type: other diff --git a/changelogs/unreleased/40770-doc-elasticsearch.yml b/changelogs/unreleased/40770-doc-elasticsearch.yml deleted file mode 100644 index b770fb7548e..00000000000 --- a/changelogs/unreleased/40770-doc-elasticsearch.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix typo in docs about Elasticsearch -merge_request: 15699 -author: Takuya Noguchi -type: other diff --git a/changelogs/unreleased/4080-align-retry-btn.yml b/changelogs/unreleased/4080-align-retry-btn.yml deleted file mode 100644 index c7d3997839c..00000000000 --- a/changelogs/unreleased/4080-align-retry-btn.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Align retry button with job title with new grid size -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/41268-bump-ruby-to-2-3-6.yml b/changelogs/unreleased/41268-bump-ruby-to-2-3-6.yml new file mode 100644 index 00000000000..188a854ebee --- /dev/null +++ b/changelogs/unreleased/41268-bump-ruby-to-2-3-6.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade Ruby to 2.3.6 to include security patches +merge_request: 16016 +author: +type: security diff --git a/changelogs/unreleased/add_project_ci_config_path_leading_slash_validation.yml b/changelogs/unreleased/add_project_ci_config_path_leading_slash_validation.yml deleted file mode 100644 index 1c96bd66ed4..00000000000 --- a/changelogs/unreleased/add_project_ci_config_path_leading_slash_validation.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Prefer ci_config_path validation for leading slashes instead of sanitizing - the input -merge_request: 15672 -author: Christiaan Van den Poel -type: other diff --git a/changelogs/unreleased/admin-welcome-new-group-link.yml b/changelogs/unreleased/admin-welcome-new-group-link.yml deleted file mode 100644 index 9c939a0a3ad..00000000000 --- a/changelogs/unreleased/admin-welcome-new-group-link.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fixed admin welcome screen new group path -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/an-gitaly-timeouts.yml b/changelogs/unreleased/an-gitaly-timeouts.yml deleted file mode 100644 index e18d82b2704..00000000000 --- a/changelogs/unreleased/an-gitaly-timeouts.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add timeouts for Gitaly calls -merge_request: 15047 -author: -type: performance diff --git a/changelogs/unreleased/brand_header_change.yml b/changelogs/unreleased/brand_header_change.yml deleted file mode 100644 index 6ea6e8192a4..00000000000 --- a/changelogs/unreleased/brand_header_change.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: When a custom header logo is present, don't show GitLab type logo -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/bvl-circuitbreaker-process.yml b/changelogs/unreleased/bvl-circuitbreaker-process.yml deleted file mode 100644 index 595dd13f724..00000000000 --- a/changelogs/unreleased/bvl-circuitbreaker-process.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Monitor NFS shards for circuitbreaker in a separate process -merge_request: 15426 -author: -type: changed diff --git a/changelogs/unreleased/bvl-delete-empty-fork-networks.yml b/changelogs/unreleased/bvl-delete-empty-fork-networks.yml deleted file mode 100644 index 3bbb4cf6e3c..00000000000 --- a/changelogs/unreleased/bvl-delete-empty-fork-networks.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Clean up empty fork networks -merge_request: 15373 -author: -type: other diff --git a/changelogs/unreleased/bvl-limit-fork-queries-on-project-show.yml b/changelogs/unreleased/bvl-limit-fork-queries-on-project-show.yml deleted file mode 100644 index 299d9bf6b9c..00000000000 --- a/changelogs/unreleased/bvl-limit-fork-queries-on-project-show.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Reduce requests for project forks on show page of projects that have forks -merge_request: 15663 -author: -type: performance diff --git a/changelogs/unreleased/bvl-subgroup-in-dropdowns.yml b/changelogs/unreleased/bvl-subgroup-in-dropdowns.yml deleted file mode 100644 index 1114d429dec..00000000000 --- a/changelogs/unreleased/bvl-subgroup-in-dropdowns.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Make sure a user can add projects to subgroups they have access to -merge_request: 15294 -author: -type: fixed diff --git a/changelogs/unreleased/cleanup-issues-schema.yml b/changelogs/unreleased/cleanup-issues-schema.yml deleted file mode 100644 index 9f5fb0bdf82..00000000000 --- a/changelogs/unreleased/cleanup-issues-schema.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Clean up schema of the "issues" table -merge_request: -author: -type: other diff --git a/changelogs/unreleased/commit-title-wrapping.yml b/changelogs/unreleased/commit-title-wrapping.yml deleted file mode 100644 index 65c28b82b8b..00000000000 --- a/changelogs/unreleased/commit-title-wrapping.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fixed long commit links not wrapping correctly -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/deploy-keys-loading-icon.yml b/changelogs/unreleased/deploy-keys-loading-icon.yml deleted file mode 100644 index e3cb5bc6924..00000000000 --- a/changelogs/unreleased/deploy-keys-loading-icon.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fixed deploy keys remove button loading state not resetting -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/dm-commit-diff-discussions-in-mr-context.yml b/changelogs/unreleased/dm-commit-diff-discussions-in-mr-context.yml deleted file mode 100644 index 1f8b42ea21f..00000000000 --- a/changelogs/unreleased/dm-commit-diff-discussions-in-mr-context.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Make diff notes created on a commit in a merge request to persist a rebase. -merge_request: 12148 -author: -type: added diff --git a/changelogs/unreleased/dm-image-blob-diff-full-url.yml b/changelogs/unreleased/dm-image-blob-diff-full-url.yml deleted file mode 100644 index db44a5a16b5..00000000000 --- a/changelogs/unreleased/dm-image-blob-diff-full-url.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Use app host instead of asset host when rendering image blob or diff -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/dm-ldap-email-readonly.yml b/changelogs/unreleased/dm-ldap-email-readonly.yml deleted file mode 100644 index 744b21f901e..00000000000 --- a/changelogs/unreleased/dm-ldap-email-readonly.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Make sure user email is read only when synced with LDAP -merge_request: 15915 -author: -type: fixed diff --git a/changelogs/unreleased/dm-search-pattern.yml b/changelogs/unreleased/dm-search-pattern.yml deleted file mode 100644 index 1670d8c4b9a..00000000000 --- a/changelogs/unreleased/dm-search-pattern.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Use fuzzy search with minimum length of 3 characters where appropriate -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/enable-scss-lint-unnecessary-mantissa.yml b/changelogs/unreleased/enable-scss-lint-unnecessary-mantissa.yml deleted file mode 100644 index 1049e94f312..00000000000 --- a/changelogs/unreleased/enable-scss-lint-unnecessary-mantissa.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Enable UnnecessaryMantissa in scss-lint -merge_request: 15255 -author: Takuya Noguchi -type: other diff --git a/changelogs/unreleased/feature-custom-text-for-new-projects.yml b/changelogs/unreleased/feature-custom-text-for-new-projects.yml deleted file mode 100644 index 905d76dab33..00000000000 --- a/changelogs/unreleased/feature-custom-text-for-new-projects.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add custom brand text on new project pages -merge_request: 15541 -author: Markus Koller -type: changed diff --git a/changelogs/unreleased/feature-disable-password-authentication.yml b/changelogs/unreleased/feature-disable-password-authentication.yml deleted file mode 100644 index 999203f12ce..00000000000 --- a/changelogs/unreleased/feature-disable-password-authentication.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Allow password authentication to be disabled entirely -merge_request: 15223 -author: Markus Koller -type: changed diff --git a/changelogs/unreleased/feature-sm-34834-missing-dependency-should-fail-job-2.yml b/changelogs/unreleased/feature-sm-34834-missing-dependency-should-fail-job-2.yml deleted file mode 100644 index ab85b8ee515..00000000000 --- a/changelogs/unreleased/feature-sm-34834-missing-dependency-should-fail-job-2.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fail jobs if its dependency is missing -merge_request: 14009 -author: -type: fixed diff --git a/changelogs/unreleased/feature_add_mermaid.yml b/changelogs/unreleased/feature_add_mermaid.yml deleted file mode 100644 index caeb5d3470d..00000000000 --- a/changelogs/unreleased/feature_add_mermaid.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 'Add support of Mermaid (generation of diagrams and flowcharts from text)' -merge_request: 15107 -author: Vitaliy @blackst0ne Klachkov -type: added diff --git a/changelogs/unreleased/fix-filter-by-my-reaction.yml b/changelogs/unreleased/fix-filter-by-my-reaction.yml deleted file mode 100644 index 8bf91ddf893..00000000000 --- a/changelogs/unreleased/fix-filter-by-my-reaction.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix filter by my reaction is not working -merge_request: 15345 -author: Hiroyuki Sato -type: fixed diff --git a/changelogs/unreleased/fix-last-push-event-widget-layout.yml b/changelogs/unreleased/fix-last-push-event-widget-layout.yml new file mode 100644 index 00000000000..ba5b115ca19 --- /dev/null +++ b/changelogs/unreleased/fix-last-push-event-widget-layout.yml @@ -0,0 +1,5 @@ +--- +title: Last push event widget width for fixed layout +merge_request: 15862 +author: George Tsiolis +type: fixed diff --git a/changelogs/unreleased/fix-new-project-guidelines-styling.yml b/changelogs/unreleased/fix-new-project-guidelines-styling.yml deleted file mode 100644 index a97f5c485d4..00000000000 --- a/changelogs/unreleased/fix-new-project-guidelines-styling.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Use Markdown styling for new project guidelines -merge_request: 15785 -author: Markus Koller -type: fixed diff --git a/changelogs/unreleased/fix-protected-branches-descriptions.yml b/changelogs/unreleased/fix-protected-branches-descriptions.yml deleted file mode 100644 index 8e233d9defd..00000000000 --- a/changelogs/unreleased/fix-protected-branches-descriptions.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Clarify wording of protected branch settings for the default branch -merge_request: -author: -type: other diff --git a/changelogs/unreleased/fix-sm-31771-do-not-allow-jobs-to-be-erased-new.yml b/changelogs/unreleased/fix-sm-31771-do-not-allow-jobs-to-be-erased-new.yml deleted file mode 100644 index 198116f34aa..00000000000 --- a/changelogs/unreleased/fix-sm-31771-do-not-allow-jobs-to-be-erased-new.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Only owner or master can erase jobs -merge_request: 15216 -author: -type: changed diff --git a/changelogs/unreleased/fix-sm-37991-avoid-deactivation-when-pipeline-schedules-execute-a-commit-includes-ci-skip.yml b/changelogs/unreleased/fix-sm-37991-avoid-deactivation-when-pipeline-schedules-execute-a-commit-includes-ci-skip.yml deleted file mode 100644 index 4e525c875ca..00000000000 --- a/changelogs/unreleased/fix-sm-37991-avoid-deactivation-when-pipeline-schedules-execute-a-commit-includes-ci-skip.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Avoid deactivation when pipeline schedules execute a branch includes `[ci skip]` - comment -merge_request: 15405 -author: -type: fixed diff --git a/changelogs/unreleased/fix-todos-last-page.yml b/changelogs/unreleased/fix-todos-last-page.yml deleted file mode 100644 index efcdbb75e6e..00000000000 --- a/changelogs/unreleased/fix-todos-last-page.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix access to the final page of todos -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/fj-40279-normalize-ldap-dn-api.yml b/changelogs/unreleased/fj-40279-normalize-ldap-dn-api.yml new file mode 100644 index 00000000000..3fd8b0eb988 --- /dev/null +++ b/changelogs/unreleased/fj-40279-normalize-ldap-dn-api.yml @@ -0,0 +1,5 @@ +--- +title: Normalizing Identity extern_uid when saving the record +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/fj-40407-missing-order-paginate.yml b/changelogs/unreleased/fj-40407-missing-order-paginate.yml deleted file mode 100644 index 27471dc2c52..00000000000 --- a/changelogs/unreleased/fj-40407-missing-order-paginate.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Added default order to UsersFinder -merge_request: 15679 -author: -type: fixed diff --git a/changelogs/unreleased/fj-40752-forks-api-not-using-services.yml b/changelogs/unreleased/fj-40752-forks-api-not-using-services.yml deleted file mode 100644 index cd7b87596e6..00000000000 --- a/changelogs/unreleased/fj-40752-forks-api-not-using-services.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Using appropiate services in the API for managing forks -merge_request: 15709 -author: -type: fixed diff --git a/changelogs/unreleased/fl-upgrade-svg.yml b/changelogs/unreleased/fl-upgrade-svg.yml deleted file mode 100644 index caf73881d0f..00000000000 --- a/changelogs/unreleased/fl-upgrade-svg.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update svg external depencency -merge_request: -author: -type: other diff --git a/changelogs/unreleased/group-new-miletone-breadcrumb.yml b/changelogs/unreleased/group-new-miletone-breadcrumb.yml deleted file mode 100644 index b82c5b604e8..00000000000 --- a/changelogs/unreleased/group-new-miletone-breadcrumb.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fixed new group milestone breadcrumbs -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/hashed-storage-attachments-migration-path.yml b/changelogs/unreleased/hashed-storage-attachments-migration-path.yml deleted file mode 100644 index 32535437046..00000000000 --- a/changelogs/unreleased/hashed-storage-attachments-migration-path.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Hashed Storage migration script now supports migrating project attachments -merge_request: 15352 -author: -type: added diff --git a/changelogs/unreleased/improved-changes-dropdown.yml b/changelogs/unreleased/improved-changes-dropdown.yml deleted file mode 100644 index f305cbe573b..00000000000 --- a/changelogs/unreleased/improved-changes-dropdown.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improved diff changed files dropdown design -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/issue_39238.yml b/changelogs/unreleased/issue_39238.yml deleted file mode 100644 index 75a4969ca9e..00000000000 --- a/changelogs/unreleased/issue_39238.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix image diff notification email from showing wrong content -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/merge-request-lock-icon-size-fix.yml b/changelogs/unreleased/merge-request-lock-icon-size-fix.yml deleted file mode 100644 index 09c059a3011..00000000000 --- a/changelogs/unreleased/merge-request-lock-icon-size-fix.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fixed merge request lock icon size -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/merge-requests-schema-cleanup.yml b/changelogs/unreleased/merge-requests-schema-cleanup.yml deleted file mode 100644 index ccce9b1436c..00000000000 --- a/changelogs/unreleased/merge-requests-schema-cleanup.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Clean up schema of the "merge_requests" table -merge_request: -author: -type: other diff --git a/changelogs/unreleased/mk-add-old-attachments-to-uploads-table.yml b/changelogs/unreleased/mk-add-old-attachments-to-uploads-table.yml deleted file mode 100644 index 499543ef883..00000000000 --- a/changelogs/unreleased/mk-add-old-attachments-to-uploads-table.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add untracked files to uploads table -merge_request: 15270 -author: -type: other diff --git a/changelogs/unreleased/mk-add-user-rate-limits.yml b/changelogs/unreleased/mk-add-user-rate-limits.yml deleted file mode 100644 index 512757da5fc..00000000000 --- a/changelogs/unreleased/mk-add-user-rate-limits.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Add anonymous rate limit per IP, and authenticated (web or API) rate limits - per user -merge_request: 14708 -author: -type: added diff --git a/changelogs/unreleased/mk-fix-schema-dump-of-untracked-files-for-uploads.yml b/changelogs/unreleased/mk-fix-schema-dump-of-untracked-files-for-uploads.yml deleted file mode 100644 index 2691e85320c..00000000000 --- a/changelogs/unreleased/mk-fix-schema-dump-of-untracked-files-for-uploads.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix error during schema dump. -merge_request: 15866 -author: -type: fixed diff --git a/changelogs/unreleased/optimize-projects-for-imported-projects.yml b/changelogs/unreleased/optimize-projects-for-imported-projects.yml deleted file mode 100644 index 13186fa36d5..00000000000 --- a/changelogs/unreleased/optimize-projects-for-imported-projects.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: check the import_status field before doing SQL operations to check the import - url -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/osw-isolate-mr-widget-exposed-attributes.yml b/changelogs/unreleased/osw-isolate-mr-widget-exposed-attributes.yml deleted file mode 100644 index 6b05713d1a1..00000000000 --- a/changelogs/unreleased/osw-isolate-mr-widget-exposed-attributes.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Stop sending milestone and labels data over the wire for MR widget requests -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/outdated-browser-position-fix.yml b/changelogs/unreleased/outdated-browser-position-fix.yml deleted file mode 100644 index 801e45a28b3..00000000000 --- a/changelogs/unreleased/outdated-browser-position-fix.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fixed outdated browser flash positioning -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/patch-24.yml b/changelogs/unreleased/patch-24.yml deleted file mode 100644 index a670eb3ab56..00000000000 --- a/changelogs/unreleased/patch-24.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix graph notes number duplication. -merge_request: 15696 -author: Vladislav Kaverin -type: fixed diff --git a/changelogs/unreleased/pawel-reduce_cardinality_of_prometheus_metrics.yml b/changelogs/unreleased/pawel-reduce_cardinality_of_prometheus_metrics.yml new file mode 100644 index 00000000000..0cee0b634d6 --- /dev/null +++ b/changelogs/unreleased/pawel-reduce_cardinality_of_prometheus_metrics.yml @@ -0,0 +1,5 @@ +--- +title: Reduce the number of buckets in gitlab_cache_operation_duration_seconds metric +merge_request: 15881 +author: +type: changed diff --git a/changelogs/unreleased/pawel-update_prometheus_gem_to_well_tested_version.yml b/changelogs/unreleased/pawel-update_prometheus_gem_to_well_tested_version.yml deleted file mode 100644 index a4133ae5cec..00000000000 --- a/changelogs/unreleased/pawel-update_prometheus_gem_to_well_tested_version.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Reenable Prometheus metrics, add more control over Prometheus method instrumentation -merge_request: 15558 -author: -type: fixed diff --git a/changelogs/unreleased/perform-sql-matching-of-tags.yml b/changelogs/unreleased/perform-sql-matching-of-tags.yml deleted file mode 100644 index 39f8a867a4d..00000000000 --- a/changelogs/unreleased/perform-sql-matching-of-tags.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Perform SQL matching of Build&Runner tags to greatly speed-up job picking -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/sh-remove-allocation-tracking-influxdb.yml b/changelogs/unreleased/sh-remove-allocation-tracking-influxdb.yml deleted file mode 100644 index b98573df303..00000000000 --- a/changelogs/unreleased/sh-remove-allocation-tracking-influxdb.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Remove allocation tracking code from InfluxDB sampler for performance -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/skip_confirmation_user_API.yml b/changelogs/unreleased/skip_confirmation_user_API.yml deleted file mode 100644 index 144ccd69e68..00000000000 --- a/changelogs/unreleased/skip_confirmation_user_API.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Add email confirmation parameters for user creation and update via API -merge_request: -author: Daniel Juarez -type: added - - diff --git a/changelogs/unreleased/text-utils.yml b/changelogs/unreleased/text-utils.yml deleted file mode 100644 index b95bb82fe01..00000000000 --- a/changelogs/unreleased/text-utils.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Export text utils functions as es6 module and add tests -merge_request: -author: -type: other diff --git a/changelogs/unreleased/throttle-touching-of-objects.yml b/changelogs/unreleased/throttle-touching-of-objects.yml deleted file mode 100644 index 0a57bea7c83..00000000000 --- a/changelogs/unreleased/throttle-touching-of-objects.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Throttle the number of UPDATEs triggered by touch -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/tm-feature-list-runners-jobs-api.yml b/changelogs/unreleased/tm-feature-list-runners-jobs-api.yml deleted file mode 100644 index d75a2b68c30..00000000000 --- a/changelogs/unreleased/tm-feature-list-runners-jobs-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: New API endpoint - list jobs for a specified runner -merge_request: 15432 -author: -type: added diff --git a/changelogs/unreleased/tm-feature-namespace-by-id-api.yml b/changelogs/unreleased/tm-feature-namespace-by-id-api.yml deleted file mode 100644 index bc4a8949d28..00000000000 --- a/changelogs/unreleased/tm-feature-namespace-by-id-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add new API endpoint - get a namespace by ID -merge_request: 15442 -author: -type: added diff --git a/changelogs/unreleased/update-emoji-digests-with-latest-from-gemojione.yml b/changelogs/unreleased/update-emoji-digests-with-latest-from-gemojione.yml deleted file mode 100644 index e509a8df6bc..00000000000 --- a/changelogs/unreleased/update-emoji-digests-with-latest-from-gemojione.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: 'Update emojis. Add :gay_pride_flag: and :speech_left:. Remove extraneous comma - in :cartwheel_tone4:' -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/update-merge-worker-metrics.yml b/changelogs/unreleased/update-merge-worker-metrics.yml deleted file mode 100644 index c733675926a..00000000000 --- a/changelogs/unreleased/update-merge-worker-metrics.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add performance logging to UpdateMergeRequestsWorker. -merge_request: 15360 -author: -type: performance diff --git a/changelogs/unreleased/update_mr_changes_empty_page.yml b/changelogs/unreleased/update_mr_changes_empty_page.yml deleted file mode 100644 index bae73c21e8f..00000000000 --- a/changelogs/unreleased/update_mr_changes_empty_page.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update empty state page of merge request 'changes' tab -merge_request: 15611 -author: Vitaliy @blackst0ne Klachkov -type: added diff --git a/changelogs/unreleased/use-count_commits-directly.yml b/changelogs/unreleased/use-count_commits-directly.yml deleted file mode 100644 index 549e0744ea4..00000000000 --- a/changelogs/unreleased/use-count_commits-directly.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve the performance for counting commits -merge_request: 15628 -author: -type: performance diff --git a/changelogs/unreleased/use-merge-requests-diff-id-column.yml b/changelogs/unreleased/use-merge-requests-diff-id-column.yml deleted file mode 100644 index da4106ec8cf..00000000000 --- a/changelogs/unreleased/use-merge-requests-diff-id-column.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Make finding most recent merge request diffs more efficient -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/user-agent-gke-api.yml b/changelogs/unreleased/user-agent-gke-api.yml deleted file mode 100644 index 1abdbadd53b..00000000000 --- a/changelogs/unreleased/user-agent-gke-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Use custom user agent header in all GCP API requests. -merge_request: 15705 -author: -type: changed diff --git a/changelogs/unreleased/winh-subgroups-api.yml b/changelogs/unreleased/winh-subgroups-api.yml deleted file mode 100644 index c49e3621e9c..00000000000 --- a/changelogs/unreleased/winh-subgroups-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add /groups/:id/subgroups endpoint to API -merge_request: 15142 -author: marbemac -type: added diff --git a/changelogs/unreleased/zj-commit-show-n-1.yml b/changelogs/unreleased/zj-commit-show-n-1.yml deleted file mode 100644 index e536434f74a..00000000000 --- a/changelogs/unreleased/zj-commit-show-n-1.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fetch blobs in bulk when generating diffs -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/zj-empty-repo-importer.yml b/changelogs/unreleased/zj-empty-repo-importer.yml deleted file mode 100644 index 71d50af9a04..00000000000 --- a/changelogs/unreleased/zj-empty-repo-importer.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix GitHub importer using removed interface -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/zj-memoization-mr-commits.yml b/changelogs/unreleased/zj-memoization-mr-commits.yml deleted file mode 100644 index 59dfc6d6049..00000000000 --- a/changelogs/unreleased/zj-memoization-mr-commits.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Cache commits for MergeRequest diffs -merge_request: -author: -type: performance diff --git a/config/routes.rb b/config/routes.rb index 016140e0ede..f162043dd5e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -43,6 +43,8 @@ Rails.application.routes.draw do get 'liveness' => 'health#liveness' get 'readiness' => 'health#readiness' post 'storage_check' => 'health#storage_check' + get 'ide' => 'ide#index' + get 'ide/*vueroute' => 'ide#index', format: false resources :metrics, only: [:index] mount Peek::Railtie => '/peek' diff --git a/config/webpack.config.js b/config/webpack.config.js index d8797bbf4d3..1218b0ef208 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -70,7 +70,7 @@ var config = { protected_branches: './protected_branches', protected_tags: './protected_tags', registry_list: './registry/index.js', - repo: './repo/index.js', + ide: './ide/index.js', sidebar: './sidebar/sidebar_bundle.js', schedule_form: './pipeline_schedules/pipeline_schedule_form_bundle.js', schedules_index: './pipeline_schedules/pipeline_schedules_index_bundle.js', @@ -118,7 +118,10 @@ var config = { }, { test: /\_worker\.js$/, - loader: 'worker-loader', + use: [ + { loader: 'worker-loader' }, + { loader: 'babel-loader' }, + ], }, { test: /\.(worker(\.min)?\.js|pdf|bmpr)$/, @@ -176,8 +179,13 @@ var config = { return chunk.name; } return chunk.mapModules((m) => { - var chunkPath = m.request.split('!').pop(); - return path.relative(m.context, chunkPath); + const pagesBase = path.join(ROOT_PATH, 'app/assets/javascripts/pages'); + if (m.resource.indexOf(pagesBase) === 0) { + return path.relative(pagesBase, m.resource) + .replace(/\/index\.[a-z]+$/, '') + .replace(/\//g, '__'); + } + return path.relative(m.context, m.resource); }).join('_'); }), @@ -204,7 +212,7 @@ var config = { 'pipelines', 'pipelines_details', 'registry_list', - 'repo', + 'ide', 'schedule_form', 'schedules_index', 'sidebar', diff --git a/db/post_migrate/20171219121201_normalize_extern_uid_from_identities.rb b/db/post_migrate/20171219121201_normalize_extern_uid_from_identities.rb new file mode 100644 index 00000000000..286721a0894 --- /dev/null +++ b/db/post_migrate/20171219121201_normalize_extern_uid_from_identities.rb @@ -0,0 +1,29 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class NormalizeExternUidFromIdentities < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + MIGRATION = 'NormalizeLdapExternUidsRange'.freeze + DELAY_INTERVAL = 10.seconds + + disable_ddl_transaction! + + class Identity < ActiveRecord::Base + include EachBatch + + self.table_name = 'identities' + end + + def up + ldap_identities = Identity.where("provider like 'ldap%'") + + if ldap_identities.any? + queue_background_migration_jobs_by_range_at_intervals(Identity, MIGRATION, DELAY_INTERVAL) + end + end + + def down + end +end diff --git a/db/schema.rb b/db/schema.rb index 2048c50f892..2b20628bd53 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171213160445) do +ActiveRecord::Schema.define(version: 20171219121201) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/doc/articles/artifactory_and_gitlab/index.md b/doc/articles/artifactory_and_gitlab/index.md index c64851bad2b..6a590b53727 100644 --- a/doc/articles/artifactory_and_gitlab/index.md +++ b/doc/articles/artifactory_and_gitlab/index.md @@ -1,278 +1 @@ -# How to deploy Maven projects to Artifactory with GitLab CI/CD - -> **Article [Type](../../development/writing_documentation.md#types-of-technical-articles):** tutorial || -> **Level:** intermediary || -> **Author:** [Fabio Busatto](https://gitlab.com/bikebilly) || -> **Publication date:** 2017-08-15 - -## Introduction - -In this article, we will show how you can leverage the power of [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/) -to build a [Maven](https://maven.apache.org/) project, deploy it to [Artifactory](https://www.jfrog.com/artifactory/), and then use it from another Maven application as a dependency. - -You'll create two different projects: - -- `simple-maven-dep`: the app built and deployed to Artifactory (available at https://gitlab.com/gitlab-examples/maven/simple-maven-dep) -- `simple-maven-app`: the app using the previous one as a dependency (available at https://gitlab.com/gitlab-examples/maven/simple-maven-app) - -We assume that you already have a GitLab account on [GitLab.com](https://gitlab.com/), and that you know the basic usage of Git and [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/). -We also assume that an Artifactory instance is available and reachable from the internet, and that you have valid credentials to deploy on it. - -## Create the simple Maven dependency - -First of all, you need an application to work with: in this specific case we will -use a simple one, but it could be any Maven application. This will be the -dependency you want to package and deploy to Artifactory, in order to be -available to other projects. - -### Prepare the dependency application - -For this article you'll use a Maven app that can be cloned from our example -project: - -1. Log in to your GitLab account -1. Create a new project by selecting **Import project from ➔ Repo by URL** -1. Add the following URL: - - ``` - https://gitlab.com/gitlab-examples/maven/simple-maven-dep.git - ``` -1. Click **Create project** - -This application is nothing more than a basic class with a stub for a JUnit based test suite. -It exposes a method called `hello` that accepts a string as input, and prints a hello message on the screen. - -The project structure is really simple, and you should consider these two resources: - -- `pom.xml`: project object model (POM) configuration file -- `src/main/java/com/example/dep/Dep.java`: source of our application - -### Configure the Artifactory deployment - -The application is ready to use, but you need some additional steps to deploy it to Artifactory: - -1. Log in to Artifactory with your user's credentials. -1. From the main screen, click on the `libs-release-local` item in the **Set Me Up** panel. -1. Copy to clipboard the configuration snippet under the **Deploy** paragraph. -1. Change the `url` value in order to have it configurable via secret variables. -1. Copy the snippet in the `pom.xml` file for your project, just after the - `dependencies` section. The snippet should look like this: - - ```xml - <distributionManagement> - <repository> - <id>central</id> - <name>83d43b5afeb5-releases</name> - <url>${env.MAVEN_REPO_URL}/libs-release-local</url> - </repository> - </distributionManagement> - ``` - -Another step you need to do before you can deploy the dependency to Artifactory -is to configure the authentication data. It is a simple task, but Maven requires -it to stay in a file called `settings.xml` that has to be in the `.m2` subdirectory -in the user's homedir. - -Since you want to use GitLab Runner to automatically deploy the application, you -should create the file in the project's home directory and set a command line -parameter in `.gitlab-ci.yml` to use the custom location instead of the default one: - -1. Create a folder called `.m2` in the root of your repository -1. Create a file called `settings.xml` in the `.m2` folder -1. Copy the following content into a `settings.xml` file: - - ```xml - <settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd" - xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <servers> - <server> - <id>central</id> - <username>${env.MAVEN_REPO_USER}</username> - <password>${env.MAVEN_REPO_PASS}</password> - </server> - </servers> - </settings> - ``` - - Username and password will be replaced by the correct values using secret variables. - -### Configure GitLab CI/CD for `simple-maven-dep` - -Now it's time we set up [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/) to automatically build, test and deploy the dependency! - -GitLab CI/CD uses a file in the root of the repo, named `.gitlab-ci.yml`, to read the definitions for jobs -that will be executed by the configured GitLab Runners. You can read more about this file in the [GitLab Documentation](https://docs.gitlab.com/ee/ci/yaml/). - -First of all, remember to set up secret variables for your deployment. Navigate to your project's **Settings > CI/CD** page -and add the following secret variables (replace them with your current values, of course): - -- **MAVEN_REPO_URL**: `http://artifactory.example.com:8081/artifactory` (your Artifactory URL) -- **MAVEN_REPO_USER**: `gitlab` (your Artifactory username) -- **MAVEN_REPO_PASS**: `AKCp2WXr3G61Xjz1PLmYa3arm3yfBozPxSta4taP3SeNu2HPXYa7FhNYosnndFNNgoEds8BCS` (your Artifactory Encrypted Password) - -Now it's time to define jobs in `.gitlab-ci.yml` and push it to the repo: - -```yaml -image: maven:latest - -variables: - MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode" - MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" - -cache: - paths: - - .m2/repository/ - - target/ - -build: - stage: build - script: - - mvn $MAVEN_CLI_OPTS compile - -test: - stage: test - script: - - mvn $MAVEN_CLI_OPTS test - -deploy: - stage: deploy - script: - - mvn $MAVEN_CLI_OPTS deploy - only: - - master -``` - -GitLab Runner will use the latest [Maven Docker image](https://hub.docker.com/_/maven/), which already contains all the tools and the dependencies you need to manage the project, -in order to run the jobs. - -Environment variables are set to instruct Maven to use the `homedir` of the repo instead of the user's home when searching for configuration and dependencies. - -Caching the `.m2/repository folder` (where all the Maven files are stored), and the `target` folder (where our application will be created), is useful for speeding up the process -by running all Maven phases in a sequential order, therefore, executing `mvn test` will automatically run `mvn compile` if necessary. - -Both `build` and `test` jobs leverage the `mvn` command to compile the application and to test it as defined in the test suite that is part of the application. - -Deploy to Artifactory is done as defined by the secret variables we have just set up. -The deployment occurs only if we're pushing or merging to `master` branch, so that the development versions are tested but not published. - -Done! Now you have all the changes in the GitLab repo, and a pipeline has already been started for this commit. In the **Pipelines** tab you can see what's happening. -If the deployment has been successful, the deploy job log will output: - -``` -[INFO] ------------------------------------------------------------------------ -[INFO] BUILD SUCCESS -[INFO] ------------------------------------------------------------------------ -[INFO] Total time: 1.983 s -``` - ->**Note**: -the `mvn` command downloads a lot of files from the internet, so you'll see a lot of extra activity in the log the first time you run it. - -Yay! You did it! Checking in Artifactory will confirm that you have a new artifact available in the `libs-release-local` repo. - -## Create the main Maven application - -Now that you have the dependency available on Artifactory, it's time to use it! -Let's see how we can have it as a dependency to our main application. - -### Prepare the main application - -We'll use again a Maven app that can be cloned from our example project: - -1. Create a new project by selecting **Import project from ➔ Repo by URL** -1. Add the following URL: - - ``` - https://gitlab.com/gitlab-examples/maven/simple-maven-app.git - ``` -1. Click **Create project** - -This one is a simple app as well. If you look at the `src/main/java/com/example/app/App.java` -file you can see that it imports the `com.example.dep.Dep` class and calls the `hello` method passing `GitLab` as a parameter. - -Since Maven doesn't know how to resolve the dependency, you need to modify the configuration: - -1. Go back to Artifactory -1. Browse the `libs-release-local` repository -1. Select the `simple-maven-dep-1.0.jar` file -1. Find the configuration snippet from the **Dependency Declaration** section of the main panel -1. Copy the snippet in the `dependencies` section of the `pom.xml` file. - The snippet should look like this: - - ```xml - <dependency> - <groupId>com.example.dep</groupId> - <artifactId>simple-maven-dep</artifactId> - <version>1.0</version> - </dependency> - ``` - -### Configure the Artifactory repository location - -At this point you defined the dependency for the application, but you still miss where you can find the required files. -You need to create a `.m2/settings.xml` file as you did for the dependency project, and let Maven know the location using environment variables. - -Here is how you can get the content of the file directly from Artifactory: - -1. From the main screen, click on the `libs-release-local` item in the **Set Me Up** panel -1. Click on **Generate Maven Settings** -1. Click on **Generate Settings** -1. Copy to clipboard the configuration file -1. Save the file as `.m2/settings.xml` in your repo - -Now you are ready to use the Artifactory repository to resolve dependencies and use `simple-maven-dep` in your main application! - -### Configure GitLab CI/CD for `simple-maven-app` - -You need a last step to have everything in place: configure the `.gitlab-ci.yml` file for this project, as you already did for `simple-maven-dep`. - -You want to leverage [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/) to automatically build, test and run your awesome application, -and see if you can get the greeting as expected! - -All you need to do is to add the following `.gitlab-ci.yml` to the repo: - -```yaml -image: maven:latest - -stages: - - build - - test - - run - -variables: - MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode" - MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" - -cache: - paths: - - .m2/repository/ - - target/ - -build: - stage: build - script: - - mvn $MAVEN_CLI_OPTS compile - -test: - stage: test - script: - - mvn $MAVEN_CLI_OPTS test - -run: - stage: run - script: - - mvn $MAVEN_CLI_OPTS package - - mvn $MAVEN_CLI_OPTS exec:java -Dexec.mainClass="com.example.app.App" -``` - -It is very similar to the configuration used for `simple-maven-dep`, but instead of the `deploy` job there is a `run` job. -Probably something that you don't want to use in real projects, but here it is useful to see the application executed automatically. - -And that's it! In the `run` job output log you will find a friendly hello to GitLab! - -## Conclusion - -In this article we covered the basic steps to use an Artifactory Maven repository to automatically publish and consume artifacts. - -A similar approach could be used to interact with any other Maven compatible Binary Repository Manager. -Obviously, you can improve these examples, optimizing the `.gitlab-ci.yml` file to better suit your needs, and adapting to your workflow. +This document was moved to [another location](../../ci/examples/artifactory_and_gitlab/index.md) diff --git a/doc/articles/index.md b/doc/articles/index.md index 862fe0868a6..7b60ce1d8b9 100644 --- a/doc/articles/index.md +++ b/doc/articles/index.md @@ -28,7 +28,6 @@ Build, test, and deploy the software you develop with [GitLab CI/CD](../ci/READM | :------------ | :------: | --------------: | | [Autoscaling GitLab Runners on AWS](runner_autoscale_aws/index.md) | Admin guide | 2017-11-24 | | [How to test and deploy Laravel/PHP applications with GitLab CI/CD and Envoy](laravel_with_gitlab_and_envoy/index.md) | Tutorial | 2017-08-31 | -| [How to deploy Maven projects to Artifactory with GitLab CI/CD](artifactory_and_gitlab/index.md) | Tutorial | 2017-08-15 | | [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) | Concepts | 2017-07-13 | | [Dockerizing GitLab Review Apps](https://about.gitlab.com/2017/07/11/dockerizing-review-apps/) | Concepts | 2017-07-11 | | [Continuous Integration: From Jenkins to GitLab Using Docker](https://about.gitlab.com/2017/07/27/docker-my-precious/) | Concepts | 2017-07-27 | diff --git a/doc/ci/README.md b/doc/ci/README.md index 12404eddbe2..05d792dea0f 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -121,7 +121,7 @@ Here is an collection of tutorials and guides on setting up your CI pipeline. - [Analyze code quality with the Code Climate CLI](examples/code_climate.md) - **Articles** - [How to test and deploy Laravel/PHP applications with GitLab CI/CD and Envoy](../articles/laravel_with_gitlab_and_envoy/index.md) - - [How to deploy Maven projects to Artifactory with GitLab CI/CD](../articles/artifactory_and_gitlab/index.md) + - [How to deploy Maven projects to Artifactory with GitLab CI/CD](examples/artifactory_and_gitlab/index.md) - [Automated Debian packaging](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) - [Spring boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) - [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md index d05b4db953a..25a0c5dcff5 100644 --- a/doc/ci/examples/README.md +++ b/doc/ci/examples/README.md @@ -27,8 +27,7 @@ Apart from those, here is an collection of tutorials and guides on setting up yo ### Java -- **Articles:** - - [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) +- [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) ### Scala @@ -41,18 +40,15 @@ Apart from those, here is an collection of tutorials and guides on setting up yo ### Elixir - [Test a Phoenix application](test-phoenix-application.md) -- **Articles:** - - [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/) +- [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/) ### iOS -- **Articles:** - - [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) +- [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) ### Android -- **Articles:** - - [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/) +- [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/) ### Code quality analysis @@ -63,20 +59,19 @@ Apart from those, here is an collection of tutorials and guides on setting up yo - [Using `dpl` as deployment tool](deployment/README.md) - [Repositories with examples for various languages](https://gitlab.com/groups/gitlab-examples) - [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml) -- **Articles:** - - [Continuous Deployment with GitLab: how to build and deploy a Debian Package with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) +- [Continuous Deployment with GitLab: how to build and deploy a Debian Package with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) +- [How to deploy Maven projects to Artifactory with GitLab CI/CD](artifactory_and_gitlab/index.md) -## GitLab CI for GitLab Pages +## GitLab CI/CD for GitLab Pages - [Example projects](https://gitlab.com/pages) -- **Articles:** - - [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](../../user/project/pages/getting_started_part_four.md) - - [SSGs Part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/): - examples for Ruby-, NodeJS-, Python-, and GoLang-based SSGs - - [Building a new GitLab docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) - - [Publish code coverage reports with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/) - -See the topic [GitLab Pages](../../user/project/pages/index.md) for a complete overview. +- [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](../../user/project/pages/getting_started_part_four.md) +- [SSGs Part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/): +examples for Ruby-, NodeJS-, Python-, and GoLang-based SSGs +- [Building a new GitLab docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) +- [Publish code coverage reports with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/) + +See the documentation on [GitLab Pages](../../user/project/pages/index.md) for a complete overview. ## More diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md new file mode 100644 index 00000000000..8e91cd05d8a --- /dev/null +++ b/doc/ci/examples/artifactory_and_gitlab/index.md @@ -0,0 +1,282 @@ +--- +redirect_from: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html' +--- + +# How to deploy Maven projects to Artifactory with GitLab CI/CD + +> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial || +> **Level:** intermediary || +> **Author:** [Fabio Busatto](https://gitlab.com/bikebilly) || +> **Publication date:** 2017-08-15 + +## Introduction + +In this article, we will show how you can leverage the power of [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/) +to build a [Maven](https://maven.apache.org/) project, deploy it to [Artifactory](https://www.jfrog.com/artifactory/), and then use it from another Maven application as a dependency. + +You'll create two different projects: + +- `simple-maven-dep`: the app built and deployed to Artifactory (available at https://gitlab.com/gitlab-examples/maven/simple-maven-dep) +- `simple-maven-app`: the app using the previous one as a dependency (available at https://gitlab.com/gitlab-examples/maven/simple-maven-app) + +We assume that you already have a GitLab account on [GitLab.com](https://gitlab.com/), and that you know the basic usage of Git and [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/). +We also assume that an Artifactory instance is available and reachable from the internet, and that you have valid credentials to deploy on it. + +## Create the simple Maven dependency + +First of all, you need an application to work with: in this specific case we will +use a simple one, but it could be any Maven application. This will be the +dependency you want to package and deploy to Artifactory, in order to be +available to other projects. + +### Prepare the dependency application + +For this article you'll use a Maven app that can be cloned from our example +project: + +1. Log in to your GitLab account +1. Create a new project by selecting **Import project from ➔ Repo by URL** +1. Add the following URL: + + ``` + https://gitlab.com/gitlab-examples/maven/simple-maven-dep.git + ``` +1. Click **Create project** + +This application is nothing more than a basic class with a stub for a JUnit based test suite. +It exposes a method called `hello` that accepts a string as input, and prints a hello message on the screen. + +The project structure is really simple, and you should consider these two resources: + +- `pom.xml`: project object model (POM) configuration file +- `src/main/java/com/example/dep/Dep.java`: source of our application + +### Configure the Artifactory deployment + +The application is ready to use, but you need some additional steps to deploy it to Artifactory: + +1. Log in to Artifactory with your user's credentials. +1. From the main screen, click on the `libs-release-local` item in the **Set Me Up** panel. +1. Copy to clipboard the configuration snippet under the **Deploy** paragraph. +1. Change the `url` value in order to have it configurable via secret variables. +1. Copy the snippet in the `pom.xml` file for your project, just after the + `dependencies` section. The snippet should look like this: + + ```xml + <distributionManagement> + <repository> + <id>central</id> + <name>83d43b5afeb5-releases</name> + <url>${env.MAVEN_REPO_URL}/libs-release-local</url> + </repository> + </distributionManagement> + ``` + +Another step you need to do before you can deploy the dependency to Artifactory +is to configure the authentication data. It is a simple task, but Maven requires +it to stay in a file called `settings.xml` that has to be in the `.m2` subdirectory +in the user's homedir. + +Since you want to use GitLab Runner to automatically deploy the application, you +should create the file in the project's home directory and set a command line +parameter in `.gitlab-ci.yml` to use the custom location instead of the default one: + +1. Create a folder called `.m2` in the root of your repository +1. Create a file called `settings.xml` in the `.m2` folder +1. Copy the following content into a `settings.xml` file: + + ```xml + <settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd" + xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <servers> + <server> + <id>central</id> + <username>${env.MAVEN_REPO_USER}</username> + <password>${env.MAVEN_REPO_PASS}</password> + </server> + </servers> + </settings> + ``` + + Username and password will be replaced by the correct values using secret variables. + +### Configure GitLab CI/CD for `simple-maven-dep` + +Now it's time we set up [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/) to automatically build, test and deploy the dependency! + +GitLab CI/CD uses a file in the root of the repo, named `.gitlab-ci.yml`, to read the definitions for jobs +that will be executed by the configured GitLab Runners. You can read more about this file in the [GitLab Documentation](https://docs.gitlab.com/ee/ci/yaml/). + +First of all, remember to set up secret variables for your deployment. Navigate to your project's **Settings > CI/CD** page +and add the following secret variables (replace them with your current values, of course): + +- **MAVEN_REPO_URL**: `http://artifactory.example.com:8081/artifactory` (your Artifactory URL) +- **MAVEN_REPO_USER**: `gitlab` (your Artifactory username) +- **MAVEN_REPO_PASS**: `AKCp2WXr3G61Xjz1PLmYa3arm3yfBozPxSta4taP3SeNu2HPXYa7FhNYosnndFNNgoEds8BCS` (your Artifactory Encrypted Password) + +Now it's time to define jobs in `.gitlab-ci.yml` and push it to the repo: + +```yaml +image: maven:latest + +variables: + MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode" + MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" + +cache: + paths: + - .m2/repository/ + - target/ + +build: + stage: build + script: + - mvn $MAVEN_CLI_OPTS compile + +test: + stage: test + script: + - mvn $MAVEN_CLI_OPTS test + +deploy: + stage: deploy + script: + - mvn $MAVEN_CLI_OPTS deploy + only: + - master +``` + +GitLab Runner will use the latest [Maven Docker image](https://hub.docker.com/_/maven/), which already contains all the tools and the dependencies you need to manage the project, +in order to run the jobs. + +Environment variables are set to instruct Maven to use the `homedir` of the repo instead of the user's home when searching for configuration and dependencies. + +Caching the `.m2/repository folder` (where all the Maven files are stored), and the `target` folder (where our application will be created), is useful for speeding up the process +by running all Maven phases in a sequential order, therefore, executing `mvn test` will automatically run `mvn compile` if necessary. + +Both `build` and `test` jobs leverage the `mvn` command to compile the application and to test it as defined in the test suite that is part of the application. + +Deploy to Artifactory is done as defined by the secret variables we have just set up. +The deployment occurs only if we're pushing or merging to `master` branch, so that the development versions are tested but not published. + +Done! Now you have all the changes in the GitLab repo, and a pipeline has already been started for this commit. In the **Pipelines** tab you can see what's happening. +If the deployment has been successful, the deploy job log will output: + +``` +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 1.983 s +``` + +>**Note**: +the `mvn` command downloads a lot of files from the internet, so you'll see a lot of extra activity in the log the first time you run it. + +Yay! You did it! Checking in Artifactory will confirm that you have a new artifact available in the `libs-release-local` repo. + +## Create the main Maven application + +Now that you have the dependency available on Artifactory, it's time to use it! +Let's see how we can have it as a dependency to our main application. + +### Prepare the main application + +We'll use again a Maven app that can be cloned from our example project: + +1. Create a new project by selecting **Import project from ➔ Repo by URL** +1. Add the following URL: + + ``` + https://gitlab.com/gitlab-examples/maven/simple-maven-app.git + ``` +1. Click **Create project** + +This one is a simple app as well. If you look at the `src/main/java/com/example/app/App.java` +file you can see that it imports the `com.example.dep.Dep` class and calls the `hello` method passing `GitLab` as a parameter. + +Since Maven doesn't know how to resolve the dependency, you need to modify the configuration: + +1. Go back to Artifactory +1. Browse the `libs-release-local` repository +1. Select the `simple-maven-dep-1.0.jar` file +1. Find the configuration snippet from the **Dependency Declaration** section of the main panel +1. Copy the snippet in the `dependencies` section of the `pom.xml` file. + The snippet should look like this: + + ```xml + <dependency> + <groupId>com.example.dep</groupId> + <artifactId>simple-maven-dep</artifactId> + <version>1.0</version> + </dependency> + ``` + +### Configure the Artifactory repository location + +At this point you defined the dependency for the application, but you still miss where you can find the required files. +You need to create a `.m2/settings.xml` file as you did for the dependency project, and let Maven know the location using environment variables. + +Here is how you can get the content of the file directly from Artifactory: + +1. From the main screen, click on the `libs-release-local` item in the **Set Me Up** panel +1. Click on **Generate Maven Settings** +1. Click on **Generate Settings** +1. Copy to clipboard the configuration file +1. Save the file as `.m2/settings.xml` in your repo + +Now you are ready to use the Artifactory repository to resolve dependencies and use `simple-maven-dep` in your main application! + +### Configure GitLab CI/CD for `simple-maven-app` + +You need a last step to have everything in place: configure the `.gitlab-ci.yml` file for this project, as you already did for `simple-maven-dep`. + +You want to leverage [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/) to automatically build, test and run your awesome application, +and see if you can get the greeting as expected! + +All you need to do is to add the following `.gitlab-ci.yml` to the repo: + +```yaml +image: maven:latest + +stages: + - build + - test + - run + +variables: + MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode" + MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" + +cache: + paths: + - .m2/repository/ + - target/ + +build: + stage: build + script: + - mvn $MAVEN_CLI_OPTS compile + +test: + stage: test + script: + - mvn $MAVEN_CLI_OPTS test + +run: + stage: run + script: + - mvn $MAVEN_CLI_OPTS package + - mvn $MAVEN_CLI_OPTS exec:java -Dexec.mainClass="com.example.app.App" +``` + +It is very similar to the configuration used for `simple-maven-dep`, but instead of the `deploy` job there is a `run` job. +Probably something that you don't want to use in real projects, but here it is useful to see the application executed automatically. + +And that's it! In the `run` job output log you will find a friendly hello to GitLab! + +## Conclusion + +In this article we covered the basic steps to use an Artifactory Maven repository to automatically publish and consume artifacts. + +A similar approach could be used to interact with any other Maven compatible Binary Repository Manager. +Obviously, you can improve these examples, optimizing the `.gitlab-ci.yml` file to better suit your needs, and adapting to your workflow. diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md index db13e0e6249..9cb1f708a6a 100644 --- a/doc/development/doc_styleguide.md +++ b/doc/development/doc_styleguide.md @@ -18,6 +18,10 @@ like `docs.gitlab.com/user/project/merge_requests.html`. With this pattern, you can immediately tell that you are navigating a user related documentation and is about the project and its merge requests. +Do not create summaries of similar types of content (e.g. an index of all articles, videos, etc.), +rather organise content by its subject (e.g. everything related to CI goes together) +and cross-link between any related content. + The table below shows what kind of documentation goes where. | Directory | What belongs here | diff --git a/doc/development/ux_guide/img/james-mackey.png b/doc/development/ux_guide/img/james-mackey.png Binary files differindex 6db257c5b39..c8f9097f69f 100644 --- a/doc/development/ux_guide/img/james-mackey.png +++ b/doc/development/ux_guide/img/james-mackey.png diff --git a/doc/development/ux_guide/img/karolina-plaskaty.png b/doc/development/ux_guide/img/karolina-plaskaty.png Binary files differindex 2e356c99762..ae2e98b7bad 100644 --- a/doc/development/ux_guide/img/karolina-plaskaty.png +++ b/doc/development/ux_guide/img/karolina-plaskaty.png diff --git a/doc/development/ux_guide/img/matthieu-poirier.png b/doc/development/ux_guide/img/matthieu-poirier.png Binary files differnew file mode 100644 index 00000000000..dd21948ebe2 --- /dev/null +++ b/doc/development/ux_guide/img/matthieu-poirier.png diff --git a/doc/development/ux_guide/img/nazim-ramesh.png b/doc/development/ux_guide/img/nazim-ramesh.png Binary files differindex 01ba0391630..cc3e197679d 100644 --- a/doc/development/ux_guide/img/nazim-ramesh.png +++ b/doc/development/ux_guide/img/nazim-ramesh.png diff --git a/doc/development/ux_guide/users.md b/doc/development/ux_guide/users.md index fce882a45f1..6afb33cfc36 100644 --- a/doc/development/ux_guide/users.md +++ b/doc/development/ux_guide/users.md @@ -1,127 +1,252 @@ # UX Personas * [Nazim Ramesh](#nazim-ramesh) - - Small to medium size organisations using GitLab CE + - Small to medium size organizations using GitLab CE +* [Matthieu Poirier](#matthieu-poirier) + - Responsible for managing and maintaining GitLab installation + - Any size organization + - Using CE or EE * [James Mackey](#james-mackey) - - Medium to large size organisations using CE or EE - - Small organisations using EE + - Medium to large size organizations using CE or EE + - Small organizations using EE * [Karolina Plaskaty](#karolina-plaskaty) - Using GitLab.com for personal/hobby projects - Would like to use GitLab at work - - Working for a medium to large size organisation + - Working within a medium to large size organization --- ## Nazim Ramesh -- Small to medium size organisations using GitLab CE +- Small to medium size organizations using GitLab CE -<img src="img/nazim-ramesh.png" width="300px"> +![nazim-ramesh](img/nazim-ramesh.png) ### Demographics -- **Age**<br>32 years old -- **Location**<br>Germany -- **Education**<br>Bachelor of Science in Computer Science -- **Occupation**<br>Full-stack web developer -- **Programming experience**<br>Over 10 years -- **Frequently used programming languages**<br>JavaScript, SQL, PHP -- **Hobbies / interests**<br>Functional programming, open source, gaming, web development and web security. +**Age** + +32 years old + +**Location** + +Germany + +**Education** + +Bachelor of Science in Computer Science + +**Occupation** + +Full-stack web developer + +**Programming experience** + +Over 10 years + +**Frequently used programming languages** + +JavaScript, SQL, PHP + +**Hobbies / interests** + +Functional programming, open source, gaming, web development, and web security. ### Motivations -Nazim works for a software development company which currently hires around 80 people. When Nazim first joined the company, the engineering team were using Subversion (SVN) as their primary form of source control. However, Nazim felt SVN was not flexible enough to work with many feature branches and noticed that developers with less experience of source control struggled with the central-repository nature of SVN. Armed with a wishlist of features, Nazim began comparing source control tools. A search for “self-hosted Git server repository management” returned GitLab. In his own words, Nazim explains why he wanted the engineering team to start using GitLab: +Nazim works for a software development company which currently hires around 80 people. When Nazim first joined the company, the engineering team were using Subversion (SVN) as their primary form of source control. However, Nazim felt SVN was not flexible enough to work with many feature branches and noticed that developers with less experience of source control struggled with the central-repository nature of SVN. Armed with a wish list of features, Nazim began comparing source control tools. A search for "self-hosted Git server repository management" returned GitLab. In his own words, Nazim explains why he wanted the engineering team to start using GitLab: -> -“I wanted them to switch away from SVN. I needed a server application to manage repositories. The common tools that were around just didn’t meet the requirements. Most of them were too simple or plain...GitLab provided all the required features. Also costs had to be low, since we don’t have a big budget for those things...the Community Edition was perfect in this regard.” -> +>"I wanted them to switch away from SVN. I needed a server application to manage repositories. The common tools that were around just didn't meet the requirements. Most of them were too simple or plain...GitLab provided all the required features. Also, costs had to be low since we don't have a big budget for those things...the Community Edition was perfect in this regard." In his role as a full-stack web developer, Nazim could recommend products that he would like the engineering team to use, but final approval lay with his line manager, Mike, VP of Engineering. Nazim recalls that he was met with reluctance from his colleagues when he raised moving to Git and using GitLab. -> -“The biggest challenge...why should we change anything at all from the status quo? We needed to switch from SVN to Git. They knew they needed to learn Git and a Git workflow...using Git was scary to my colleagues...they thought it was more complex than SVN to use.” -> +>"The biggest challenge...why should we change anything at all from the status quo? We needed to switch from SVN to Git. They knew they needed to learn Git and a Git workflow...using Git was scary to my colleagues...they thought it was more complex than SVN to use." Undeterred, Nazim decided to migrate a couple of projects across to GitLab. -> -“Old SVN users couldn’t see the benefits of Git at first. It took a month or two to convince them.” -> +>"Old SVN users couldn't see the benefits of Git at first. It took a month or two to convince them." -Slowly, by showing his colleagues how easy it was to use Git, the majority of the team’s projects were migrated to GitLab. +Slowly, by showing his colleagues how easy it was to use Git, the majority of the team's projects were migrated to GitLab. -The engineering team have been using GitLab CE for around 2 years now. Nazim credits himself as being entirely responsible for his company’s decision to move to GitLab. +The engineering team have been using GitLab CE for around 2 years now. Nazim credits himself as being entirely responsible for his company's decision to move to GitLab. ### Frustrations #### Adoption to GitLab has been slow -Not only has the engineering team had to get to grips with Git, they’ve also had to adapt to using GitLab. Due to lack of training and existing skills in other tools, the full feature set of GitLab CE is not being utilised. Nazim sold GitLab to his manager as an ‘all in one’ tool which would replace multiple tools used within the company, thus saving costs. Nazim hasn’t had the time to integrate the legacy tools to GitLab and he’s struggling to convince his peers to change their habits. +Not only has the engineering team had to get to grips with Git, they've also had to adapt to using GitLab. Due to lack of training and existing skills in other tools, the full feature set of GitLab CE is not being utilized. Nazim sold GitLab to his manager as an ‘all in one' tool which would replace multiple tools used within the company, thus saving costs. Nazim hasn't had the time to integrate the legacy tools to GitLab and he's struggling to convince his peers to change their habits. #### Missing Features -Nazim’s company want GitLab to be able to do everything. There isn’t a large budget for software, so they’re selective about what tools are implemented. It needs to add real value to the company. In order for GitLab to be widely adopted and to meet the requirements of different roles within the company, it needs a host of features. When an individual within Nazim’s company wants to know if GitLab has a specific feature or does a particular thing, Nazim is the person to ask. He becomes the point of contact to investigate, build or sometimes just raise the feature request. Nazim gets frustrated when GitLab isn’t able to do what he or his colleagues need it to do. +Nazim's company want GitLab to be able to do everything. There isn't a large budget for software, so they're selective about what tools are implemented. It needs to add real value to the company. In order for GitLab to be widely adopted and to meet the requirements of different roles within the company, it needs a host of features. When an individual within Nazim's company wants to know if GitLab has a specific feature or does a particular thing, Nazim is the person to ask. He becomes the point of contact to investigate, build or sometimes just raise the feature request. Nazim gets frustrated when GitLab isn't able to do what he or his colleagues need it to do. #### Regressions and bugs -Nazim often has to calm down his colleagues, when a release contains regressions or new bugs. As he puts it “every new version adds something awesome, but breaks something”. He feels that “old issues for "minor" annoyances get quickly buried in the mass of open issues and linger for a very long time. More generally, I have the feeling that GitLab focus on adding new functionalities, but overlook a bunch of annoying minor regressions or introduced bugs.” Due to limited resource and expertise within the team, not only is it difficult to remain up-to-date with the frequent release cycle, it’s also counterproductive to fix workflows every month. +Nazim often has to calm down his colleagues, when a release contains regressions or new bugs. As he puts it "every new version adds something awesome, but breaks something". He feels that "old issues for minor annoyances get quickly buried in the mass of open issues and linger for a very long time. More generally, I have the feeling that GitLab focus on adding new functionalities, but overlook a bunch of annoying minor regressions or introduced bugs." Due to limited resource and expertise within the team, not only is it difficult to remain up-to-date with the frequent release cycle, it's also counterproductive to fix workflows every month. #### Uses too much RAM and CPU -> -“Memory usages mean that if we host it from a cloud based host like AWS, we spend almost as much on the instance as what we would pay GitHub” -> +>"Memory usages mean that if we host it from a cloud-based host like AWS, we spend almost as much on the instance as what we would pay GitHub" + #### UI/UX -GitLab’s interface initially attracted Nazim when he was comparing version control software. He thought it would help his less technical colleagues to adapt to using Git and perhaps, GitLab could be rolled out to other areas of the business, beyond engineering. However, using GitLab’s interface daily has left him frustrated at the lack of personalisation / control over his user experience. He’s also regularly lost in a maze of navigation. Whilst he acknowledges that GitLab listens to its users and that the interface is improving, he becomes annoyed when the changes are too progressive. “Too frequent UI changes. Most of them tend to turn out great after a few cycles of fixes, but the frequency is still far too high for me to feel comfortable to always stay on the current release.” +GitLab's interface initially attracted Nazim when he was comparing version control software. He thought it would help his less technical colleagues to adapt to using Git and perhaps, GitLab could be rolled out to other areas of the business, beyond engineering. However, using GitLab's interface daily has left him frustrated at the lack of personalization/control over his user experience. He's also regularly lost in a maze of navigation. Whilst he acknowledges that GitLab listens to its users and that the interface is improving, he becomes annoyed when the changes are too progressive. "Too frequent UI changes. Most of them tend to turn out great after a few cycles of fixes, but the frequency is still far too high for me to feel comfortable to always stay on the current release." ### Goals * To convince his colleagues to fully adopt GitLab CE, thus improving workflow and collaboration. -* To use a feature rich version control platform that covers all stages of the development lifecycle, in order to reduce dependencies on other tools. +* To use a feature-rich version control platform that covers all stages of the development lifecycle, in order to reduce dependencies on other tools. * To use an intuitive and stable product, so he can spend more time on his core job responsibilities and less time bug-fixing, guiding colleagues, etc. --- +## Matthieu Poirier +- Responsible for managing and maintaining GitLab installation +- Any size organization +- Using CE or EE + +![matthieu-poirier](img/matthieu-poirier.png) + +### Demographics + +**Age** + +42 years old + +**Location** + +France + +**Education** + +Masters Degree in Computer Science + +**Occupation** + +DevOps Engineer + +**Programming experience** + +Over 10 years + +**Frequently used programming languages** + +JavaScript, SQL, PHP and Node.js + +**Hobbies / interests** + +Functional programming, data analysis, building apps, and tools. + +### Motivations +Matthieu works in DevOps for a web services company which currently hires 90 staff. When Matthieu first joined the company, he was responsible for managing a custom built in-house bug tracking tool and release management system. Over time, as the company grew, his colleagues requested more features and tools to help them in their day-to-day work. To meet their needs, Matthieu was forced to "hack together" a solution. In his own words, Matthieu explains that it became: + +>"...a huge pain managing access to all the individual pieces. In addition, they didn't have any integration with each other, nobody ended up using them and we couldn't do any workflows with merge requests and the like. I was sick of managing all those separate parts and wanted to move to a single platform that would handle it all." + + +He further explains that he wanted to introduce "better, easier, more formal code reviews" and to start using continuous integration and deployment. + +Matthieu tried to find a platform which would consolidate the company's existing toolset, and centralize code, documentation, and issues. He discovered GitHub, but the price was an issue: + +>"We needed to host our code on-site and wanted GitHub Enterprise functionality without the GitHub Enterprise costs." + + +Not only was GitLab cheaper than GitHub, it was also more cost-effective than maintaining multiple tools. Subsequently, Matthieu found it easy to sell the merits of GitLab to his manager. + +Matthieu describes GitLab as: + +>"the only tool that offers the real feeling of having everything you need in one place." + + +He credits himself as being entirely responsible for moving his company to GitLab. + +### Frustrations +#### Updating to the latest release +Matthieu introduced his company to GitLab. He is responsible for maintaining and managing the company's installation in addition to his day job. He feels updates are too frequent and he doesn't always have sufficient time to update GitLab. As a result, he's not up to date with releases. + +Matthieu tried to set up automatic updates, however, as he isn't a Systems Administrator, he wasn't confident in his set-up. He feels he should be able to "upgrade without users even noticing" but hasn't figured out how to do this yet. Matthieu would like the "update process to be triggered from the Admin Panel, perhaps accompanied with a changelog and the option to skip updates." + +Matthieu is looking for confirmation that his update procedure is "secure and efficient" so more tutorials related to this topic would be useful to him. + +#### Configuration +Matthieu dislikes using the combination of gitlab.rb and the UI for changing settings. He explains that it "would be nice to be able to configure more from the Admin UI rather than just the config files." + +#### Creating a backup +Matthieu explains that the "backup solution is not well integrated into the UI", for example, he "cannot see if backups succeeded" or whether they have been rolled back to via the UI. + +#### Onboarding +It's Matthieu's responsibility to get teams across his organization up and running with GitLab. He explains that whilst many teams might be leveraging GitLab, they are: + +>"..not aware of GitLab's powerful CI or our omnibus install of Mattermost...It would be nice to have a tutorial type walkthrough available when a new user logs in on how to get started with all these features. AutoDevOps may solve some of this, but GitLab has many powerful features wrapped up into it and some [teams] may just think that it is only a Git repo similar to GitHub." + + +He states that there has been: "a sluggishness of others to adapt" and it's "a low-effort adaptation at that." + +### Goals +* To save time. One of the reasons Matthieu moved his company to GitLab was to reduce the effort it took him to manage and configure multiple tools, thus saving him time. He has to balance his day job in addition to managing the company's GitLab installation and onboarding new teams to GitLab. +* To use a platform which is easy to manage. Matthieu isn't a Systems Administrator, and when updating GitLab, creating backups, etc. He would prefer to work within GitLab's UI. Explanations / guided instructions when configuring settings in GitLab's interface would really help Matthieu. He needs reassurance that what he is about to change is + +1. the right setting +2. will provide him with the desired result he wants. + +* Matthieu needs to educate his colleagues about GitLab. Matthieu's colleagues won't adopt GitLab as they're unaware of its capabilities and the positive impact it could have on their work. Matthieu needs support in getting this message across to them. + +--- ## James Mackey -- Medium to large size organisations using CE or EE -- Small organisations using EE +- Medium to large size organizations using CE or EE +- Small organizations using EE -<img src="img/james-mackey.png" width="300px"> +![james-mackey.png](img/james-mackey.png) ### Demographics -- **Age**<br>36 years old -- **Location**<br>US -- **Education**<br>Masters degree in Computer Science -- **Occupation**<br>Full-stack web developer -- **Programming experience**<br>Over 10 years -- **Frequently used programming languages**<br>JavaScript, SQL, Node.js, Java, PHP, Python -- **Hobbies / interests**<br>DevOps, open source, web development, science, automation and electronics. +**Age** + +36 years old + +**Location** + +US + +**Education** + +Masters degree in Computer Science + +**Occupation** + +Full-stack web developer + +**Programming experience** + +Over 10 years + +**Frequently used programming languages** + +JavaScript, SQL, Node.js, Java, PHP, Python + +**Hobbies / interests** + +DevOps, open source, web development, science, automation, and electronics. ### Motivations -James works for a research company which currently hires around 800 staff. He began using GitLab.com back in 2013 for his own open source, hobby projects and loved “the simplicity of installation, administration and use”. After using GitLab for over a year, he began to wonder about using it at work. James explains: +James works for a research company which currently hires around 800 staff. He began using GitLab.com back in 2013 for his own open source, hobby projects and loved "the simplicity of installation, administration and use". After using GitLab for over a year, he began to wonder about using it at work. James explains: -> -“We first installed the CE edition...on a staging server for a PoC and asked a beta team to use it, specifically for the Merge Request features. Soon other teams began asking us to be beta users too, because the team that was already using GitLab was really enjoying it.” -> +>"We first installed the CE edition...on a staging server for a PoC and asked a beta team to use it, specifically for the Merge Request features. Soon other teams began asking us to be beta users too because the team that was already using GitLab was really enjoying it." -James and his colleagues also reviewed competitor products including GitHub Enterprise, but they found it “less innovative and with considerable costs...GitLab had the features we wanted at a much lower cost per head than GitHub”. + +James and his colleagues also reviewed competitor products including GitHub Enterprise, but they found it "less innovative and with considerable costs...GitLab had the features we wanted at a much lower cost per head than GitHub". The company James works for provides employees with a discretionary budget to spend how they want on software, so James and his team decided to upgrade to EE. -James feels partially responsible for his organisation’s decision to start using GitLab. +James feels partially responsible for his organization's decision to start using GitLab. + +>"It's still up to the teams themselves [to decide] which tools to use. We just had a great experience moving our daily development to GitLab, so other teams have followed the path or are thinking about switching." -> -“It's still up to the teams themselves [to decide] which tools to use. We just had a great experience moving our daily development to GitLab, so other teams have followed the path or are thinking about switching.” -> ### Frustrations #### Third Party Integration -Some of GitLab EE’s features are too basic, in particular, issues boards which do not have the level of reporting that James and his team need. Subsequently, they still need to use GitLab EE in conjunction with other tools, such as JIRA. Whilst James feels it isn’t essential for GitLab to meet all his needs (his company are happy for him to use, and pay for, multiple tools), he sometimes isn’t sure what is/isn’t possible with plugins and what level of custom development he and his team will need to do. +Some of GitLab EE's features are too basic, in particular, issues boards which do not have the level of reporting that James and his team need. Subsequently, they still need to use GitLab EE in conjunction with other tools, such as JIRA. Whilst James feels it isn't essential for GitLab to meet all his needs (his company are happy for him to use, and pay for, multiple tools), he sometimes isn't sure what is/isn't possible with plugins and what level of custom development he and his team will need to do. #### UX/UI -James and his team use CI quite heavily for several projects. Whilst they’ve welcomed improvements to the builds and pipelines interface, they still have some difficulty following build process on the different tabs under Pipelines. Some confusion has arisen from not knowing where to find different pieces of information or how to get to the next stages logs from the current stage’s log output screen. They feel more intuitive linking and flow may alleviate the problem. Generally, they feel GitLab’s navigation needs to reviewed and optimised. +James and his team use CI quite heavily for several projects. Whilst they've welcomed improvements to the builds and pipelines interface, they still have some difficulty following build process on the different tabs under Pipelines. Some confusion has arisen from not knowing where to find different pieces of information or how to get to the next stages logs from the current stage's log output screen. They feel more intuitive linking and flow may alleviate the problem. Generally, they feel GitLab's navigation needs to reviewed and optimized. #### Permissions -> -“There is no granular control over user or group permissions. The permissions for a project are too tightly coupled to the permissions for Gitlab CI/build pipelines.” -> +>"There is no granular control over user or group permissions. The permissions for a project are too tightly coupled to the permissions for Gitlab CI/build pipelines." + ### Goals -* To be able to integrate third party tools easily with GitLab EE and to create custom integrations and patches where needed. +* To be able to integrate third-party tools easily with GitLab EE and to create custom integrations and patches where needed. * To use GitLab EE primarily for code hosting, merge requests, continuous integration and issue management. James and his team want to be able to understand and use these particular features easily. * To able to share one instance of GitLab EE with multiple teams across the business. Advanced user management, the ability to separate permissions on different parts of the source code, etc are important to James. @@ -130,36 +255,56 @@ James and his team use CI quite heavily for several projects. Whilst they’ve w ## Karolina Plaskaty - Using GitLab.com for personal/hobby projects - Would like to use GitLab at work -- Working for a medium to large size organisation +- Working within a medium to large size organization -<img src="img/karolina-plaskaty.png" width="300px"> +![karolina-plaskaty.png](img/karolina-plaskaty.png) ### Demographics -- **Age**<br>26 years old -- **Location**<br>UK -- **Education**<br>Self taught -- **Occupation**<br>Junior web-developer -- **Programming experience**<br>6 years -- **Frequently used programming languages**<br>JavaScript and SQL -- **Hobbies / interests**<br>Web development, mobile development, UX, open source, gaming and travel. +**Age** + +26 years old + +**Location** + +UK + +**Education** + +Self taught + +**Occupation** + +Junior web-developer + +**Programming experience** + +6 years + +**Frequently used programming languages** + +JavaScript and SQL + +**Hobbies / interests** + +Web development, mobile development, UX, open source, gaming, and travel. ### Motivations -Karolina has been using GitLab.com for around a year. She roughly spends 8 hours every week programming, of that, 2 hours is spent contributing to open source projects. Karolina contributes to open source projects to gain programming experience and to give back to the community. She likes GitLab.com for its free private repositories and range of features which provide her with everything she needs for her personal projects. Karolina is also a massive fan of GitLab’s values and the fact that it isn’t a “behemoth of a company”. She explains that “displaying every single thing (doc, culture, assumptions, development...) in the open gives me greater confidence to choose Gitlab personally and to recommend it at work.” She’s also an avid reader of GitLab’s blog. +Karolina has been using GitLab.com for around a year. She roughly spends 8 hours every week programming, of that, 2 hours is spent contributing to open source projects. Karolina contributes to open source projects to gain programming experience and to give back to the community. She likes GitLab.com for its free private repositories and range of features which provide her with everything she needs for her personal projects. Karolina is also a massive fan of GitLab's values and the fact that it isn't a "behemoth of a company". She explains that "displaying every single thing (doc, culture, assumptions, development...) in the open gives me greater confidence to choose Gitlab personally and to recommend it at work." She's also an avid reader of GitLab's blog. -Karolina works for a software development company which currently hires around 500 people. Karolina would love to use GitLab at work but the company has used GitHub Enterprise for a number of years. She describes management at her company as “old fashioned” and explains that it’s “less of a technical issue and more of a cultural issue” to convince upper management to move to GitLab. Karolina is also relatively new to the company so she’s apprehensive about pushing too hard to change version control platforms. +Karolina works for a software development company which currently hires around 500 people. Karolina would love to use GitLab at work but the company has used GitHub Enterprise for a number of years. She describes management at her company as "old fashioned" and explains that it's "less of a technical issue and more of a cultural issue" to convince upper management to move to GitLab. Karolina is also relatively new to the company so she's apprehensive about pushing too hard to change version control platforms. ### Frustrations #### Unable to use GitLab at work -Karolina wants to use GitLab at work but isn’t sure how to approach the subject with management. In her current role, she doesn’t feel that she has the authority to request GitLab. +Karolina wants to use GitLab at work but isn't sure how to approach the subject with management. In her current role, she doesn't feel that she has the authority to request GitLab. #### Performance -GitLab.com is frequently slow and unavailable. Karolina has also heard that GitLab is a “memory hog” which has deterred her from running GitLab on her own machine for just hobby / personal projects. +GitLab.com is frequently slow and unavailable. Karolina has also heard that GitLab is a "memory hog" which has deterred her from running GitLab on her own machine for just hobby / personal projects. #### UX/UI -Karolina has an interest in UX and therefore has strong opinions about how GitLab should look and feel. She feels the interface is cluttered, “it has too many links/buttons” and the navigation “feels a bit weird sometimes. I get lost if I don’t pay attention.” As Karolina also enjoys contributing to open-source projects, it’s important to her that GitLab is well designed for public repositories, she doesn’t feel that GitLab currently achieves this. +Karolina has an interest in UX and therefore has strong opinions about how GitLab should look and feel. She feels the interface is cluttered, "it has too many links/buttons" and the navigation "feels a bit weird sometimes. I get lost if I don't pay attention." As Karolina also enjoys contributing to open-source projects, it's important to her that GitLab is well designed for public repositories, she doesn't feel that GitLab currently achieves this. ### Goals * To develop her programming experience and to learn from other developers. * To contribute to both her own and other open source projects. -* To use a fast and intuitive version control platform. +* To use a fast and intuitive version control platform.
\ No newline at end of file diff --git a/doc/install/installation.md b/doc/install/installation.md index 56888b05609..67b89d608cc 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -133,9 +133,9 @@ Remove the old Ruby 1.8 if present: Download Ruby and compile it: mkdir /tmp/ruby && cd /tmp/ruby - curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.5.tar.gz - echo '3247e217d6745c27ef23bdc77b6abdb4b57a118f ruby-2.3.5.tar.gz' | shasum -c - && tar xzf ruby-2.3.5.tar.gz - cd ruby-2.3.5 + curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.6.tar.gz + echo '4e6a0f828819e15d274ae58485585fc8b7caace0 ruby-2.3.6.tar.gz' | shasum -c - && tar xzf ruby-2.3.6.tar.gz + cd ruby-2.3.6 ./configure --disable-install-rdoc make sudo make install @@ -367,7 +367,7 @@ sudo usermod -aG redis git # Enable packfile bitmaps sudo -u git -H git config --global repack.writeBitmaps true - + # Enable push options sudo -u git -H git config --global receive.advertisePushOptions true diff --git a/doc/update/10.1-to-10.2.md b/doc/update/10.1-to-10.2.md index 9e0d8f79522..632e8befa74 100644 --- a/doc/update/10.1-to-10.2.md +++ b/doc/update/10.1-to-10.2.md @@ -339,11 +339,11 @@ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production If all items are green, then congratulations, the upgrade is complete! -## Things went south? Revert to previous version (10.0) +## Things went south? Revert to previous version (10.1) ### 1. Revert the code to the previous version -Follow the [upgrade guide from 9.5 to 10.0](9.5-to-10.0.md), except for the +Follow the [upgrade guide from 10.0 to 10.1](10.0-to-10.1.md), except for the database migration (the backup is already migrated to the previous version). ### 2. Restore from the backup diff --git a/doc/update/10.2-to-10.3.md b/doc/update/10.2-to-10.3.md index 07f9ee965f0..d6e2db8a353 100644 --- a/doc/update/10.2-to-10.3.md +++ b/doc/update/10.2-to-10.3.md @@ -339,11 +339,11 @@ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production If all items are green, then congratulations, the upgrade is complete! -## Things went south? Revert to previous version (10.0) +## Things went south? Revert to previous version (10.2) ### 1. Revert the code to the previous version -Follow the [upgrade guide from 9.5 to 10.0](9.5-to-10.0.md), except for the +Follow the [upgrade guide from 10.1 to 10.2](10.1-to-10.2.md), except for the database migration (the backup is already migrated to the previous version). ### 2. Restore from the backup diff --git a/doc/update/10.3-to-10.4.md b/doc/update/10.3-to-10.4.md new file mode 100644 index 00000000000..850cb3103f4 --- /dev/null +++ b/doc/update/10.3-to-10.4.md @@ -0,0 +1,360 @@ +--- +comments: false +--- + +# From 10.3 to 10.4 + +Make sure you view this update guide from the tag (version) of GitLab you would +like to install. In most cases this should be the highest numbered production +tag (without rc in it). You can select the tag in the version dropdown at the +top left corner of GitLab (below the menu bar). + +If the highest number stable branch is unclear please check the +[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation +guide links by version. + +### 1. Stop server + +```bash +sudo service gitlab stop +``` + +### 2. Backup + +```bash +cd /home/git/gitlab + +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production +``` + +### 3. Update Ruby + +NOTE: GitLab 9.0 and higher only support Ruby 2.3.x and dropped support for Ruby 2.1.x. Be +sure to upgrade your interpreter if necessary. + +You can check which version you are running with `ruby -v`. + +Download and compile Ruby: + +```bash +mkdir /tmp/ruby && cd /tmp/ruby +curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.6.tar.gz +echo '4e6a0f828819e15d274ae58485585fc8b7caace0 ruby-2.3.6.tar.gz' | shasum -c - && tar xzf ruby-2.3.6.tar.gz +cd ruby-2.3.6 +./configure --disable-install-rdoc +make +sudo make install +``` + +Install Bundler: + +```bash +sudo gem install bundler --no-ri --no-rdoc +``` + +### 4. Update Node + +GitLab now runs [webpack](http://webpack.js.org) to compile frontend assets and +it has a minimum requirement of node v4.3.0. + +You can check which version you are running with `node -v`. If you are running +a version older than `v4.3.0` you will need to update to a newer version. You +can find instructions to install from community maintained packages or compile +from source at the nodejs.org website. + +<https://nodejs.org/en/download/> + + +Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage +JavaScript dependencies. + +```bash +curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - +echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list +sudo apt-get update +sudo apt-get install yarn +``` + +More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install). + +### 5. Update Go + +NOTE: GitLab 9.2 and higher only supports Go 1.8.3 and dropped support for Go +1.5.x through 1.7.x. Be sure to upgrade your installation if necessary. + +You can check which version you are running with `go version`. + +Download and install Go: + +```bash +# Remove former Go installation folder +sudo rm -rf /usr/local/go + +curl --remote-name --progress https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz +echo '1862f4c3d3907e59b04a757cfda0ea7aa9ef39274af99a784f5be843c80c6772 go1.8.3.linux-amd64.tar.gz' | shasum -a256 -c - && \ + sudo tar -C /usr/local -xzf go1.8.3.linux-amd64.tar.gz +sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/ +rm go1.8.3.linux-amd64.tar.gz +``` + +### 6. Get latest code + +```bash +cd /home/git/gitlab + +sudo -u git -H git fetch --all +sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically +sudo -u git -H git checkout -- locale +``` + +For GitLab Community Edition: + +```bash +cd /home/git/gitlab + +sudo -u git -H git checkout 10-4-stable +``` + +OR + +For GitLab Enterprise Edition: + +```bash +cd /home/git/gitlab + +sudo -u git -H git checkout 10-4-stable-ee +``` + +### 7. Update gitlab-shell + +```bash +cd /home/git/gitlab-shell + +sudo -u git -H git fetch --all --tags +sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION) +sudo -u git -H bin/compile +``` + +### 8. Update gitlab-workhorse + +Install and compile gitlab-workhorse. GitLab-Workhorse uses +[GNU Make](https://www.gnu.org/software/make/). +If you are not using Linux you may have to run `gmake` instead of +`make` below. + +```bash +cd /home/git/gitlab-workhorse + +sudo -u git -H git fetch --all --tags +sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION) +sudo -u git -H make +``` + +### 9. Update Gitaly + +#### New Gitaly configuration options required + +In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`. + +```shell +echo ' +[gitaly-ruby] +dir = "/home/git/gitaly/ruby" + +[gitlab-shell] +dir = "/home/git/gitlab-shell" +' | sudo -u git tee -a /home/git/gitaly/config.toml +``` + +#### Check Gitaly configuration + +Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly +configuration file may contain syntax errors. The block name +`[[storages]]`, which may occur more than once in your `config.toml` +file, should be `[[storage]]` instead. + +```shell +sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml +``` + +#### Compile Gitaly + +```shell +cd /home/git/gitaly +sudo -u git -H git fetch --all --tags +sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION) +sudo -u git -H make +``` + +### 10. Update MySQL permissions + +If you are using MySQL you need to grant the GitLab user the necessary +permissions on the database: + +```bash +mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';" +``` + +If you use MySQL with replication, or just have MySQL configured with binary logging, +you will need to also run the following on all of your MySQL servers: + +```bash +mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;" +``` + +You can make this setting permanent by adding it to your `my.cnf`: + +``` +log_bin_trust_function_creators=1 +``` + +### 11. Update configuration files + +#### New configuration options for `gitlab.yml` + +There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: + +```sh +cd /home/git/gitlab + +git diff origin/10-3-stable:config/gitlab.yml.example origin/10-4-stable:config/gitlab.yml.example +``` + +#### Nginx configuration + +Ensure you're still up-to-date with the latest NGINX configuration changes: + +```sh +cd /home/git/gitlab + +# For HTTPS configurations +git diff origin/10-3-stable:lib/support/nginx/gitlab-ssl origin/10-4-stable:lib/support/nginx/gitlab-ssl + +# For HTTP configurations +git diff origin/10-3-stable:lib/support/nginx/gitlab origin/10-4-stable:lib/support/nginx/gitlab +``` + +If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx +configuration as GitLab application no longer handles setting it. + +If you are using Apache instead of NGINX please see the updated [Apache templates]. +Also note that because Apache does not support upstreams behind Unix sockets you +will need to let gitlab-workhorse listen on a TCP port. You can do this +via [/etc/default/gitlab]. + +[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache +[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-4-stable/lib/support/init.d/gitlab.default.example#L38 + +#### SMTP configuration + +If you're installing from source and use SMTP to deliver mail, you will need to add the following line +to config/initializers/smtp_settings.rb: + +```ruby +ActionMailer::Base.delivery_method = :smtp +``` + +See [smtp_settings.rb.sample] as an example. + +[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-4-stable/config/initializers/smtp_settings.rb.sample#L13 + +#### Init script + +There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`: + +```sh +cd /home/git/gitlab + +git diff origin/10-3-stable:lib/support/init.d/gitlab.default.example origin/10-4-stable:lib/support/init.d/gitlab.default.example +``` + +Ensure you're still up-to-date with the latest init script changes: + +```bash +cd /home/git/gitlab + +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab +``` + +For Ubuntu 16.04.1 LTS: + +```bash +sudo systemctl daemon-reload +``` + +### 12. Install libs, migrations, etc. + +```bash +cd /home/git/gitlab + +# MySQL installations (note: the line below states '--without postgres') +sudo -u git -H bundle install --without postgres development test --deployment + +# PostgreSQL installations (note: the line below states '--without mysql') +sudo -u git -H bundle install --without mysql development test --deployment + +# Optional: clean up old gems +sudo -u git -H bundle clean + +# Run database migrations +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production + +# Compile GetText PO files + +sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production + +# Update node dependencies and recompile assets +sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production + +# Clean up cache +sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production +``` + +**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md). + +### 13. Start application + +```bash +sudo service gitlab start +sudo service nginx restart +``` + +### 14. Check application status + +Check if GitLab and its environment are configured correctly: + +```bash +cd /home/git/gitlab + +sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production +``` + +To make sure you didn't miss anything run a more thorough check: + +```bash +cd /home/git/gitlab + +sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production +``` + +If all items are green, then congratulations, the upgrade is complete! + +## Things went south? Revert to previous version (10.3) + +### 1. Revert the code to the previous version + +Follow the [upgrade guide from 10.2 to 10.3](10.2-to-10.3.md), except for the +database migration (the backup is already migrated to the previous version). + +### 2. Restore from the backup + +```bash +cd /home/git/gitlab + +sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production +``` + +If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-4-stable/config/gitlab.yml.example +[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-4-stable/lib/support/init.d/gitlab.default.example diff --git a/doc/user/group/img/transfer_project_to_other_group.png b/doc/user/group/img/transfer_project_to_other_group.png Binary files differdeleted file mode 100644 index 042c002f83f..00000000000 --- a/doc/user/group/img/transfer_project_to_other_group.png +++ /dev/null diff --git a/doc/user/group/index.md b/doc/user/group/index.md index 1733017cbc0..7f77a33aadc 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -65,7 +65,7 @@ together in a single list view. > **Notes:** - For a list of words that are not allowed to be used as group names see the - [reserved names][reserved]. + [reserved names](../reserved_names.md). You can create a group in GitLab from: @@ -97,7 +97,7 @@ Add members to a group by navigating to the group's dashboard, and clicking **Me ![add members to group](img/add_new_members.png) -Select the [permission level][permissions] and add the new member. You can also set the expiring +Select the [permission level](../permissions.md#permissions) and add the new member. You can also set the expiring date for that user, from which they will no longer have access to your group. One of the benefits of putting multiple projects in one group is that you can @@ -152,16 +152,9 @@ There are two different ways to add a new project to a group: ![Select group](img/select_group_dropdown.png) -## Transfer an existing project into a group +## Transfer projects into groups -You can transfer an existing project into a group as long as you have at least **Master** [permissions][permissions] to that group -and if you are an **Owner** of the project. - -![Transfer a project to a new namespace](img/transfer_project_to_other_group.png) - -Find this option under your project's settings. - -GitLab administrators can use the admin interface to move any project to any namespace if needed. +Learn how to [transfer a project into a group](../project/index.md#transfer-an-existing-project-into-a-group). ## Sharing a project with a group @@ -190,24 +183,29 @@ access further configurations for your group. #### Changing a group's path -> **Note:** If you want to retain ownership over the original namespace and -protect the URL redirects, then instead of changing a group's path or renaming a -username, you can create a new group and transfer projects to it. +Changing a group's path can have unintended side effects. Read +[how redirects will behave](../project/index.md#redirects-when-changing-repository-paths) +before proceeding. -Changing a group's path can have unintended side effects. +If you are vacating the path so it can be claimed by another group or user, +you may need to rename the group name as well since both names and paths must +be unique. -* Existing web URLs for the group and anything under it (i.e. projects) will -redirect to the new URLs. -* Existing Git remote URLs for projects under the group will redirect to the new remote URL, and they -will show a warning with the new remote URL. -* The redirect to the new URL is permanent, that implies the original namespace -can't be claimed again by any group or user. -* If you are vacating the path so it can be claimed by another group or user, -you may need to rename the group name as well since both names and paths must be -unique +To change your group path: -> It is currently not possible to rename a namespace if it contains a -project with container registry tags, because the project cannot be moved. +1. Navigate to your group's **Settings > General**. +1. Enter a new name under "Group path". +1. Hit **Save group**. + +CAUTION: **Caution:** +It is currently not possible to rename a namespace if it contains a +project with [Container Registry](../project/container_registry.md) tags, +because the project cannot be moved. + +TIP: **TIP:** +If you want to retain ownership over the original namespace and +protect the URL redirects, then instead of changing a group's path or renaming a +username, you can create a new group and transfer projects to it. #### Enforce 2FA to group members @@ -246,11 +244,7 @@ Learn more about [Member Lock](https://docs.gitlab.com/ee/user/group/index.html# - **Projects**: view all projects within that group, add members to each project, access each project's settings, and remove any project from the same screen. - **Webhooks**: configure [webhooks](../project/integrations/webhooks.md) -and [push rules](https://docs.gitlab.com/ee/push_rules/push_rules.html#push-rules) to your group (Push Rules is available in [GitLab Enteprise Edition Starter][ee].) +and [push rules](https://docs.gitlab.com/ee/push_rules/push_rules.html#push-rules) to your group (Push Rules is available in [GitLab Enteprise Edition Starter](https://about.gitlab.com/products/).) - **Audit Events**: view [Audit Events](https://docs.gitlab.com/ee/administration/audit_events.html#audit-events) for the group (GitLab admins only, available in [GitLab Enterprise Edition Starter][ee]). - **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group - -[permissions]: ../permissions.md#permissions -[ee]: https://about.gitlab.com/products/ -[reserved]: ../reserved_names.md diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index dae4cbe170b..ab16f8d14c1 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -8,51 +8,6 @@ experience according to the best approach to their cases. There are several ways to sign into your GitLab account. See the [authentication topic](../../topics/authentication/index.md) for more details. -### Why do I keep getting signed out? - -When signing in to the main GitLab application, a `_gitlab_session` cookie is -set. `_gitlab_session` is cleared client-side when you close your browser -and expires after "Application settings -> Session duration (minutes)"/`session_expire_delay` -(defaults to `10080` minutes = 7 days). - -When signing in to the main GitLab application, you can also check the -"Remember me" option which sets the `remember_user_token` -cookie (via [`devise`](https://github.com/plataformatec/devise)). -`remember_user_token` expires after -`config/initializers/devise.rb` -> `config.remember_for` (defaults to 2 weeks). - -When the `_gitlab_session` expires or isn't available, GitLab uses the `remember_user_token` -to get you a new `_gitlab_session` and keep you signed in through browser restarts. - -After your `remember_user_token` expires and your `_gitlab_session` is cleared/expired, -you will be asked to sign in again to verify your identity (which is for security reasons). - -## Username - -Your `username` is a unique [`namespace`](../group/index.md#namespaces) -related to your user ID. - -### Changing your username - -You can change your `username` from your -[profile settings](#profile-settings). - -> **Note:** If you want to retain ownership over the original namespace and -protect the URL redirects, then instead of changing your username, you can -create a new group and transfer projects to it. -Alternatively, you can follow [this detailed procedure from the GitLab Team Handbook](https://about.gitlab.com/handbook/tools-and-tips/#how-to-change-your-username-at-gitlabcom). - -Changing your username can have unintended side effects. - -* Existing web URLs for the user and anything under it (i.e. projects) will -redirect to the new URLs. -* Existing Git remote URLs for projects under the user will redirect to the new remote URL. Git responses -will show a warning with the new remote URL. -* The redirect to the new URL is permanent, that implies the original namespace can't be claimed again by any group or user. - -> It is currently not possible to rename a namespace if it contains a -project with container registry tags, because the project cannot be moved. - ## User profile Your profile is available from the up-right corner menu bar (user's avatar) > **Profile**, @@ -84,4 +39,51 @@ From there, you can: - Manage [SSH keys](../../ssh/README.md#ssh) to access your account via SSH - Manage your [preferences](preferences.md#syntax-highlighting-theme) to customize your own GitLab experience -- Acess your audit log, a security log of important events involving your account +- Access your audit log, a security log of important events involving your account + +## Changing your username + +Your `username` is a unique [`namespace`](../group/index.md#namespaces) +related to your user ID. Changing it can have unintended side effects, read +[how redirects will behave](../project/index.md#redirects-when-changing-repository-paths) +before proceeding. + +To change your `username`: + +1. Navigate to your [profile's](#profile-settings) **Settings > Account**. +1. Enter a new username under "Change username". +1. Hit **Update username**. + +CAUTION: **Caution:** +It is currently not possible to change your username if it contains a +project with [Container Registry](../project/container_registry.md) tags, +because the project cannot be moved. + +TIP: **Tip:** +If you want to retain ownership over the original namespace and +protect the URL redirects, then instead of changing a group's path or renaming a +username, you can create a new group and transfer projects to it. +Alternatively, you can follow [this detailed procedure from the GitLab Team Handbook](https://about.gitlab.com/handbook/tools-and-tips/#how-to-change-your-username-at-gitlabcom) +which also covers the case where you have projects hosted with +[GitLab Pages](../project/pages/index.md). + +## Troubleshooting + +### Why do I keep getting signed out? + +When signing in to the main GitLab application, a `_gitlab_session` cookie is +set. `_gitlab_session` is cleared client-side when you close your browser +and expires after "Application settings -> Session duration (minutes)"/`session_expire_delay` +(defaults to `10080` minutes = 7 days). + +When signing in to the main GitLab application, you can also check the +"Remember me" option which sets the `remember_user_token` +cookie (via [`devise`](https://github.com/plataformatec/devise)). +`remember_user_token` expires after +`config/initializers/devise.rb` -> `config.remember_for` (defaults to 2 weeks). + +When the `_gitlab_session` expires or isn't available, GitLab uses the `remember_user_token` +to get you a new `_gitlab_session` and keep you signed in through browser restarts. + +After your `remember_user_token` expires and your `_gitlab_session` is cleared/expired, +you will be asked to sign in again to verify your identity (which is for security reasons). diff --git a/doc/user/project/index.md b/doc/user/project/index.md index 5d91aef5735..4c772c62f8d 100644 --- a/doc/user/project/index.md +++ b/doc/user/project/index.md @@ -93,6 +93,9 @@ from your fork to the upstream project ## Project settings +Set the project's visibility level and the access levels to its various pages +and perform actions like archiving, renaming or transferring a project. + Read through the documentation on [project settings](settings/index.md). ## Import or export a project @@ -116,3 +119,32 @@ when a project is part of a group (under a [group namespace](../group/index.md#namespaces)). If you choose to leave a project you will no longer be a project member, therefore, unable to contribute. + +## Redirects when changing repository paths + +When a repository path changes, it is essential to smoothly transition from the +old location to the new one. GitLab provides two kinds of redirects: the web UI +and Git push/pull redirects. + +Depending on the situation, different things apply. + +When [transferring a project](settings/index.md#transferring-an-existing-project-into-another-namespace), +or [renaming a user](../profile/index.md#changing-your-username) or +[changing a group path](../group/index.md#changing-a-group-s-path): + +- **The redirect to the new URL is permanent**, which means that the original + namespace can't be claimed again by any group or user. +- Existing web URLs for the namespace and anything under it (e.g., projects) will + redirect to the new URLs. +- Starting with GitLab 10.3, existing Git remote URLs for projects under the + namespace will redirect to the new remote URL. Every time you push/pull to a + repository that has changed its location, a warning message to update + your remote will be displayed instead of rejecting your action. + This means that any automation scripts, or Git clients will continue to + work after a rename, making any transition a lot smoother. + To avoid pulling from or pushing to an entirely incorrect repository, the old + path will be reserved. + +When [renaming-a-repository](settings/index.md#renaming-a-repository), the same +things apply, except for the Git push/pull actions which will be rejected with a +warning message to change to the new remote URL. diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index 2b6fde1e2a5..f01fa5b1860 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -1,11 +1,20 @@ # Project settings +NOTE: **Note:** +Only project Masters and Admin users have the [permissions] to access a project +settings. + You can adjust your [project](../index.md) settings by navigating to your project's homepage and clicking **Settings**. ## General settings -Adjust your project's path and name, description, avatar, [default branch](../repository/branches/index.md#default-branch), and tags: +Under a project's general settings you can find everything concerning the +functionality of a project. + +### General project settings + +Adjust your project's name, description, avatar, [default branch](../repository/branches/index.md#default-branch), and tags: ![general project settings](img/general_settings.png) @@ -45,14 +54,64 @@ Here you can run housekeeping, archive, rename, transfer, or remove a project. #### Archiving a project ->**Note:** Only Project Owners and Admin users have the permission to archive a project +NOTE: **Note:** +Only project Owners and Admin users have the [permissions] to archive a project. + +An archived project will be hidden by default in the project listings. + +1. Navigate to your project's **Settings > General > Advanced settings**. +1. Under "Archive project", hit the **Archive project** button. +1. Confirm the action when asked to. + +An archived project can be fully restored and will therefore retain its +repository and all associated resources whilst in an archived state. + +#### Renaming a repository + +NOTE: **Note:** +Only project Masters and Admin users have the [permissions] to rename a +repository. Not to be confused with a project's name where it can also be +changed from the [general project settings](#general-project-settings). + +A project's repository name defines its URL (the one you use to access the +project via a browser) and its place on the file disk where GitLab is installed. + +To rename a repository: + +1. Navigate to your project's **Settings > General > Advanced settings**. +1. Under "Rename repository", change the "Path" to your liking. +1. Hit **Rename project**. + +Remember that this can have unintended side effects since everyone with the +old URL will not be able to push or pull. Read more about what happens with the +[redirects when renaming repositories](../index.md#redirects-when-changing-repository-paths). + +#### Transferring an existing project into another namespace + +NOTE: **Note:** +Only project Owners and Admin users have the [permissions] to transfer a project. + +You can transfer an existing project into a [group](../../group/index.md) if: + +1. you have at least **Master** [permissions] to that group +1. you are an **Owner** of the project. + +Similarly, if you are an owner of a group, you can transfer any of its projects +under your own user. -It's possible to mark a project as archived via the Project Settings. An archived project will be hidden by default in the project listings. +To transfer a project: -An archived project can be fully restored and will therefore retain it's repository and all associated resources whilst in an archived state. +1. Navigate to your project's **Settings > General > Advanced settings**. +1. Under "Transfer project", choose the namespace you want to transfer the + project to. +1. Confirm the transfer by typing the project's path as instructed. -#### Renaming a project +Once done, you will be taken to the new project's namespace. At this point, +read what happens with the +[redirects from the old project to the new one](../index.md#redirects-when-changing-repository-paths). ->**Note:** Only Project Owners and Admin users have the permission to rename a project +NOTE: **Note:** +GitLab administrators can use the admin interface to move any project to any +namespace if needed. -It's possible to rename a project from "Rename repository" or "Transfer project" sections. When doing so, you will need to update your local repositories to point to the new location, otherwise Git operations will be rejected. +[permissions]: ../../permissions.md##project-members-permissions diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb index 6ea132fc5bf..877cebf6786 100644 --- a/lib/gitlab/metrics/influx_db.rb +++ b/lib/gitlab/metrics/influx_db.rb @@ -38,6 +38,7 @@ module Gitlab # This is memoized since this method is called for every instrumented # method. Loading data from an external cache on every method call slows # things down too much. + # in milliseconds @method_call_threshold ||= settings[:method_call_threshold] end diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index 9112164f22e..329b07af5db 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -35,8 +35,8 @@ module Gitlab @transaction = transaction @name = name @labels = { module: @module_name, method: @method_name } - @real_time = 0 - @cpu_time = 0 + @real_time = 0.0 + @cpu_time = 0.0 @call_count = 0 end @@ -54,7 +54,7 @@ module Gitlab @call_count += 1 if call_measurement_enabled? && above_threshold? - self.class.call_duration_histogram.observe(@transaction.labels.merge(labels), real_time / 1000.0) + self.class.call_duration_histogram.observe(@transaction.labels.merge(labels), real_time) end retval @@ -65,8 +65,8 @@ module Gitlab Metric.new( Instrumentation.series, { - duration: real_time, - cpu_duration: cpu_time, + duration: real_time.in_milliseconds.to_i, + cpu_duration: cpu_time.in_milliseconds.to_i, call_count: call_count }, method: @name @@ -76,7 +76,7 @@ module Gitlab # Returns true if the total runtime of this method exceeds the method call # threshold. def above_threshold? - real_time >= Metrics.method_call_threshold + real_time.in_milliseconds >= Metrics.method_call_threshold end def call_measurement_enabled? diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb index b68800417a2..4e1ea62351f 100644 --- a/lib/gitlab/metrics/samplers/ruby_sampler.rb +++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb @@ -52,7 +52,7 @@ module Gitlab metrics[:memory_usage].set(labels, System.memory_usage) metrics[:file_descriptors].set(labels, System.file_descriptor_count) - metrics[:sampler_duration].observe(labels.merge(worker_label), (System.monotonic_time - start_time) / 1000.0) + metrics[:sampler_duration].observe(labels.merge(worker_label), System.monotonic_time - start_time) ensure GC::Profiler.clear end diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb index efd3c9daf79..250897a79c2 100644 --- a/lib/gitlab/metrics/subscribers/rails_cache.rb +++ b/lib/gitlab/metrics/subscribers/rails_cache.rb @@ -66,7 +66,7 @@ module Gitlab :gitlab_cache_operation_duration_seconds, 'Cache access time', Transaction::BASE_LABELS.merge({ action: nil }), - [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] + [0.001, 0.01, 0.1, 1, 10] ) end diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb index c2cbd3c16a1..e60e245cf89 100644 --- a/lib/gitlab/metrics/system.rb +++ b/lib/gitlab/metrics/system.rb @@ -35,27 +35,27 @@ module Gitlab if Process.const_defined?(:CLOCK_THREAD_CPUTIME_ID) def self.cpu_time Process - .clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond) + .clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_second) end else def self.cpu_time Process - .clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond) + .clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :float_second) end end # Returns the current real time in a given precision. # - # Returns the time as a Fixnum. - def self.real_time(precision = :millisecond) + # Returns the time as a Float for precision = :float_second. + def self.real_time(precision = :float_second) Process.clock_gettime(Process::CLOCK_REALTIME, precision) end - # Returns the current monotonic clock time in a given precision. + # Returns the current monotonic clock time as seconds with microseconds precision. # - # Returns the time as a Fixnum. - def self.monotonic_time(precision = :millisecond) - Process.clock_gettime(Process::CLOCK_MONOTONIC, precision) + # Returns the time as a Float. + def self.monotonic_time + Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) end end end diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index ee3afc5ffdb..e7975c023a9 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -35,6 +35,10 @@ module Gitlab @finished_at ? (@finished_at - @started_at) : 0.0 end + def duration_milliseconds + duration.in_milliseconds.to_i + end + def allocated_memory @memory_after - @memory_before end @@ -50,7 +54,7 @@ module Gitlab @memory_after = System.memory_usage @finished_at = System.monotonic_time - self.class.metric_transaction_duration_seconds.observe(labels, duration * 1000) + self.class.metric_transaction_duration_seconds.observe(labels, duration) self.class.metric_transaction_allocated_memory_bytes.observe(labels, allocated_memory * 1024.0) Thread.current[THREAD_KEY] = nil @@ -97,7 +101,7 @@ module Gitlab end def track_self - values = { duration: duration, allocated_memory: allocated_memory } + values = { duration: duration_milliseconds, allocated_memory: allocated_memory } @values.each do |name, value| values[name] = value diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index fef9d3e31d4..7037e2e61cc 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -27,10 +27,17 @@ module Gitlab # It allows us to search only for projects user has access to attr_reader :limit_projects - def initialize(current_user, limit_projects, query) + # Whether a custom filter is used to restrict scope of projects. + # If the default filter (which lists all projects user has access to) + # is used, we can skip it when filtering merge requests and optimize the + # query + attr_reader :default_project_filter + + def initialize(current_user, limit_projects, query, default_project_filter: false) @current_user = current_user @limit_projects = limit_projects || Project.all @query = query + @default_project_filter = default_project_filter end def objects(scope, page = nil) @@ -94,7 +101,11 @@ module Gitlab end def merge_requests - merge_requests = MergeRequestsFinder.new(current_user).execute.in_projects(project_ids_relation) + merge_requests = MergeRequestsFinder.new(current_user).execute + unless default_project_filter + merge_requests = merge_requests.in_projects(project_ids_relation) + end + merge_requests = if query =~ /[#!](\d+)\z/ merge_requests.where(iid: $1) diff --git a/package.json b/package.json index a5bf2309a0f..d80e25e1ac6 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "vue": "^2.5.8", "vue-loader": "^13.5.0", "vue-resource": "^1.3.4", + "vue-router": "^3.0.1", "vue-template-compiler": "^2.5.8", "vuex": "^3.0.1", "webpack": "^3.5.5", @@ -87,7 +88,7 @@ "worker-loader": "^1.1.0" }, "devDependencies": { - "@gitlab-org/gitlab-svgs": "^1.3.0", + "@gitlab-org/gitlab-svgs": "^1.4.0", "babel-plugin-istanbul": "^4.1.5", "eslint": "^3.10.1", "eslint-config-airbnb-base": "^10.0.1", diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb index 59cd147e055..8f999511d58 100644 --- a/qa/qa/git/repository.rb +++ b/qa/qa/git/repository.rb @@ -23,7 +23,7 @@ module QA def password=(pass) @password = pass - @uri.password = CGI.escape(pass) + @uri.password = CGI.escape(pass).gsub('+', '%20') end def use_default_credentials diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 58116e6e0fe..45c424af8c4 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -98,6 +98,14 @@ describe Projects::MergeRequestsController do expect(response).to match_response_schema('entities/merge_request_widget') end end + + context 'when no serialiser was passed' do + it 'renders widget MR entity as json' do + go(serializer: nil, format: :json) + + expect(response).to match_response_schema('entities/merge_request_widget') + end + end end describe "as diff" do diff --git a/spec/features/projects/ref_switcher_spec.rb b/spec/features/projects/ref_switcher_spec.rb deleted file mode 100644 index 33ccbc1a29f..00000000000 --- a/spec/features/projects/ref_switcher_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'rails_helper' - -feature 'Ref switcher', :js do - let(:user) { create(:user) } - let(:project) { create(:project, :public, :repository) } - - before do - project.team << [user, :master] - set_cookie('new_repo', 'true') - sign_in(user) - visit project_tree_path(project, 'master') - end - - it 'allow user to change ref by enter key' do - click_button 'master' - wait_for_requests - - page.within '.project-refs-form' do - input = find('input[type="search"]') - input.set 'binary' - wait_for_requests - - expect(find('.dropdown-content ul')).to have_selector('li', count: 7) - - page.within '.dropdown-content ul' do - input.native.send_keys :enter - end - end - - expect(page).to have_title 'add-pdf-text-binary' - end - - it "user selects ref with special characters" do - click_button 'master' - wait_for_requests - - page.within '.project-refs-form' do - page.fill_in 'Search branches and tags', with: "'test'" - click_link "'test'" - end - - expect(page).to have_title "'test'" - end - - context "create branch" do - let(:input) { find('.js-new-branch-name') } - - before do - click_button 'master' - wait_for_requests - - page.within '.project-refs-form' do - find(".dropdown-footer-list a").click - end - end - - it "shows error message for the invalid branch name" do - input.set 'foo bar' - click_button('Create') - wait_for_requests - expect(page).to have_content 'Branch name is invalid' - end - - it "should create new branch properly" do - input.set 'new-branch-name' - click_button('Create') - wait_for_requests - expect(find('.js-project-refs-dropdown')).to have_content 'new-branch-name' - end - - it "should create new branch by Enter key" do - input.set 'new-branch-name-2' - input.native.send_keys :enter - wait_for_requests - expect(find('.js-project-refs-dropdown')).to have_content 'new-branch-name-2' - end - end -end diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb index 8f06328962e..3f6d16c8acf 100644 --- a/spec/features/projects/tree/create_directory_spec.rb +++ b/spec/features/projects/tree/create_directory_spec.rb @@ -13,6 +13,14 @@ feature 'Multi-file editor new directory', :js do visit project_tree_path(project, :master) wait_for_requests + + click_link('Multi Edit') + + wait_for_requests + end + + after do + set_cookie('new_repo', 'false') end it 'creates directory in current directory' do @@ -21,17 +29,29 @@ feature 'Multi-file editor new directory', :js do click_link('New directory') page.within('.modal') do - find('.form-control').set('foldername') + find('.form-control').set('folder name') click_button('Create directory') end + find('.add-to-tree').click + + click_link('New file') + + page.within('.modal-dialog') do + find('.form-control').set('file name') + + click_button('Create file') + end + + wait_for_requests + find('.multi-file-commit-panel-collapse-btn').click - fill_in('commit-message', with: 'commit message') + fill_in('commit-message', with: 'commit message ide') click_button('Commit') - expect(page).to have_selector('td', text: 'commit message') + expect(page).to have_content('folder name') end end diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb index bdebc12ef47..ba71eef07f4 100644 --- a/spec/features/projects/tree/create_file_spec.rb +++ b/spec/features/projects/tree/create_file_spec.rb @@ -13,6 +13,14 @@ feature 'Multi-file editor new file', :js do visit project_tree_path(project, :master) wait_for_requests + + click_link('Multi Edit') + + wait_for_requests + end + + after do + set_cookie('new_repo', 'false') end it 'creates file in current directory' do @@ -21,17 +29,19 @@ feature 'Multi-file editor new file', :js do click_link('New file') page.within('.modal') do - find('.form-control').set('filename') + find('.form-control').set('file name') click_button('Create file') end + wait_for_requests + find('.multi-file-commit-panel-collapse-btn').click - fill_in('commit-message', with: 'commit message') + fill_in('commit-message', with: 'commit message ide') click_button('Commit') - expect(page).to have_selector('td', text: 'commit message') + expect(page).to have_content('file name') end end diff --git a/spec/features/projects/tree/upload_file_spec.rb b/spec/features/projects/tree/upload_file_spec.rb index d4e57d1ecfa..9fbb1dbd0e8 100644 --- a/spec/features/projects/tree/upload_file_spec.rb +++ b/spec/features/projects/tree/upload_file_spec.rb @@ -15,6 +15,14 @@ feature 'Multi-file editor upload file', :js do visit project_tree_path(project, :master) wait_for_requests + + click_link('Multi Edit') + + wait_for_requests + end + + after do + set_cookie('new_repo', 'false') end it 'uploads text file' do @@ -41,6 +49,5 @@ feature 'Multi-file editor upload file', :js do expect(page).to have_selector('.multi-file-tab', text: 'dk.png') expect(page).not_to have_selector('.monaco-editor') - expect(page).to have_content('The source could not be displayed for this temporary file.') end end diff --git a/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js index f750061a6a1..c4d3866c922 100644 --- a/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js +++ b/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import listCollapsed from '~/repo/components/commit_sidebar/list_collapsed.vue'; +import store from '~/ide/stores'; +import listCollapsed from '~/ide/components/commit_sidebar/list_collapsed.vue'; import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; import { file } from '../../helpers'; diff --git a/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js index 18c9b46fcd9..fc7c9ae9dd7 100644 --- a/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js +++ b/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import listItem from '~/repo/components/commit_sidebar/list_item.vue'; +import listItem from '~/ide/components/commit_sidebar/list_item.vue'; import mountComponent from '../../../helpers/vue_mount_component_helper'; import { file } from '../../helpers'; diff --git a/spec/javascripts/repo/components/commit_sidebar/list_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_spec.js index df7e3c5de21..cb5240ad118 100644 --- a/spec/javascripts/repo/components/commit_sidebar/list_spec.js +++ b/spec/javascripts/repo/components/commit_sidebar/list_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import commitSidebarList from '~/repo/components/commit_sidebar/list.vue'; +import store from '~/ide/stores'; +import commitSidebarList from '~/ide/components/commit_sidebar/list.vue'; import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; import { file } from '../../helpers'; @@ -13,8 +13,11 @@ describe('Multi-file editor commit sidebar list', () => { vm = createComponentWithStore(Component, store, { title: 'Staged', fileList: [], - collapsed: false, - }).$mount(); + }); + + vm.$store.state.rightPanelCollapsed = false; + + vm.$mount(); }); afterEach(() => { @@ -43,30 +46,14 @@ describe('Multi-file editor commit sidebar list', () => { describe('collapsed', () => { beforeEach((done) => { - vm.collapsed = true; + vm.$store.state.rightPanelCollapsed = true; Vue.nextTick(done); }); - it('adds collapsed class', () => { - expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull(); - }); - it('hides list', () => { expect(vm.$el.querySelector('.list-unstyled')).toBeNull(); expect(vm.$el.querySelector('.help-block')).toBeNull(); }); - - it('hides collapse button', () => { - expect(vm.$el.querySelector('.multi-file-commit-panel-collapse-btn')).toBeNull(); - }); - }); - - it('clicking toggle collapse button emits toggle event', () => { - spyOn(vm, '$emit'); - - vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click(); - - expect(vm.$emit).toHaveBeenCalledWith('toggleCollapsed'); }); }); diff --git a/spec/javascripts/repo/components/ide_context_bar_spec.js b/spec/javascripts/repo/components/ide_context_bar_spec.js new file mode 100644 index 00000000000..3f8f37d2343 --- /dev/null +++ b/spec/javascripts/repo/components/ide_context_bar_spec.js @@ -0,0 +1,49 @@ +import Vue from 'vue'; +import store from '~/ide/stores'; +import ideContextBar from '~/ide/components/ide_context_bar.vue'; +import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; + +describe('Multi-file editor right context bar', () => { + let vm; + + beforeEach(() => { + const Component = Vue.extend(ideContextBar); + + vm = createComponentWithStore(Component, store); + + vm.$store.state.rightPanelCollapsed = false; + + vm.$mount(); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('collapsed', () => { + beforeEach((done) => { + vm.$store.state.rightPanelCollapsed = true; + + Vue.nextTick(done); + }); + + it('adds collapsed class', () => { + expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull(); + }); + + it('shows correct icon', () => { + expect(vm.currentIcon).toBe('angle-double-left'); + }); + }); + + it('clicking toggle collapse button collapses the bar', () => { + spyOn(vm, 'setPanelCollapsedStatus').and.returnValue(Promise.resolve()); + + vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click(); + + expect(vm.setPanelCollapsedStatus).toHaveBeenCalledWith({ + side: 'right', + collapsed: true, + }); + }); +}); diff --git a/spec/javascripts/repo/components/repo_sidebar_spec.js b/spec/javascripts/repo/components/ide_repo_tree_spec.js index df7cf8aabbb..b6f70f585cd 100644 --- a/spec/javascripts/repo/components/repo_sidebar_spec.js +++ b/spec/javascripts/repo/components/ide_repo_tree_spec.js @@ -1,20 +1,26 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import repoSidebar from '~/repo/components/repo_sidebar.vue'; +import store from '~/ide/stores'; +import ideRepoTree from '~/ide/components/ide_repo_tree.vue'; import { file, resetStore } from '../helpers'; -describe('RepoSidebar', () => { +describe('IdeRepoTree', () => { let vm; beforeEach(() => { - const RepoSidebar = Vue.extend(repoSidebar); + const IdeRepoTree = Vue.extend(ideRepoTree); - vm = new RepoSidebar({ + vm = new IdeRepoTree({ store, + propsData: { + treeId: 'abcproject/mybranch', + }, }); + vm.$store.state.currentBranch = 'master'; vm.$store.state.isRoot = true; - vm.$store.state.tree.push(file()); + vm.$store.state.trees['abcproject/mybranch'] = { + tree: [file()], + }; vm.$mount(); }); @@ -26,13 +32,9 @@ describe('RepoSidebar', () => { }); it('renders a sidebar', () => { - const thead = vm.$el.querySelector('thead'); const tbody = vm.$el.querySelector('tbody'); expect(vm.$el.classList.contains('sidebar-mini')).toBeFalsy(); - expect(thead.querySelector('.name').textContent.trim()).toEqual('Name'); - expect(thead.querySelector('.last-commit').textContent.trim()).toEqual('Last commit'); - expect(thead.querySelector('.last-update').textContent.trim()).toEqual('Last update'); expect(tbody.querySelector('.repo-file-options')).toBeFalsy(); expect(tbody.querySelector('.prev-directory')).toBeFalsy(); expect(tbody.querySelector('.loading-file')).toBeFalsy(); @@ -40,7 +42,6 @@ describe('RepoSidebar', () => { }); it('renders 5 loading files if tree is loading', (done) => { - vm.$store.state.tree = []; vm.$store.state.loading = true; Vue.nextTick(() => { diff --git a/spec/javascripts/repo/components/ide_side_bar_spec.js b/spec/javascripts/repo/components/ide_side_bar_spec.js new file mode 100644 index 00000000000..30e45169205 --- /dev/null +++ b/spec/javascripts/repo/components/ide_side_bar_spec.js @@ -0,0 +1,43 @@ +import Vue from 'vue'; +import store from '~/ide/stores'; +import ideSidebar from '~/ide/components/ide_side_bar.vue'; +import { resetStore } from '../helpers'; +import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; + +describe('IdeSidebar', () => { + let vm; + + beforeEach(() => { + const Component = Vue.extend(ideSidebar); + + vm = createComponentWithStore(Component, store).$mount(); + + vm.$store.state.leftPanelCollapsed = false; + }); + + afterEach(() => { + vm.$destroy(); + + resetStore(vm.$store); + }); + + it('renders a sidebar', () => { + expect(vm.$el.querySelector('.multi-file-commit-panel-inner')).not.toBeNull(); + }); + + describe('collapsed', () => { + beforeEach((done) => { + vm.$store.state.leftPanelCollapsed = true; + + Vue.nextTick(done); + }); + + it('adds collapsed class', () => { + expect(vm.$el.classList).toContain('is-collapsed'); + }); + + it('shows correct icon', () => { + expect(vm.currentIcon).toBe('angle-double-right'); + }); + }); +}); diff --git a/spec/javascripts/repo/components/repo_spec.js b/spec/javascripts/repo/components/ide_spec.js index b32d2c13af8..20b8dc25dcb 100644 --- a/spec/javascripts/repo/components/repo_spec.js +++ b/spec/javascripts/repo/components/ide_spec.js @@ -1,14 +1,14 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import repo from '~/repo/components/repo.vue'; +import store from '~/ide/stores'; +import ide from '~/ide/components/ide.vue'; import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; import { file, resetStore } from '../helpers'; -describe('repo component', () => { +describe('ide component', () => { let vm; beforeEach(() => { - const Component = Vue.extend(repo); + const Component = Vue.extend(ide); vm = createComponentWithStore(Component, store).$mount(); }); @@ -24,7 +24,9 @@ describe('repo component', () => { }); it('renders panel right when files are open', (done) => { - vm.$store.state.tree.push(file()); + vm.$store.state.trees['abcproject/mybranch'] = { + tree: [file()], + }; Vue.nextTick(() => { expect(vm.$el.querySelector('.panel-right')).toBeNull(); diff --git a/spec/javascripts/repo/components/new_branch_form_spec.js b/spec/javascripts/repo/components/new_branch_form_spec.js index 9a705a1f0ed..cd1d073ec18 100644 --- a/spec/javascripts/repo/components/new_branch_form_spec.js +++ b/spec/javascripts/repo/components/new_branch_form_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import newBranchForm from '~/repo/components/new_branch_form.vue'; +import store from '~/ide/stores'; +import newBranchForm from '~/ide/components/new_branch_form.vue'; import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; import { resetStore } from '../helpers'; diff --git a/spec/javascripts/repo/components/new_dropdown/index_spec.js b/spec/javascripts/repo/components/new_dropdown/index_spec.js index 93b10fc1fee..b001c1655b4 100644 --- a/spec/javascripts/repo/components/new_dropdown/index_spec.js +++ b/spec/javascripts/repo/components/new_dropdown/index_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import newDropdown from '~/repo/components/new_dropdown/index.vue'; +import store from '~/ide/stores'; +import newDropdown from '~/ide/components/new_dropdown/index.vue'; import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; import { resetStore } from '../../helpers'; @@ -10,8 +10,12 @@ describe('new dropdown component', () => { beforeEach(() => { const component = Vue.extend(newDropdown); - vm = createComponentWithStore(component, store); + vm = createComponentWithStore(component, store, { + branch: 'master', + path: '', + }); + vm.$store.state.currentProjectId = 'abcproject'; vm.$store.state.path = ''; vm.$mount(); @@ -23,9 +27,10 @@ describe('new dropdown component', () => { resetStore(vm.$store); }); - it('renders new file and new directory links', () => { + it('renders new file, upload and new directory links', () => { expect(vm.$el.querySelectorAll('a')[0].textContent.trim()).toBe('New file'); - expect(vm.$el.querySelectorAll('a')[1].textContent.trim()).toBe('New directory'); + expect(vm.$el.querySelectorAll('a')[1].textContent.trim()).toBe('Upload file'); + expect(vm.$el.querySelectorAll('a')[2].textContent.trim()).toBe('New directory'); }); describe('createNewItem', () => { @@ -36,7 +41,7 @@ describe('new dropdown component', () => { }); it('sets modalType to tree when new directory is clicked', () => { - vm.$el.querySelectorAll('a')[1].click(); + vm.$el.querySelectorAll('a')[2].click(); expect(vm.modalType).toBe('tree'); }); diff --git a/spec/javascripts/repo/components/new_dropdown/modal_spec.js b/spec/javascripts/repo/components/new_dropdown/modal_spec.js index 1ff7590ec79..233cca06ed0 100644 --- a/spec/javascripts/repo/components/new_dropdown/modal_spec.js +++ b/spec/javascripts/repo/components/new_dropdown/modal_spec.js @@ -1,12 +1,42 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import modal from '~/repo/components/new_dropdown/modal.vue'; +import store from '~/ide/stores'; +import service from '~/ide/services'; +import modal from '~/ide/components/new_dropdown/modal.vue'; import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; import { file, resetStore } from '../../helpers'; describe('new file modal component', () => { const Component = Vue.extend(modal); let vm; + let projectTree; + + beforeEach(() => { + spyOn(service, 'getProjectData').and.returnValue(Promise.resolve({ + data: { + id: '123', + }, + })); + + spyOn(service, 'getBranchData').and.returnValue(Promise.resolve({ + commit: { + id: '123branch', + }, + })); + + spyOn(service, 'getTreeData').and.returnValue(Promise.resolve({ + headers: { + 'page-title': 'test', + }, + json: () => Promise.resolve({ + last_commit_path: 'last_commit_path', + parent_tree_url: 'parent_tree_url', + path: '/', + trees: [{ name: 'tree' }], + blobs: [{ name: 'blob' }], + submodules: [{ name: 'submodule' }], + }), + })); + }); afterEach(() => { vm.$destroy(); @@ -17,12 +47,26 @@ describe('new file modal component', () => { ['tree', 'blob'].forEach((type) => { describe(type, () => { beforeEach(() => { + store.state.projects.abcproject = { + web_url: '', + }; + store.state.trees = []; + store.state.trees['abcproject/mybranch'] = { + tree: [], + }; + projectTree = store.state.trees['abcproject/mybranch']; + store.state.currentProjectId = 'abcproject'; + vm = createComponentWithStore(Component, store, { type, + branchId: 'master', path: '', - }).$mount(); + parent: projectTree, + }); vm.entryName = 'testing'; + + vm.$mount(); }); it(`sets modal title as ${type}`, () => { @@ -50,6 +94,9 @@ describe('new file modal component', () => { vm.createEntryInStore(); expect(vm.createTempEntry).toHaveBeenCalledWith({ + projectId: 'abcproject', + branchId: 'master', + parent: projectTree, name: 'testing', type, }); @@ -76,31 +123,18 @@ describe('new file modal component', () => { }); it('opens newly created file', (done) => { - vm.createEntryInStore(); - - setTimeout(() => { - expect(vm.$store.state.openFiles.length).toBe(1); - expect(vm.$store.state.openFiles[0].name).toBe(type === 'blob' ? 'testing' : '.gitkeep'); - - done(); - }); - }); - - it(`creates ${type} in the current stores path`, (done) => { - vm.$store.state.path = 'app'; - - vm.createEntryInStore(); - - setTimeout(() => { - expect(vm.$store.state.tree[0].path).toBe('app/testing'); - expect(vm.$store.state.tree[0].name).toBe('testing'); + if (type === 'blob') { + vm.createEntryInStore(); - if (type === 'tree') { - expect(vm.$store.state.tree[0].tree.length).toBe(1); - } + setTimeout(() => { + expect(vm.$store.state.openFiles.length).toBe(1); + expect(vm.$store.state.openFiles[0].name).toBe(type === 'blob' ? 'testing' : '.gitkeep'); + done(); + }); + } else { done(); - }); + } }); if (type === 'blob') { @@ -108,25 +142,27 @@ describe('new file modal component', () => { vm.createEntryInStore(); setTimeout(() => { - expect(vm.$store.state.tree.length).toBe(1); - expect(vm.$store.state.tree[0].name).toBe('testing'); - expect(vm.$store.state.tree[0].type).toBe('blob'); - expect(vm.$store.state.tree[0].tempFile).toBeTruthy(); + const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree; + expect(baseTree.length).toBe(1); + expect(baseTree[0].name).toBe('testing'); + expect(baseTree[0].type).toBe('blob'); + expect(baseTree[0].tempFile).toBeTruthy(); done(); }); }); it('does not create temp file when file already exists', (done) => { - vm.$store.state.tree.push(file('testing', '1', type)); + const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree; + baseTree.push(file('testing', '1', type)); vm.createEntryInStore(); setTimeout(() => { - expect(vm.$store.state.tree.length).toBe(1); - expect(vm.$store.state.tree[0].name).toBe('testing'); - expect(vm.$store.state.tree[0].type).toBe('blob'); - expect(vm.$store.state.tree[0].tempFile).toBeFalsy(); + expect(baseTree.length).toBe(1); + expect(baseTree[0].name).toBe('testing'); + expect(baseTree[0].type).toBe('blob'); + expect(baseTree[0].tempFile).toBeFalsy(); done(); }); @@ -135,48 +171,47 @@ describe('new file modal component', () => { it('creates new tree', () => { vm.createEntryInStore(); - expect(vm.$store.state.tree.length).toBe(1); - expect(vm.$store.state.tree[0].name).toBe('testing'); - expect(vm.$store.state.tree[0].type).toBe('tree'); - expect(vm.$store.state.tree[0].tempFile).toBeTruthy(); - expect(vm.$store.state.tree[0].tree.length).toBe(1); - expect(vm.$store.state.tree[0].tree[0].name).toBe('.gitkeep'); + const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree; + expect(baseTree.length).toBe(1); + expect(baseTree[0].name).toBe('testing'); + expect(baseTree[0].type).toBe('tree'); + expect(baseTree[0].tempFile).toBeTruthy(); }); it('creates multiple trees when entryName has slashes', () => { vm.entryName = 'app/test'; vm.createEntryInStore(); - expect(vm.$store.state.tree.length).toBe(1); - expect(vm.$store.state.tree[0].name).toBe('app'); - expect(vm.$store.state.tree[0].tree[0].name).toBe('test'); - expect(vm.$store.state.tree[0].tree[0].tree[0].name).toBe('.gitkeep'); + const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree; + expect(baseTree.length).toBe(1); + expect(baseTree[0].name).toBe('app'); }); it('creates tree in existing tree', () => { - vm.$store.state.tree.push(file('app', '1', 'tree')); + const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree; + baseTree.push(file('app', '1', 'tree')); vm.entryName = 'app/test'; vm.createEntryInStore(); - expect(vm.$store.state.tree.length).toBe(1); - expect(vm.$store.state.tree[0].name).toBe('app'); - expect(vm.$store.state.tree[0].tempFile).toBeFalsy(); - expect(vm.$store.state.tree[0].tree[0].tempFile).toBeTruthy(); - expect(vm.$store.state.tree[0].tree[0].name).toBe('test'); - expect(vm.$store.state.tree[0].tree[0].tree[0].name).toBe('.gitkeep'); + expect(baseTree.length).toBe(1); + expect(baseTree[0].name).toBe('app'); + expect(baseTree[0].tempFile).toBeFalsy(); + expect(baseTree[0].tree[0].tempFile).toBeTruthy(); + expect(baseTree[0].tree[0].name).toBe('test'); }); it('does not create new tree when already exists', () => { - vm.$store.state.tree.push(file('app', '1', 'tree')); + const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree; + baseTree.push(file('app', '1', 'tree')); vm.entryName = 'app'; vm.createEntryInStore(); - expect(vm.$store.state.tree.length).toBe(1); - expect(vm.$store.state.tree[0].name).toBe('app'); - expect(vm.$store.state.tree[0].tempFile).toBeFalsy(); - expect(vm.$store.state.tree[0].tree.length).toBe(0); + expect(baseTree.length).toBe(1); + expect(baseTree[0].name).toBe('app'); + expect(baseTree[0].tempFile).toBeFalsy(); + expect(baseTree[0].tree.length).toBe(0); }); } }); @@ -188,6 +223,8 @@ describe('new file modal component', () => { vm = createComponentWithStore(Component, store, { type: 'tree', + projectId: 'abcproject', + branchId: 'master', path: '', }).$mount('.js-test'); diff --git a/spec/javascripts/repo/components/new_dropdown/upload_spec.js b/spec/javascripts/repo/components/new_dropdown/upload_spec.js index bf7893029b1..788c08e5279 100644 --- a/spec/javascripts/repo/components/new_dropdown/upload_spec.js +++ b/spec/javascripts/repo/components/new_dropdown/upload_spec.js @@ -1,19 +1,61 @@ import Vue from 'vue'; -import upload from '~/repo/components/new_dropdown/upload.vue'; -import store from '~/repo/stores'; +import upload from '~/ide/components/new_dropdown/upload.vue'; +import store from '~/ide/stores'; +import service from '~/ide/services'; import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; import { resetStore } from '../../helpers'; describe('new dropdown upload', () => { let vm; + let projectTree; beforeEach(() => { + spyOn(service, 'getProjectData').and.returnValue(Promise.resolve({ + data: { + id: '123', + }, + })); + + spyOn(service, 'getBranchData').and.returnValue(Promise.resolve({ + commit: { + id: '123branch', + }, + })); + + spyOn(service, 'getTreeData').and.returnValue(Promise.resolve({ + headers: { + 'page-title': 'test', + }, + json: () => Promise.resolve({ + last_commit_path: 'last_commit_path', + parent_tree_url: 'parent_tree_url', + path: '/', + trees: [{ name: 'tree' }], + blobs: [{ name: 'blob' }], + submodules: [{ name: 'submodule' }], + }), + })); + const Component = Vue.extend(upload); + store.state.projects.abcproject = { + web_url: '', + }; + store.state.currentProjectId = 'abcproject'; + store.state.trees = []; + store.state.trees['abcproject/mybranch'] = { + tree: [], + }; + projectTree = store.state.trees['abcproject/mybranch']; + vm = createComponentWithStore(Component, store, { + branchId: 'master', path: '', + parent: projectTree, }); + vm.entryName = 'testing'; + vm.$mount(); }); @@ -65,23 +107,33 @@ describe('new dropdown upload', () => { vm.createFile(target, file, true); vm.$nextTick(() => { - expect(vm.$store.state.tree.length).toBe(1); - expect(vm.$store.state.tree[0].name).toBe(file.name); - expect(vm.$store.state.tree[0].content).toBe(target.result); + const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree; + expect(baseTree.length).toBe(1); + expect(baseTree[0].name).toBe(file.name); + expect(baseTree[0].content).toBe(target.result); done(); }); }); it('creates new file in path', (done) => { - vm.$store.state.path = 'testing'; + const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree; + const tree = { + type: 'tree', + name: 'testing', + path: 'testing', + tree: [], + }; + baseTree.push(tree); + + vm.parent = tree; vm.createFile(target, file, true); vm.$nextTick(() => { - expect(vm.$store.state.tree.length).toBe(1); - expect(vm.$store.state.tree[0].name).toBe(file.name); - expect(vm.$store.state.tree[0].content).toBe(target.result); - expect(vm.$store.state.tree[0].path).toBe(`testing/${file.name}`); + expect(baseTree.length).toBe(1); + expect(baseTree[0].tree[0].name).toBe(file.name); + expect(baseTree[0].tree[0].content).toBe(target.result); + expect(baseTree[0].tree[0].path).toBe(`testing/${file.name}`); done(); }); @@ -91,10 +143,11 @@ describe('new dropdown upload', () => { vm.createFile(binaryTarget, file, false); vm.$nextTick(() => { - expect(vm.$store.state.tree.length).toBe(1); - expect(vm.$store.state.tree[0].name).toBe(file.name); - expect(vm.$store.state.tree[0].content).toBe(binaryTarget.result.split('base64,')[1]); - expect(vm.$store.state.tree[0].base64).toBe(true); + const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree; + expect(baseTree.length).toBe(1); + expect(baseTree[0].name).toBe(file.name); + expect(baseTree[0].content).toBe(binaryTarget.result.split('base64,')[1]); + expect(baseTree[0].base64).toBe(true); done(); }); diff --git a/spec/javascripts/repo/components/repo_commit_section_spec.js b/spec/javascripts/repo/components/repo_commit_section_spec.js index 72712e058e5..cd93fb3ccbf 100644 --- a/spec/javascripts/repo/components/repo_commit_section_spec.js +++ b/spec/javascripts/repo/components/repo_commit_section_spec.js @@ -1,8 +1,8 @@ import Vue from 'vue'; import * as urlUtils from '~/lib/utils/url_utility'; -import store from '~/repo/stores'; -import service from '~/repo/services'; -import repoCommitSection from '~/repo/components/repo_commit_section.vue'; +import store from '~/ide/stores'; +import service from '~/ide/services'; +import repoCommitSection from '~/ide/components/repo_commit_section.vue'; import getSetTimeoutPromise from '../../helpers/set_timeout_promise_helper'; import { file, resetStore } from '../helpers'; @@ -16,6 +16,18 @@ describe('RepoCommitSection', () => { store, }).$mount(); + comp.$store.state.currentProjectId = 'abcproject'; + comp.$store.state.currentBranchId = 'master'; + comp.$store.state.projects.abcproject = { + web_url: '', + branches: { + master: { + workingReference: '1', + }, + }, + }; + + comp.$store.state.rightPanelCollapsed = false; comp.$store.state.currentBranch = 'master'; comp.$store.state.openFiles = [file(), file()]; comp.$store.state.openFiles.forEach(f => Object.assign(f, { @@ -29,7 +41,19 @@ describe('RepoCommitSection', () => { beforeEach((done) => { vm = createComponent(); - vm.collapsed = false; + spyOn(service, 'getTreeData').and.returnValue(Promise.resolve({ + headers: { + 'page-title': 'test', + }, + json: () => Promise.resolve({ + last_commit_path: 'last_commit_path', + parent_tree_url: 'parent_tree_url', + path: '/', + trees: [{ name: 'tree' }], + blobs: [{ name: 'blob' }], + submodules: [{ name: 'submodule' }], + }), + })); Vue.nextTick(done); }); @@ -45,7 +69,6 @@ describe('RepoCommitSection', () => { const submitCommit = vm.$el.querySelector('form .btn'); expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull(); - expect(vm.$el.querySelector('.multi-file-commit-panel-section header').textContent.trim()).toEqual('Staged'); expect(changedFileElements.length).toEqual(2); changedFileElements.forEach((changedFile, i) => { diff --git a/spec/javascripts/repo/components/repo_edit_button_spec.js b/spec/javascripts/repo/components/repo_edit_button_spec.js index 44018464b35..2895b794506 100644 --- a/spec/javascripts/repo/components/repo_edit_button_spec.js +++ b/spec/javascripts/repo/components/repo_edit_button_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import repoEditButton from '~/repo/components/repo_edit_button.vue'; +import store from '~/ide/stores'; +import repoEditButton from '~/ide/components/repo_edit_button.vue'; import { file, resetStore } from '../helpers'; describe('RepoEditButton', () => { @@ -32,7 +32,7 @@ describe('RepoEditButton', () => { vm.$mount(); expect(vm.$el.querySelector('.btn')).not.toBeNull(); - expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Edit'); + expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Cancel edit'); }); it('renders edit button with cancel text', () => { @@ -50,7 +50,7 @@ describe('RepoEditButton', () => { vm.$el.querySelector('.btn').click(); vm.$nextTick(() => { - expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Cancel edit'); + expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Edit'); done(); }); diff --git a/spec/javascripts/repo/components/repo_editor_spec.js b/spec/javascripts/repo/components/repo_editor_spec.js index 81158cad639..e7b2ed08acd 100644 --- a/spec/javascripts/repo/components/repo_editor_spec.js +++ b/spec/javascripts/repo/components/repo_editor_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import repoEditor from '~/repo/components/repo_editor.vue'; -import monacoLoader from '~/repo/monaco_loader'; +import store from '~/ide/stores'; +import repoEditor from '~/ide/components/repo_editor.vue'; +import monacoLoader from '~/ide/monaco_loader'; import { file, resetStore } from '../helpers'; describe('RepoEditor', () => { diff --git a/spec/javascripts/repo/components/repo_file_buttons_spec.js b/spec/javascripts/repo/components/repo_file_buttons_spec.js index d6e255e4810..115569a9117 100644 --- a/spec/javascripts/repo/components/repo_file_buttons_spec.js +++ b/spec/javascripts/repo/components/repo_file_buttons_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import repoFileButtons from '~/repo/components/repo_file_buttons.vue'; +import store from '~/ide/stores'; +import repoFileButtons from '~/ide/components/repo_file_buttons.vue'; import { file, resetStore } from '../helpers'; describe('RepoFileButtons', () => { diff --git a/spec/javascripts/repo/components/repo_file_spec.js b/spec/javascripts/repo/components/repo_file_spec.js index bf9181fb09c..e8b370f97b4 100644 --- a/spec/javascripts/repo/components/repo_file_spec.js +++ b/spec/javascripts/repo/components/repo_file_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import repoFile from '~/repo/components/repo_file.vue'; +import store from '~/ide/stores'; +import repoFile from '~/ide/components/repo_file.vue'; import { file, resetStore } from '../helpers'; describe('RepoFile', () => { @@ -35,11 +35,10 @@ describe('RepoFile', () => { const fileIcon = vm.$el.querySelector('.file-icon'); expect(vm.$el.querySelector(`.${vm.file.icon}`).style.marginLeft).toEqual('0px'); - expect(name.href).toMatch(`/${vm.file.url}`); + expect(name.href).toMatch(''); expect(name.textContent.trim()).toEqual(vm.file.name); expect(fileIcon.classList.contains(vm.file.icon)).toBeTruthy(); expect(fileIcon.style.marginLeft).toEqual(`${vm.file.level * 10}px`); - expect(vm.$el.querySelectorAll('.animation-container').length).toBe(2); }); it('does render if hasFiles is true and is loading tree', () => { @@ -75,16 +74,16 @@ describe('RepoFile', () => { }); }); - it('fires clickedTreeRow when the link is clicked', () => { + it('fires clickFile when the link is clicked', () => { vm = createComponent({ file: file(), }); - spyOn(vm, 'clickedTreeRow'); + spyOn(vm, 'clickFile'); vm.$el.click(); - expect(vm.clickedTreeRow).toHaveBeenCalledWith(vm.file); + expect(vm.clickFile).toHaveBeenCalledWith(vm.file); }); describe('submodule', () => { diff --git a/spec/javascripts/repo/components/repo_loading_file_spec.js b/spec/javascripts/repo/components/repo_loading_file_spec.js index 031f2a9c0b2..18366fb89bc 100644 --- a/spec/javascripts/repo/components/repo_loading_file_spec.js +++ b/spec/javascripts/repo/components/repo_loading_file_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import repoLoadingFile from '~/repo/components/repo_loading_file.vue'; +import store from '~/ide/stores'; +import repoLoadingFile from '~/ide/components/repo_loading_file.vue'; import { resetStore } from '../helpers'; describe('RepoLoadingFile', () => { @@ -48,6 +48,7 @@ describe('RepoLoadingFile', () => { it('renders 1 column of animated LoC if isMini', (done) => { vm = createComponent(); + vm.$store.state.leftPanelCollapsed = true; vm.$store.state.openFiles.push('test'); vm.$nextTick(() => { diff --git a/spec/javascripts/repo/components/repo_prev_directory_spec.js b/spec/javascripts/repo/components/repo_prev_directory_spec.js index 7f82ae36a64..ff26cab2262 100644 --- a/spec/javascripts/repo/components/repo_prev_directory_spec.js +++ b/spec/javascripts/repo/components/repo_prev_directory_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import repoPrevDirectory from '~/repo/components/repo_prev_directory.vue'; +import store from '~/ide/stores'; +import repoPrevDirectory from '~/ide/components/repo_prev_directory.vue'; import { resetStore } from '../helpers'; describe('RepoPrevDirectory', () => { diff --git a/spec/javascripts/repo/components/repo_preview_spec.js b/spec/javascripts/repo/components/repo_preview_spec.js index 8d1a87494cf..e90837e4cb2 100644 --- a/spec/javascripts/repo/components/repo_preview_spec.js +++ b/spec/javascripts/repo/components/repo_preview_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import repoPreview from '~/repo/components/repo_preview.vue'; +import store from '~/ide/stores'; +import repoPreview from '~/ide/components/repo_preview.vue'; import { file, resetStore } from '../helpers'; describe('RepoPreview', () => { diff --git a/spec/javascripts/repo/components/repo_tab_spec.js b/spec/javascripts/repo/components/repo_tab_spec.js index 7d2174196c9..507bca983df 100644 --- a/spec/javascripts/repo/components/repo_tab_spec.js +++ b/spec/javascripts/repo/components/repo_tab_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import repoTab from '~/repo/components/repo_tab.vue'; +import store from '~/ide/stores'; +import repoTab from '~/ide/components/repo_tab.vue'; import { file, resetStore } from '../helpers'; describe('RepoTab', () => { @@ -31,16 +31,16 @@ describe('RepoTab', () => { expect(name.textContent.trim()).toEqual(vm.tab.name); }); - it('calls setFileActive when clicking tab', () => { + it('fires clickFile when the link is clicked', () => { vm = createComponent({ tab: file(), }); - spyOn(vm, 'setFileActive'); + spyOn(vm, 'clickFile'); vm.$el.click(); - expect(vm.setFileActive).toHaveBeenCalledWith(vm.tab); + expect(vm.clickFile).toHaveBeenCalledWith(vm.tab); }); it('calls closeFile when clicking close button', () => { diff --git a/spec/javascripts/repo/components/repo_tabs_spec.js b/spec/javascripts/repo/components/repo_tabs_spec.js index 1fb2242c051..0beaf643793 100644 --- a/spec/javascripts/repo/components/repo_tabs_spec.js +++ b/spec/javascripts/repo/components/repo_tabs_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import repoTabs from '~/repo/components/repo_tabs.vue'; +import store from '~/ide/stores'; +import repoTabs from '~/ide/components/repo_tabs.vue'; import { file, resetStore } from '../helpers'; describe('RepoTabs', () => { diff --git a/spec/javascripts/repo/helpers.js b/spec/javascripts/repo/helpers.js index 820a44992b4..ac43d221198 100644 --- a/spec/javascripts/repo/helpers.js +++ b/spec/javascripts/repo/helpers.js @@ -1,5 +1,5 @@ -import { decorateData } from '~/repo/stores/utils'; -import state from '~/repo/stores/state'; +import { decorateData } from '~/ide/stores/utils'; +import state from '~/ide/stores/state'; export const resetStore = (store) => { store.replaceState(state()); @@ -12,4 +12,5 @@ export const file = (name = 'name', id = name, type = '') => decorateData({ url: 'url', name, path: name, + lastCommit: {}, }); diff --git a/spec/javascripts/repo/lib/common/disposable_spec.js b/spec/javascripts/repo/lib/common/disposable_spec.js index 62c3913bf4d..af12ca15369 100644 --- a/spec/javascripts/repo/lib/common/disposable_spec.js +++ b/spec/javascripts/repo/lib/common/disposable_spec.js @@ -1,4 +1,4 @@ -import Disposable from '~/repo/lib/common/disposable'; +import Disposable from '~/ide/lib/common/disposable'; describe('Multi-file editor library disposable class', () => { let instance; diff --git a/spec/javascripts/repo/lib/common/model_manager_spec.js b/spec/javascripts/repo/lib/common/model_manager_spec.js index 8c134f178c0..563c2e33834 100644 --- a/spec/javascripts/repo/lib/common/model_manager_spec.js +++ b/spec/javascripts/repo/lib/common/model_manager_spec.js @@ -1,6 +1,6 @@ /* global monaco */ -import monacoLoader from '~/repo/monaco_loader'; -import ModelManager from '~/repo/lib/common/model_manager'; +import monacoLoader from '~/ide/monaco_loader'; +import ModelManager from '~/ide/lib/common/model_manager'; import { file } from '../../helpers'; describe('Multi-file editor library model manager', () => { diff --git a/spec/javascripts/repo/lib/common/model_spec.js b/spec/javascripts/repo/lib/common/model_spec.js index d41ade237ca..878a4a3f3fe 100644 --- a/spec/javascripts/repo/lib/common/model_spec.js +++ b/spec/javascripts/repo/lib/common/model_spec.js @@ -1,6 +1,6 @@ /* global monaco */ -import monacoLoader from '~/repo/monaco_loader'; -import Model from '~/repo/lib/common/model'; +import monacoLoader from '~/ide/monaco_loader'; +import Model from '~/ide/lib/common/model'; import { file } from '../../helpers'; describe('Multi-file editor library model', () => { diff --git a/spec/javascripts/repo/lib/decorations/controller_spec.js b/spec/javascripts/repo/lib/decorations/controller_spec.js index 2e32e8fa0bd..fea12d74dca 100644 --- a/spec/javascripts/repo/lib/decorations/controller_spec.js +++ b/spec/javascripts/repo/lib/decorations/controller_spec.js @@ -1,8 +1,8 @@ /* global monaco */ -import monacoLoader from '~/repo/monaco_loader'; -import editor from '~/repo/lib/editor'; -import DecorationsController from '~/repo/lib/decorations/controller'; -import Model from '~/repo/lib/common/model'; +import monacoLoader from '~/ide/monaco_loader'; +import editor from '~/ide/lib/editor'; +import DecorationsController from '~/ide/lib/decorations/controller'; +import Model from '~/ide/lib/common/model'; import { file } from '../../helpers'; describe('Multi-file editor library decorations controller', () => { diff --git a/spec/javascripts/repo/lib/diff/controller_spec.js b/spec/javascripts/repo/lib/diff/controller_spec.js index ed62e28d3a3..1d55c165260 100644 --- a/spec/javascripts/repo/lib/diff/controller_spec.js +++ b/spec/javascripts/repo/lib/diff/controller_spec.js @@ -1,10 +1,10 @@ /* global monaco */ -import monacoLoader from '~/repo/monaco_loader'; -import editor from '~/repo/lib/editor'; -import ModelManager from '~/repo/lib/common/model_manager'; -import DecorationsController from '~/repo/lib/decorations/controller'; -import DirtyDiffController, { getDiffChangeType, getDecorator } from '~/repo/lib/diff/controller'; -import { computeDiff } from '~/repo/lib/diff/diff'; +import monacoLoader from '~/ide/monaco_loader'; +import editor from '~/ide/lib/editor'; +import ModelManager from '~/ide/lib/common/model_manager'; +import DecorationsController from '~/ide/lib/decorations/controller'; +import DirtyDiffController, { getDiffChangeType, getDecorator } from '~/ide/lib/diff/controller'; +import { computeDiff } from '~/ide/lib/diff/diff'; import { file } from '../../helpers'; describe('Multi-file editor library dirty diff controller', () => { diff --git a/spec/javascripts/repo/lib/diff/diff_spec.js b/spec/javascripts/repo/lib/diff/diff_spec.js index 3269ec5d2c9..57f3ac3d365 100644 --- a/spec/javascripts/repo/lib/diff/diff_spec.js +++ b/spec/javascripts/repo/lib/diff/diff_spec.js @@ -1,4 +1,4 @@ -import { computeDiff } from '~/repo/lib/diff/diff'; +import { computeDiff } from '~/ide/lib/diff/diff'; describe('Multi-file editor library diff calculator', () => { describe('computeDiff', () => { diff --git a/spec/javascripts/repo/lib/editor_options_spec.js b/spec/javascripts/repo/lib/editor_options_spec.js index b4887d063ed..edbf5450dce 100644 --- a/spec/javascripts/repo/lib/editor_options_spec.js +++ b/spec/javascripts/repo/lib/editor_options_spec.js @@ -1,4 +1,4 @@ -import editorOptions from '~/repo/lib/editor_options'; +import editorOptions from '~/ide/lib/editor_options'; describe('Multi-file editor library editor options', () => { it('returns an array', () => { diff --git a/spec/javascripts/repo/lib/editor_spec.js b/spec/javascripts/repo/lib/editor_spec.js index cd32832a232..8d51d48a782 100644 --- a/spec/javascripts/repo/lib/editor_spec.js +++ b/spec/javascripts/repo/lib/editor_spec.js @@ -1,6 +1,6 @@ /* global monaco */ -import monacoLoader from '~/repo/monaco_loader'; -import editor from '~/repo/lib/editor'; +import monacoLoader from '~/ide/monaco_loader'; +import editor from '~/ide/lib/editor'; import { file } from '../helpers'; describe('Multi-file editor library', () => { diff --git a/spec/javascripts/repo/monaco_loader_spec.js b/spec/javascripts/repo/monaco_loader_spec.js index 887a80160fc..b8ac36972aa 100644 --- a/spec/javascripts/repo/monaco_loader_spec.js +++ b/spec/javascripts/repo/monaco_loader_spec.js @@ -1,5 +1,5 @@ import monacoContext from 'monaco-editor/dev/vs/loader'; -import monacoLoader from '~/repo/monaco_loader'; +import monacoLoader from '~/ide/monaco_loader'; describe('MonacoLoader', () => { it('calls require.config and exports require', () => { diff --git a/spec/javascripts/repo/stores/actions/branch_spec.js b/spec/javascripts/repo/stores/actions/branch_spec.js index af9d6835a67..00d16fd790d 100644 --- a/spec/javascripts/repo/stores/actions/branch_spec.js +++ b/spec/javascripts/repo/stores/actions/branch_spec.js @@ -1,5 +1,5 @@ -import store from '~/repo/stores'; -import service from '~/repo/services'; +import store from '~/ide/stores'; +import service from '~/ide/services'; import { resetStore } from '../../helpers'; describe('Multi-file store branch actions', () => { @@ -16,19 +16,25 @@ describe('Multi-file store branch actions', () => { })); spyOn(history, 'pushState'); - store.state.project.id = 2; - store.state.currentBranch = 'testing'; + store.state.currentProjectId = 'abcproject'; + store.state.currentBranchId = 'testing'; + store.state.projects.abcproject = { + branches: { + master: { + workingReference: '1', + }, + }, + }; }); it('creates new branch', (done) => { store.dispatch('createNewBranch', 'master') .then(() => { - expect(store.state.currentBranch).toBe('testing'); - expect(service.createBranch).toHaveBeenCalledWith(2, { + expect(store.state.currentBranchId).toBe('testing'); + expect(service.createBranch).toHaveBeenCalledWith('abcproject', { branch: 'master', ref: 'testing', }); - expect(history.pushState).toHaveBeenCalled(); done(); }) diff --git a/spec/javascripts/repo/stores/actions/file_spec.js b/spec/javascripts/repo/stores/actions/file_spec.js index 099c0556e71..8ce01d3bf12 100644 --- a/spec/javascripts/repo/stores/actions/file_spec.js +++ b/spec/javascripts/repo/stores/actions/file_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import store from '~/repo/stores'; -import service from '~/repo/services'; +import store from '~/ide/stores'; +import service from '~/ide/services'; import { file, resetStore } from '../../helpers'; describe('Multi-file store file actions', () => { @@ -24,8 +24,6 @@ describe('Multi-file store file actions', () => { localFile.parentTreeUrl = 'parentTreeUrl'; store.state.openFiles.push(localFile); - - spyOn(history, 'pushState'); }); afterEach(() => { @@ -82,15 +80,6 @@ describe('Multi-file store file actions', () => { }).catch(done.fail); }); - it('calls pushState when no open files are left', (done) => { - store.dispatch('closeFile', { file: localFile }) - .then(() => { - expect(history.pushState).toHaveBeenCalledWith(jasmine.anything(), '', 'parentTreeUrl'); - - done(); - }).catch(done.fail); - }); - it('sets next file as active', (done) => { const f = file(); store.state.openFiles.push(f); @@ -322,8 +311,26 @@ describe('Multi-file store file actions', () => { }); describe('createTempFile', () => { + let projectTree; + beforeEach(() => { document.body.innerHTML += '<div class="flash-container"></div>'; + + store.state.currentProjectId = 'abcproject'; + store.state.currentBranchId = 'master'; + store.state.projects.abcproject = { + branches: { + master: { + workingReference: '1', + }, + }, + }; + + store.state.trees['abcproject/mybranch'] = { + tree: [], + }; + + projectTree = store.state.trees['abcproject/mybranch']; }); afterEach(() => { @@ -332,11 +339,13 @@ describe('Multi-file store file actions', () => { it('creates temp file', (done) => { store.dispatch('createTempFile', { - tree: store.state, name: 'test', + projectId: 'abcproject', + branchId: 'mybranch', + parent: projectTree, }).then((f) => { expect(f.tempFile).toBeTruthy(); - expect(store.state.tree.length).toBe(1); + expect(store.state.trees['abcproject/mybranch'].tree.length).toBe(1); done(); }).catch(done.fail); @@ -344,8 +353,10 @@ describe('Multi-file store file actions', () => { it('adds tmp file to open files', (done) => { store.dispatch('createTempFile', { - tree: store.state, name: 'test', + projectId: 'abcproject', + branchId: 'mybranch', + parent: projectTree, }).then((f) => { expect(store.state.openFiles.length).toBe(1); expect(store.state.openFiles[0].name).toBe(f.name); @@ -356,8 +367,10 @@ describe('Multi-file store file actions', () => { it('sets tmp file as active', (done) => { store.dispatch('createTempFile', { - tree: store.state, name: 'test', + projectId: 'abcproject', + branchId: 'mybranch', + parent: projectTree, }).then((f) => { expect(f.active).toBeTruthy(); @@ -367,8 +380,10 @@ describe('Multi-file store file actions', () => { it('enters edit mode if file is not base64', (done) => { store.dispatch('createTempFile', { - tree: store.state, name: 'test', + projectId: 'abcproject', + branchId: 'mybranch', + parent: projectTree, }).then(() => { expect(store.state.editMode).toBeTruthy(); @@ -376,24 +391,14 @@ describe('Multi-file store file actions', () => { }).catch(done.fail); }); - it('does not enter edit mode if file is base64', (done) => { - store.dispatch('createTempFile', { - tree: store.state, - name: 'test', - base64: true, - }).then(() => { - expect(store.state.editMode).toBeFalsy(); - - done(); - }).catch(done.fail); - }); - it('creates flash message is file already exists', (done) => { - store.state.tree.push(file('test', '1', 'blob')); + store.state.trees['abcproject/mybranch'].tree.push(file('test', '1', 'blob')); store.dispatch('createTempFile', { - tree: store.state, name: 'test', + projectId: 'abcproject', + branchId: 'mybranch', + parent: projectTree, }).then(() => { expect(document.querySelector('.flash-alert')).not.toBeNull(); @@ -402,11 +407,13 @@ describe('Multi-file store file actions', () => { }); it('increases level of file', (done) => { - store.state.level = 1; + store.state.trees['abcproject/mybranch'].level = 1; store.dispatch('createTempFile', { - tree: store.state, name: 'test', + projectId: 'abcproject', + branchId: 'mybranch', + parent: projectTree, }).then((f) => { expect(f.level).toBe(2); diff --git a/spec/javascripts/repo/stores/actions/tree_spec.js b/spec/javascripts/repo/stores/actions/tree_spec.js index 2bbc49d5a9f..65351dbb7d9 100644 --- a/spec/javascripts/repo/stores/actions/tree_spec.js +++ b/spec/javascripts/repo/stores/actions/tree_spec.js @@ -1,10 +1,30 @@ import Vue from 'vue'; -import * as urlUtils from '~/lib/utils/url_utility'; -import store from '~/repo/stores'; -import service from '~/repo/services'; +import store from '~/ide/stores'; +import service from '~/ide/services'; import { file, resetStore } from '../../helpers'; describe('Multi-file store tree actions', () => { + let projectTree; + + const basicCallParameters = { + endpoint: 'rootEndpoint', + projectId: 'abcproject', + branch: 'master', + }; + + beforeEach(() => { + store.state.currentProjectId = 'abcproject'; + store.state.currentBranchId = 'master'; + store.state.projects.abcproject = { + web_url: '', + branches: { + master: { + workingReference: '1', + }, + }, + }; + }); + afterEach(() => { resetStore(store); }); @@ -24,38 +44,32 @@ describe('Multi-file store tree actions', () => { submodules: [{ name: 'submodule' }], }), })); - spyOn(history, 'pushState'); - - Object.assign(store.state.endpoints, { - rootEndpoint: 'rootEndpoint', - }); }); it('calls service getTreeData', (done) => { - store.dispatch('getTreeData') - .then(() => { - expect(service.getTreeData).toHaveBeenCalledWith('rootEndpoint'); + store.dispatch('getTreeData', basicCallParameters) + .then(() => { + expect(service.getTreeData).toHaveBeenCalledWith('rootEndpoint'); - done(); - }).catch(done.fail); + done(); + }).catch(done.fail); }); it('adds data into tree', (done) => { - store.dispatch('getTreeData') - .then(Vue.nextTick) + store.dispatch('getTreeData', basicCallParameters) .then(() => { - expect(store.state.tree.length).toBe(3); - expect(store.state.tree[0].type).toBe('tree'); - expect(store.state.tree[1].type).toBe('submodule'); - expect(store.state.tree[2].type).toBe('blob'); + projectTree = store.state.trees['abcproject/master']; + expect(projectTree.tree.length).toBe(3); + expect(projectTree.tree[0].type).toBe('tree'); + expect(projectTree.tree[1].type).toBe('submodule'); + expect(projectTree.tree[2].type).toBe('blob'); done(); }).catch(done.fail); }); it('sets parent tree URL', (done) => { - store.dispatch('getTreeData') - .then(Vue.nextTick) + store.dispatch('getTreeData', basicCallParameters) .then(() => { expect(store.state.parentTreeUrl).toBe('parent_tree_url'); @@ -64,10 +78,9 @@ describe('Multi-file store tree actions', () => { }); it('sets last commit path', (done) => { - store.dispatch('getTreeData') - .then(Vue.nextTick) + store.dispatch('getTreeData', basicCallParameters) .then(() => { - expect(store.state.lastCommitPath).toBe('last_commit_path'); + expect(store.state.trees['abcproject/master'].lastCommitPath).toBe('last_commit_path'); done(); }).catch(done.fail); @@ -76,8 +89,7 @@ describe('Multi-file store tree actions', () => { it('sets root if not currently at root', (done) => { store.state.isInitialRoot = false; - store.dispatch('getTreeData') - .then(Vue.nextTick) + store.dispatch('getTreeData', basicCallParameters) .then(() => { expect(store.state.isInitialRoot).toBeTruthy(); expect(store.state.isRoot).toBeTruthy(); @@ -87,7 +99,7 @@ describe('Multi-file store tree actions', () => { }); it('sets page title', (done) => { - store.dispatch('getTreeData') + store.dispatch('getTreeData', basicCallParameters) .then(() => { expect(document.title).toBe('test'); @@ -95,40 +107,15 @@ describe('Multi-file store tree actions', () => { }).catch(done.fail); }); - it('toggles loading', (done) => { - store.dispatch('getTreeData') - .then(() => { - expect(store.state.loading).toBeTruthy(); - - return Vue.nextTick(); - }) - .then(() => { - expect(store.state.loading).toBeFalsy(); - - done(); - }).catch(done.fail); - }); - - it('calls pushState with endpoint', (done) => { - store.dispatch('getTreeData') - .then(Vue.nextTick) - .then(() => { - expect(history.pushState).toHaveBeenCalledWith(jasmine.anything(), '', 'rootEndpoint'); - - done(); - }).catch(done.fail); - }); - it('calls getLastCommitData if prevLastCommitPath is not null', (done) => { const getLastCommitDataSpy = jasmine.createSpy('getLastCommitData'); const oldGetLastCommitData = store._actions.getLastCommitData; // eslint-disable-line store._actions.getLastCommitData = [getLastCommitDataSpy]; // eslint-disable-line store.state.prevLastCommitPath = 'test'; - store.dispatch('getTreeData') - .then(Vue.nextTick) + store.dispatch('getTreeData', basicCallParameters) .then(() => { - expect(getLastCommitDataSpy).toHaveBeenCalledWith(store.state); + expect(getLastCommitDataSpy).toHaveBeenCalledWith(projectTree); store._actions.getLastCommitData = oldGetLastCommitData; // eslint-disable-line @@ -149,6 +136,8 @@ describe('Multi-file store tree actions', () => { store._actions.getTreeData = [getTreeDataSpy]; // eslint-disable-line tree = { + projectId: 'abcproject', + branchId: 'master', opened: false, tree: [], }; @@ -175,10 +164,11 @@ describe('Multi-file store tree actions', () => { tree, }).then(() => { expect(getTreeDataSpy).toHaveBeenCalledWith({ + projectId: 'abcproject', + branch: 'master', endpoint: 'test', tree, }); - expect(store.state.previousUrl).toBe('test'); done(); }).catch(done.fail); @@ -199,155 +189,29 @@ describe('Multi-file store tree actions', () => { done(); }).catch(done.fail); }); - - it('pushes new state', (done) => { - spyOn(history, 'pushState'); - Object.assign(tree, { - opened: true, - parentTreeUrl: 'testing', - }); - - store.dispatch('toggleTreeOpen', { - endpoint: 'test', - tree, - }).then(() => { - expect(history.pushState).toHaveBeenCalledWith(jasmine.anything(), '', 'testing'); - - done(); - }).catch(done.fail); - }); - }); - - describe('clickedTreeRow', () => { - describe('tree', () => { - let toggleTreeOpenSpy; - let oldToggleTreeOpen; - - beforeEach(() => { - toggleTreeOpenSpy = jasmine.createSpy('toggleTreeOpen'); - - oldToggleTreeOpen = store._actions.toggleTreeOpen; // eslint-disable-line - store._actions.toggleTreeOpen = [toggleTreeOpenSpy]; // eslint-disable-line - }); - - afterEach(() => { - store._actions.toggleTreeOpen = oldToggleTreeOpen; // eslint-disable-line - }); - - it('opens tree', (done) => { - const tree = { - url: 'a', - type: 'tree', - }; - - store.dispatch('clickedTreeRow', tree) - .then(() => { - expect(toggleTreeOpenSpy).toHaveBeenCalledWith({ - endpoint: tree.url, - tree, - }); - - done(); - }).catch(done.fail); - }); - }); - - describe('submodule', () => { - let row; - - beforeEach(() => { - spyOn(urlUtils, 'visitUrl'); - - row = { - url: 'submoduleurl', - type: 'submodule', - loading: false, - }; - }); - - it('toggles loading for row', (done) => { - store.dispatch('clickedTreeRow', row) - .then(() => { - expect(row.loading).toBeTruthy(); - - done(); - }).catch(done.fail); - }); - - it('opens submodule URL', (done) => { - store.dispatch('clickedTreeRow', row) - .then(() => { - expect(urlUtils.visitUrl).toHaveBeenCalledWith('submoduleurl'); - - done(); - }).catch(done.fail); - }); - }); - - describe('blob', () => { - let row; - - beforeEach(() => { - row = { - type: 'blob', - opened: false, - }; - }); - - it('calls getFileData', (done) => { - const getFileDataSpy = jasmine.createSpy('getFileData'); - const oldGetFileData = store._actions.getFileData; // eslint-disable-line - store._actions.getFileData = [getFileDataSpy]; // eslint-disable-line - - store.dispatch('clickedTreeRow', row) - .then(() => { - expect(getFileDataSpy).toHaveBeenCalledWith(row); - - store._actions.getFileData = oldGetFileData; // eslint-disable-line - - done(); - }).catch(done.fail); - }); - - it('calls setFileActive when file is opened', (done) => { - const setFileActiveSpy = jasmine.createSpy('setFileActive'); - const oldSetFileActive = store._actions.setFileActive; // eslint-disable-line - store._actions.setFileActive = [setFileActiveSpy]; // eslint-disable-line - - row.opened = true; - - store.dispatch('clickedTreeRow', row) - .then(() => { - expect(setFileActiveSpy).toHaveBeenCalledWith(row); - - store._actions.setFileActive = oldSetFileActive; // eslint-disable-line - - done(); - }).catch(done.fail); - }); - }); }); describe('createTempTree', () => { - it('creates temp tree', (done) => { - store.dispatch('createTempTree', 'test') - .then(() => { - expect(store.state.tree[0].tempFile).toBeTruthy(); - expect(store.state.tree[0].name).toBe('test'); - expect(store.state.tree[0].type).toBe('tree'); - - done(); - }).catch(done.fail); + beforeEach(() => { + store.state.trees['abcproject/mybranch'] = { + tree: [], + }; + projectTree = store.state.trees['abcproject/mybranch']; }); - it('creates .gitkeep file in temp tree', (done) => { - store.dispatch('createTempTree', 'test') - .then(() => { - expect(store.state.tree[0].tree[0].tempFile).toBeTruthy(); - expect(store.state.tree[0].tree[0].name).toBe('.gitkeep'); + it('creates temp tree', (done) => { + store.dispatch('createTempTree', { + projectId: store.state.currentProjectId, + branchId: store.state.currentBranchId, + name: 'test', + parent: projectTree, + }) + .then(() => { + expect(projectTree.tree[0].name).toBe('test'); + expect(projectTree.tree[0].type).toBe('tree'); - done(); - }).catch(done.fail); + done(); + }).catch(done.fail); }); it('creates new folder inside another tree', (done) => { @@ -357,35 +221,46 @@ describe('Multi-file store tree actions', () => { tree: [], }; - store.state.tree.push(tree); + projectTree.tree.push(tree); - store.dispatch('createTempTree', 'testing/test') - .then(() => { - expect(store.state.tree[0].name).toBe('testing'); - expect(store.state.tree[0].tree[0].tempFile).toBeTruthy(); - expect(store.state.tree[0].tree[0].name).toBe('test'); - expect(store.state.tree[0].tree[0].type).toBe('tree'); + store.dispatch('createTempTree', { + projectId: store.state.currentProjectId, + branchId: store.state.currentBranchId, + name: 'testing/test', + parent: projectTree, + }) + .then(() => { + expect(projectTree.tree[0].name).toBe('testing'); + expect(projectTree.tree[0].tree[0].tempFile).toBeTruthy(); + expect(projectTree.tree[0].tree[0].name).toBe('test'); + expect(projectTree.tree[0].tree[0].type).toBe('tree'); - done(); - }).catch(done.fail); + done(); + }).catch(done.fail); }); it('does not create new tree if already exists', (done) => { const tree = { type: 'tree', name: 'testing', + endpoint: 'test', tree: [], }; - store.state.tree.push(tree); + projectTree.tree.push(tree); - store.dispatch('createTempTree', 'testing/test') - .then(() => { - expect(store.state.tree[0].name).toBe('testing'); - expect(store.state.tree[0].tempFile).toBeUndefined(); + store.dispatch('createTempTree', { + projectId: store.state.currentProjectId, + branchId: store.state.currentBranchId, + name: 'testing/test', + parent: projectTree, + }) + .then(() => { + expect(projectTree.tree[0].name).toBe('testing'); + expect(projectTree.tree[0].tempFile).toBeUndefined(); - done(); - }).catch(done.fail); + done(); + }).catch(done.fail); }); }); @@ -405,12 +280,17 @@ describe('Multi-file store tree actions', () => { }]), })); - store.state.tree.push(file('testing', '1', 'tree')); - store.state.lastCommitPath = 'lastcommitpath'; + store.state.trees['abcproject/mybranch'] = { + tree: [], + }; + + projectTree = store.state.trees['abcproject/mybranch']; + projectTree.tree.push(file('testing', '1', 'tree')); + projectTree.lastCommitPath = 'lastcommitpath'; }); it('calls service with lastCommitPath', (done) => { - store.dispatch('getLastCommitData') + store.dispatch('getLastCommitData', projectTree) .then(() => { expect(service.getTreeLastCommit).toHaveBeenCalledWith('lastcommitpath'); @@ -419,22 +299,22 @@ describe('Multi-file store tree actions', () => { }); it('updates trees last commit data', (done) => { - store.dispatch('getLastCommitData') - .then(Vue.nextTick) + store.dispatch('getLastCommitData', projectTree) + .then(Vue.nextTick) .then(() => { - expect(store.state.tree[0].lastCommit.message).toBe('commit message'); + expect(projectTree.tree[0].lastCommit.message).toBe('commit message'); done(); }).catch(done.fail); }); it('does not update entry if not found', (done) => { - store.state.tree[0].name = 'a'; + projectTree.tree[0].name = 'a'; - store.dispatch('getLastCommitData') + store.dispatch('getLastCommitData', projectTree) .then(Vue.nextTick) .then(() => { - expect(store.state.tree[0].lastCommit.message).not.toBe('commit message'); + expect(projectTree.tree[0].lastCommit.message).not.toBe('commit message'); done(); }).catch(done.fail); diff --git a/spec/javascripts/repo/stores/actions_spec.js b/spec/javascripts/repo/stores/actions_spec.js index 21d87e46216..0b0d34f072a 100644 --- a/spec/javascripts/repo/stores/actions_spec.js +++ b/spec/javascripts/repo/stores/actions_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import * as urlUtils from '~/lib/utils/url_utility'; -import store from '~/repo/stores'; -import service from '~/repo/services'; +import store from '~/ide/stores'; +import service from '~/ide/services'; import { resetStore, file } from '../helpers'; describe('Multi-file store actions', () => { @@ -110,6 +110,7 @@ describe('Multi-file store actions', () => { it('can force closed if there are changed files', (done) => { store.state.editMode = true; + store.state.openFiles.push(file()); store.state.openFiles[0].changed = true; @@ -125,7 +126,6 @@ describe('Multi-file store actions', () => { it('discards file changes', (done) => { const f = file(); store.state.editMode = true; - store.state.tree.push(f); store.state.openFiles.push(f); f.changed = true; @@ -141,8 +141,6 @@ describe('Multi-file store actions', () => { describe('toggleBlobView', () => { it('sets edit mode view if in edit mode', (done) => { - store.state.editMode = true; - store.dispatch('toggleBlobView') .then(() => { expect(store.state.currentBlobView).toBe('repo-editor'); @@ -153,6 +151,8 @@ describe('Multi-file store actions', () => { }); it('sets preview mode view if not in edit mode', (done) => { + store.state.editMode = false; + store.dispatch('toggleBlobView') .then(() => { expect(store.state.currentBlobView).toBe('repo-preview'); @@ -165,9 +165,15 @@ describe('Multi-file store actions', () => { describe('checkCommitStatus', () => { beforeEach(() => { - store.state.project.id = 2; - store.state.currentBranch = 'master'; - store.state.currentRef = '1'; + store.state.currentProjectId = 'abcproject'; + store.state.currentBranchId = 'master'; + store.state.projects.abcproject = { + branches: { + master: { + workingReference: '1', + }, + }, + }; }); it('calls service', (done) => { @@ -177,7 +183,7 @@ describe('Multi-file store actions', () => { store.dispatch('checkCommitStatus') .then(() => { - expect(service.getBranchData).toHaveBeenCalledWith(2, 'master'); + expect(service.getBranchData).toHaveBeenCalledWith('abcproject', 'master'); done(); }) @@ -221,7 +227,17 @@ describe('Multi-file store actions', () => { document.body.innerHTML += '<div class="flash-container"></div>'; - store.state.project.id = 123; + store.state.currentProjectId = 'abcproject'; + store.state.currentBranchId = 'master'; + store.state.projects.abcproject = { + web_url: 'webUrl', + branches: { + master: { + workingReference: '1', + }, + }, + }; + payload = { branch: 'master', }; @@ -248,7 +264,7 @@ describe('Multi-file store actions', () => { it('calls service', (done) => { store.dispatch('commitChanges', { payload, newMr: false }) .then(() => { - expect(service.commit).toHaveBeenCalledWith(123, payload); + expect(service.commit).toHaveBeenCalledWith('abcproject', payload); done(); }).catch(done.fail); @@ -284,17 +300,6 @@ describe('Multi-file store actions', () => { }).catch(done.fail); }); - it('toggles edit mode', (done) => { - store.state.editMode = true; - - store.dispatch('commitChanges', { payload, newMr: false }) - .then(() => { - expect(store.state.editMode).toBeFalsy(); - - done(); - }).catch(done.fail); - }); - it('closes all files', (done) => { store.state.openFiles.push(file()); store.state.openFiles[0].opened = true; @@ -317,23 +322,12 @@ describe('Multi-file store actions', () => { }).catch(done.fail); }); - it('updates commit ref', (done) => { - store.dispatch('commitChanges', { payload, newMr: false }) - .then(() => { - expect(store.state.currentRef).toBe('123456'); - - done(); - }).catch(done.fail); - }); - it('redirects to new merge request page', (done) => { spyOn(urlUtils, 'visitUrl'); - store.state.endpoints.newMergeRequestUrl = 'newMergeRequestUrl?branch='; - store.dispatch('commitChanges', { payload, newMr: true }) .then(() => { - expect(urlUtils.visitUrl).toHaveBeenCalledWith('newMergeRequestUrl?branch=master'); + expect(urlUtils.visitUrl).toHaveBeenCalledWith('webUrl/merge_requests/new?merge_request%5Bsource_branch%5D=master'); done(); }).catch(done.fail); @@ -363,15 +357,30 @@ describe('Multi-file store actions', () => { }); describe('createTempEntry', () => { + beforeEach(() => { + store.state.trees['abcproject/mybranch'] = { + tree: [], + }; + store.state.projects.abcproject = { + web_url: '', + }; + }); + it('creates a temp tree', (done) => { + const projectTree = store.state.trees['abcproject/mybranch']; + store.dispatch('createTempEntry', { + projectId: 'abcproject', + branchId: 'mybranch', + parent: projectTree, name: 'test', type: 'tree', }) .then(() => { - expect(store.state.tree.length).toBe(1); - expect(store.state.tree[0].tempFile).toBeTruthy(); - expect(store.state.tree[0].type).toBe('tree'); + const baseTree = projectTree.tree; + expect(baseTree.length).toBe(1); + expect(baseTree[0].tempFile).toBeTruthy(); + expect(baseTree[0].type).toBe('tree'); done(); }) @@ -379,14 +388,20 @@ describe('Multi-file store actions', () => { }); it('creates temp file', (done) => { + const projectTree = store.state.trees['abcproject/mybranch']; + store.dispatch('createTempEntry', { + projectId: 'abcproject', + branchId: 'mybranch', + parent: projectTree, name: 'test', type: 'blob', }) .then(() => { - expect(store.state.tree.length).toBe(1); - expect(store.state.tree[0].tempFile).toBeTruthy(); - expect(store.state.tree[0].type).toBe('blob'); + const baseTree = projectTree.tree; + expect(baseTree.length).toBe(1); + expect(baseTree[0].tempFile).toBeTruthy(); + expect(baseTree[0].type).toBe('blob'); done(); }) diff --git a/spec/javascripts/repo/stores/getters_spec.js b/spec/javascripts/repo/stores/getters_spec.js index 952b8ec3a59..d0d5934f29a 100644 --- a/spec/javascripts/repo/stores/getters_spec.js +++ b/spec/javascripts/repo/stores/getters_spec.js @@ -1,5 +1,5 @@ -import * as getters from '~/repo/stores/getters'; -import state from '~/repo/stores/state'; +import * as getters from '~/ide/stores/getters'; +import state from '~/ide/stores/state'; import { file } from '../helpers'; describe('Multi-file store getters', () => { @@ -9,20 +9,6 @@ describe('Multi-file store getters', () => { localState = state(); }); - describe('treeList', () => { - it('returns flat tree list', () => { - localState.tree.push(file('1')); - localState.tree[0].tree.push(file('2')); - localState.tree[0].tree[0].tree.push(file('3')); - - const treeList = getters.treeList(localState); - - expect(treeList.length).toBe(3); - expect(treeList[1].name).toBe(localState.tree[0].tree[0].name); - expect(treeList[2].name).toBe(localState.tree[0].tree[0].tree[0].name); - }); - }); - describe('changedFiles', () => { it('returns a list of changed opened files', () => { localState.openFiles.push(file()); @@ -49,7 +35,7 @@ describe('Multi-file store getters', () => { localState.openFiles.push(file()); localState.openFiles.push(file('active')); - expect(getters.activeFile(localState)).toBeUndefined(); + expect(getters.activeFile(localState)).toBeNull(); }); }); @@ -67,18 +53,6 @@ describe('Multi-file store getters', () => { }); }); - describe('isCollapsed', () => { - it('returns true if state has open files', () => { - localState.openFiles.push(file()); - - expect(getters.isCollapsed(localState)).toBeTruthy(); - }); - - it('returns false if state has no open files', () => { - expect(getters.isCollapsed(localState)).toBeFalsy(); - }); - }); - describe('canEditFile', () => { beforeEach(() => { localState.onTopOfBranch = true; @@ -109,12 +83,6 @@ describe('Multi-file store getters', () => { expect(getters.canEditFile(localState)).toBeFalsy(); }); - - it('returns false if user can commit but on a branch', () => { - localState.onTopOfBranch = false; - - expect(getters.canEditFile(localState)).toBeFalsy(); - }); }); describe('modifiedFiles', () => { diff --git a/spec/javascripts/repo/stores/mutations/branch_spec.js b/spec/javascripts/repo/stores/mutations/branch_spec.js index 3c06794d5e3..a7167537ef2 100644 --- a/spec/javascripts/repo/stores/mutations/branch_spec.js +++ b/spec/javascripts/repo/stores/mutations/branch_spec.js @@ -1,5 +1,5 @@ -import mutations from '~/repo/stores/mutations/branch'; -import state from '~/repo/stores/state'; +import mutations from '~/ide/stores/mutations/branch'; +import state from '~/ide/stores/state'; describe('Multi-file store branch mutations', () => { let localState; @@ -12,7 +12,7 @@ describe('Multi-file store branch mutations', () => { it('sets currentBranch', () => { mutations.SET_CURRENT_BRANCH(localState, 'master'); - expect(localState.currentBranch).toBe('master'); + expect(localState.currentBranchId).toBe('master'); }); }); }); diff --git a/spec/javascripts/repo/stores/mutations/file_spec.js b/spec/javascripts/repo/stores/mutations/file_spec.js index 2f2835dde1f..947a60587df 100644 --- a/spec/javascripts/repo/stores/mutations/file_spec.js +++ b/spec/javascripts/repo/stores/mutations/file_spec.js @@ -1,5 +1,5 @@ -import mutations from '~/repo/stores/mutations/file'; -import state from '~/repo/stores/state'; +import mutations from '~/ide/stores/mutations/file'; +import state from '~/ide/stores/state'; import { file } from '../../helpers'; describe('Multi-file store file mutations', () => { diff --git a/spec/javascripts/repo/stores/mutations/tree_spec.js b/spec/javascripts/repo/stores/mutations/tree_spec.js index 1c76cfed9c8..cf1248ba28b 100644 --- a/spec/javascripts/repo/stores/mutations/tree_spec.js +++ b/spec/javascripts/repo/stores/mutations/tree_spec.js @@ -1,5 +1,5 @@ -import mutations from '~/repo/stores/mutations/tree'; -import state from '~/repo/stores/state'; +import mutations from '~/ide/stores/mutations/tree'; +import state from '~/ide/stores/state'; import { file } from '../../helpers'; describe('Multi-file store tree mutations', () => { diff --git a/spec/javascripts/repo/stores/mutations_spec.js b/spec/javascripts/repo/stores/mutations_spec.js index d1c9885e01d..5fd8ad94972 100644 --- a/spec/javascripts/repo/stores/mutations_spec.js +++ b/spec/javascripts/repo/stores/mutations_spec.js @@ -1,5 +1,5 @@ -import mutations from '~/repo/stores/mutations'; -import state from '~/repo/stores/state'; +import mutations from '~/ide/stores/mutations'; +import state from '~/ide/stores/state'; import { file } from '../helpers'; describe('Multi-file store mutations', () => { @@ -65,11 +65,11 @@ describe('Multi-file store mutations', () => { it('toggles editMode', () => { mutations.TOGGLE_EDIT_MODE(localState); - expect(localState.editMode).toBeTruthy(); + expect(localState.editMode).toBeFalsy(); mutations.TOGGLE_EDIT_MODE(localState); - expect(localState.editMode).toBeFalsy(); + expect(localState.editMode).toBeTruthy(); }); }); @@ -85,14 +85,6 @@ describe('Multi-file store mutations', () => { }); }); - describe('SET_COMMIT_REF', () => { - it('sets currentRef', () => { - mutations.SET_COMMIT_REF(localState, '123'); - - expect(localState.currentRef).toBe('123'); - }); - }); - describe('SET_ROOT', () => { it('sets isRoot & initialRoot', () => { mutations.SET_ROOT(localState, true); @@ -107,11 +99,27 @@ describe('Multi-file store mutations', () => { }); }); - describe('SET_PREVIOUS_URL', () => { - it('sets previousUrl', () => { - mutations.SET_PREVIOUS_URL(localState, 'testing'); + describe('SET_LEFT_PANEL_COLLAPSED', () => { + it('sets left panel collapsed', () => { + mutations.SET_LEFT_PANEL_COLLAPSED(localState, true); + + expect(localState.leftPanelCollapsed).toBeTruthy(); + + mutations.SET_LEFT_PANEL_COLLAPSED(localState, false); + + expect(localState.leftPanelCollapsed).toBeFalsy(); + }); + }); + + describe('SET_RIGHT_PANEL_COLLAPSED', () => { + it('sets right panel collapsed', () => { + mutations.SET_RIGHT_PANEL_COLLAPSED(localState, true); + + expect(localState.rightPanelCollapsed).toBeTruthy(); + + mutations.SET_RIGHT_PANEL_COLLAPSED(localState, false); - expect(localState.previousUrl).toBe('testing'); + expect(localState.rightPanelCollapsed).toBeFalsy(); }); }); }); diff --git a/spec/javascripts/repo/stores/utils_spec.js b/spec/javascripts/repo/stores/utils_spec.js index 37287c587d7..89745a2029e 100644 --- a/spec/javascripts/repo/stores/utils_spec.js +++ b/spec/javascripts/repo/stores/utils_spec.js @@ -1,4 +1,6 @@ -import * as utils from '~/repo/stores/utils'; +import * as utils from '~/ide/stores/utils'; +import state from '~/ide/stores/state'; +import { file } from '../helpers'; describe('Multi-file store utils', () => { describe('setPageTitle', () => { @@ -9,13 +11,28 @@ describe('Multi-file store utils', () => { }); }); - describe('pushState', () => { - it('calls history.pushState', () => { - spyOn(history, 'pushState'); + describe('treeList', () => { + let localState; - utils.pushState('test'); + beforeEach(() => { + localState = state(); + }); + + it('returns flat tree list', () => { + localState.trees = []; + localState.trees['abcproject/mybranch'] = { + tree: [], + }; + const baseTree = localState.trees['abcproject/mybranch'].tree; + baseTree.push(file('1')); + baseTree[0].tree.push(file('2')); + baseTree[0].tree[0].tree.push(file('3')); + + const treeList = utils.treeList(localState, 'abcproject/mybranch'); - expect(history.pushState).toHaveBeenCalledWith({ url: 'test' }, '', 'test'); + expect(treeList.length).toBe(3); + expect(treeList[1].name).toBe(baseTree[0].tree[0].name); + expect(treeList[2].name).toBe(baseTree[0].tree[0].tree[0].name); }); }); @@ -52,10 +69,10 @@ describe('Multi-file store utils', () => { }); describe('findIndexOfFile', () => { - let state; + let localState; beforeEach(() => { - state = [{ + localState = [{ path: '1', }, { path: '2', @@ -63,7 +80,7 @@ describe('Multi-file store utils', () => { }); it('finds in the index of an entry by path', () => { - const index = utils.findIndexOfFile(state, { + const index = utils.findIndexOfFile(localState, { path: '2', }); @@ -72,10 +89,10 @@ describe('Multi-file store utils', () => { }); describe('findEntry', () => { - let state; + let localState; beforeEach(() => { - state = { + localState = { tree: [{ type: 'tree', name: 'test', @@ -87,14 +104,14 @@ describe('Multi-file store utils', () => { }); it('returns an entry found by name', () => { - const foundEntry = utils.findEntry(state, 'tree', 'test'); + const foundEntry = utils.findEntry(localState.tree, 'tree', 'test'); expect(foundEntry.type).toBe('tree'); expect(foundEntry.name).toBe('test'); }); it('returns undefined when no entry found', () => { - const foundEntry = utils.findEntry(state, 'blob', 'test'); + const foundEntry = utils.findEntry(localState.tree, 'blob', 'test'); expect(foundEntry).toBeUndefined(); }); diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js index d5754aaa9e7..fdead874209 100644 --- a/spec/javascripts/vue_shared/components/commit_spec.js +++ b/spec/javascripts/vue_shared/components/commit_spec.js @@ -10,7 +10,7 @@ describe('Commit component', () => { CommitComponent = Vue.extend(commitComp); }); - it('should render a code-fork icon if it does not represent a tag', () => { + it('should render a fork icon if it does not represent a tag', () => { component = new CommitComponent({ propsData: { tag: false, @@ -30,7 +30,7 @@ describe('Commit component', () => { }, }).$mount(); - expect(component.$el.querySelector('.icon-container i').classList).toContain('fa-code-fork'); + expect(component.$el.querySelector('.icon-container').children).toContain('svg'); }); describe('Given all the props', () => { diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js index b0b78e34e0f..1465ef5855f 100644 --- a/spec/javascripts/vue_shared/components/table_pagination_spec.js +++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js @@ -19,6 +19,22 @@ describe('Pagination component', () => { }); describe('render', () => { + it('should not render anything', () => { + component = mountComponet({ + pageInfo: { + nextPage: 1, + page: 1, + perPage: 20, + previousPage: null, + total: 15, + totalPages: 1, + }, + change: spy, + }); + + expect(component.$el.innerHTML).not.toBeDefined(); + }); + describe('prev button', () => { it('should be disabled and non clickable', () => { component = mountComponet({ diff --git a/spec/lib/gitlab/metrics/method_call_spec.rb b/spec/lib/gitlab/metrics/method_call_spec.rb index 78767d06462..41a9d1d9c90 100644 --- a/spec/lib/gitlab/metrics/method_call_spec.rb +++ b/spec/lib/gitlab/metrics/method_call_spec.rb @@ -96,14 +96,17 @@ describe Gitlab::Metrics::MethodCall do describe '#to_metric' do it 'returns a Metric instance' do + expect(method_call).to receive(:real_time).and_return(4.0001) + expect(method_call).to receive(:cpu_time).and_return(3.0001) + method_call.measure { 'foo' } metric = method_call.to_metric expect(metric).to be_an_instance_of(Gitlab::Metrics::Metric) expect(metric.series).to eq('rails_method_calls') - expect(metric.values[:duration]).to be_a_kind_of(Numeric) - expect(metric.values[:cpu_duration]).to be_a_kind_of(Numeric) + expect(metric.values[:duration]).to eq(4000) + expect(metric.values[:cpu_duration]).to eq(3000) expect(metric.values[:call_count]).to be_an(Integer) expect(metric.tags).to eq({ method: 'Foo#bar' }) @@ -116,13 +119,13 @@ describe Gitlab::Metrics::MethodCall do end it 'returns false when the total call time is not above the threshold' do - expect(method_call).to receive(:real_time).and_return(9) + expect(method_call).to receive(:real_time).and_return(0.009) expect(method_call.above_threshold?).to eq(false) end it 'returns true when the total call time is above the threshold' do - expect(method_call).to receive(:real_time).and_return(9000) + expect(method_call).to receive(:real_time).and_return(9) expect(method_call.above_threshold?).to eq(true) end diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb index 4d94d8705fb..14afcdf5daa 100644 --- a/spec/lib/gitlab/metrics/system_spec.rb +++ b/spec/lib/gitlab/metrics/system_spec.rb @@ -29,19 +29,19 @@ describe Gitlab::Metrics::System do describe '.cpu_time' do it 'returns a Fixnum' do - expect(described_class.cpu_time).to be_an(Integer) + expect(described_class.cpu_time).to be_an(Float) end end describe '.real_time' do it 'returns a Fixnum' do - expect(described_class.real_time).to be_an(Integer) + expect(described_class.real_time).to be_an(Float) end end describe '.monotonic_time' do - it 'returns a Fixnum' do - expect(described_class.monotonic_time).to be_an(Integer) + it 'returns a Float' do + expect(described_class.monotonic_time).to be_an(Float) end end end diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb index e44a7c23452..a958baab44f 100644 --- a/spec/lib/gitlab/search_results_spec.rb +++ b/spec/lib/gitlab/search_results_spec.rb @@ -51,6 +51,17 @@ describe Gitlab::SearchResults do expect(results.objects('merge_requests')).to include merge_request_2 end + + it 'includes project filter by default' do + expect(results).to receive(:project_ids_relation).and_call_original + results.objects('merge_requests') + end + + it 'it skips project filter if default is used' do + allow(results).to receive(:default_project_filter).and_return(true) + expect(results).not_to receive(:project_ids_relation) + results.objects('merge_requests') + end end it 'does not list issues on private projects' do diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb index a45a6088831..7c66c98231b 100644 --- a/spec/models/identity_spec.rb +++ b/spec/models/identity_spec.rb @@ -44,4 +44,31 @@ describe Identity do end end end + + context 'callbacks' do + context 'before_save' do + describe 'normalizes extern uid' do + let!(:ldap_identity) { create(:identity, provider: 'ldapmain', extern_uid: 'uid=john smith,ou=people,dc=example,dc=com') } + + it 'if extern_uid changes' do + expect(ldap_identity).not_to receive(:ensure_normalized_extern_uid) + ldap_identity.save + end + + it 'if current_uid is nil' do + expect(ldap_identity).to receive(:ensure_normalized_extern_uid) + + ldap_identity.update(extern_uid: nil) + + expect(ldap_identity.extern_uid).to be_nil + end + + it 'if extern_uid changed and not nil' do + ldap_identity.update(extern_uid: 'uid=john1,ou=PEOPLE,dc=example,dc=com') + + expect(ldap_identity.extern_uid).to eq 'uid=john1,ou=people,dc=example,dc=com' + end + end + end + end end diff --git a/spec/serializers/merge_request_serializer_spec.rb b/spec/serializers/merge_request_serializer_spec.rb index 1ad974c774b..b259cb92962 100644 --- a/spec/serializers/merge_request_serializer_spec.rb +++ b/spec/serializers/merge_request_serializer_spec.rb @@ -36,8 +36,8 @@ describe MergeRequestSerializer do context 'no serializer' do let(:serializer) { nil } - it 'raises an error' do - expect { json_entity }.to raise_error(NoMethodError) + it 'falls back to the widget entity' do + expect(json_entity).to match_schema('entities/merge_request_widget') end end end diff --git a/yarn.lock b/yarn.lock index 55d0d33c9f2..358a1baec42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -54,9 +54,9 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@gitlab-org/gitlab-svgs@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.3.0.tgz#07f2aa75d6e0e857eaa20c38a3bad7e6c22c420c" +"@gitlab-org/gitlab-svgs@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.4.0.tgz#83c0a76485c1378babf2e83456b4d2442efa98e8" "@types/jquery@^2.0.40": version "2.0.48" @@ -6528,6 +6528,10 @@ vue-resource@^1.3.4: dependencies: got "^7.0.0" +vue-router@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9" + vue-style-loader@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-3.0.3.tgz#623658f81506aef9d121cdc113a4f5c9cac32df7" |