diff --git a/services/web/app/views/project/editor.jade b/services/web/app/views/project/editor.jade index 1fba8d539e..091c9d64d0 100644 --- a/services/web/app/views/project/editor.jade +++ b/services/web/app/views/project/editor.jade @@ -8,7 +8,7 @@ block vars block scripts //- Only use the native bootstrap on the editor page, //- since we use the Angular-based bootstrap elsewhere. - script(src=jsPath+'libs/bootstrap-3.1.1.js') + //- script(src=jsPath+'libs/bootstrap-3.1.1.js') script(src=jsPath+'libs/jquery-layout.js') script(src=jsPath+'libs/jquery.storage.js') @@ -35,17 +35,32 @@ block content include ./editor/left-menu - include ./editor/header - - include ./editor/share - - #ide-body(ng-cloak, layout="main", ng-hide="state.loading") - .ui-layout-west - include ./editor/file-tree - + #chat-wrapper( + layout="chat", + spacing-open="12", + spacing-closed="0", + initial-size-east="250", + init-closed-east="true", + open-east="ui.chatOpen", + ng-hide="state.loading", + ng-cloak + ) .ui-layout-center - include ./editor/editor - include ./editor/track-changes + include ./editor/header + + include ./editor/share + + #ide-body(ng-cloak, layout="main", ng-hide="state.loading", resize-on="layout:chat:resize") + .ui-layout-west + include ./editor/file-tree + + .ui-layout-center + include ./editor/editor + include ./editor/track-changes + + .ui-layout-east + | Chat! + script(src='/socket.io/socket.io.js') diff --git a/services/web/app/views/project/editor/editor.jade b/services/web/app/views/project/editor/editor.jade index 2836480468..117854654a 100644 --- a/services/web/app/views/project/editor/editor.jade +++ b/services/web/app/views/project/editor/editor.jade @@ -2,6 +2,8 @@ div.full-size( ng-show="ui.view == 'editor'" layout="pdf" resize-on="layout:main:resize" + resize-proportionally="true" + initial-size-east="'50%'" ) .ui-layout-center .loading-panel(ng-show="!editor.sharejs_doc || editor.opening") diff --git a/services/web/app/views/project/editor/header.jade b/services/web/app/views/project/editor/header.jade index a6859981ee..b9184fca4f 100644 --- a/services/web/app/views/project/editor/header.jade +++ b/services/web/app/views/project/editor/header.jade @@ -29,5 +29,12 @@ header.toolbar.toolbar-header(ng-cloak, ng-hide="state.loading") tooltip-placement="bottom" ) i.fa.fa-fw.fa-history - a.btn.btn-full-height(href='#', tooltip="Chat", tooltip-placement="bottom") + a.btn.btn-full-height( + href, + tooltip="Chat", + tooltip-placement="bottom", + ng-class="{ active: ui.chatOpen }", + ng-click="toggleChat()", + ng-controller="ChatButtonController" + ) i.fa.fa-fw.fa-comment \ No newline at end of file diff --git a/services/web/app/views/project/editor/left-menu.jade b/services/web/app/views/project/editor/left-menu.jade index b15afdeace..f4e9a23179 100644 --- a/services/web/app/views/project/editor/left-menu.jade +++ b/services/web/app/views/project/editor/left-menu.jade @@ -2,76 +2,120 @@ aside#left-menu.full-size( ng-class="{ 'shown': ui.leftMenuShown }" ng-cloak ) + h4 Download + + ul.unformatted-list.nav.nav-downloads + li + a( + ng-href="/project/{{project_id}}/download/zip" + target="_blank" + ) + i.fa.fa-file-archive-o.fa-2x + br + | Source + li + a( + ng-href="/project/{{project_id}}/{{pdf.url}}" + target="_blank" + ng-if="pdf.url" + ) + i.fa.fa-file-pdf-o.fa-2x + br + | PDF + div.pdf-disabled( + ng-if="!pdf.url" + tooltip="Please compile your project before downloading the PDF" + tooltip-placement="bottom" + ) + i.fa.fa-file-pdf-o.fa-2x + br + | PDF + h4 Settings - form(ng-controller="SettingsController") - .form-controls - label(for="compiler") Compiler - select.form-control( - name="compiler" - ng-model="project.compiler" - ) - option(value='pdflatex') pdfLaTeX - option(value='latex') LaTeX - option(value='xelatex') XeLaTeX - option(value='lualatex') LuaLaTeX + form.settings(ng-controller="SettingsController") + .containter-fluid + .form-controls + .row + label.col-md-6(for="compiler") Compiler + .col-md-6 + select.form-control( + name="compiler" + ng-model="project.compiler" + ) + option(value='pdflatex') pdfLaTeX + option(value='latex') LaTeX + option(value='xelatex') XeLaTeX + option(value='lualatex') LuaLaTeX - .form-controls - label(for="spellCheckLanguage") Spell Check - select.form-control( - name="spellCheckLanguage" - ng-model="project.spellCheckLanguage" - ) - option(value="") Off - optgroup(label="Language") - for language in languages - option( - value=language.code - )= language.name + .form-controls + .row + label.col-md-6(for="spellCheckLanguage") Spell Check + .col-md-6 + select.form-control( + name="spellCheckLanguage" + ng-model="project.spellCheckLanguage" + ) + option(value="") Off + optgroup(label="Language") + for language in languages + option( + value=language.code + )= language.name - .form-controls - label(for="autoComplete") Auto-Complete - input.form-control( - type="checkbox" - name="autoComplete" - ng-model="settings.autoComplete" - ) + .form-controls + .row + label.col-md-6(for="autoComplete") Auto-Complete + .col-md-6 + select.form-control( + name="autoComplete" + ng-model="settings.autoComplete" + ng-options="o.v as o.n for o in [{ n: 'On', v: true }, { n: 'Off', v: false }]" + ) - .form-controls - label(for="theme") Theme - select.form-control( - name="theme" - ng-model="settings.theme" - ) - each theme in themes - option(value=theme) #{theme} - - .form-controls - label(for="mode") Keybindings - select.form-control( - name="mode" - ng-model="settings.mode" - ) - option(value='default') None - option(value='vim') Vim - option(value='emacs') Emacs + .form-controls + .row + label.col-md-6(for="theme") Theme + .col-md-6 + select.form-control( + name="theme" + ng-model="settings.theme" + ) + each theme in themes + option(value=theme) #{theme} - .form-controls - label(for="fontSize") Font Size - select.form-control( - name="fontSize" - ng-model="settings.fontSize" - ) - each size in ['10','11','12','13','14','16','20','24'] - option(value=size) #{size}px + .form-controls + .row + label.col-md-6(for="mode") Keybindings + .col-md-6 + select.form-control( + name="mode" + ng-model="settings.mode" + ) + option(value='default') None + option(value='vim') Vim + option(value='emacs') Emacs - .form-controls - label(for="pdfViewer") PDF Viewer - select.form-control( - name="pdfViewer" - ng-model="settings.pdfViewer" - ) - option(value="pdfjs") Built-In - option(value="native") Native + .form-controls + .row + label.col-md-6(for="fontSize") Font Size + .col-md-6 + select.form-control( + name="fontSize" + ng-model="settings.fontSize" + ) + each size in ['10','11','12','13','14','16','20','24'] + option(value=size) #{size}px + + .form-controls + .row + label.col-md-6(for="pdfViewer") PDF Viewer + .col-md-6 + select.form-control( + name="pdfViewer" + ng-model="settings.pdfViewer" + ) + option(value="pdfjs") Built-In + option(value="native") Native #left-menu-mask( ng-show="ui.leftMenuShown", diff --git a/services/web/public/coffee/app/ide.coffee b/services/web/public/coffee/app/ide.coffee index 3a10089306..54abf30703 100644 --- a/services/web/public/coffee/app/ide.coffee +++ b/services/web/public/coffee/app/ide.coffee @@ -8,6 +8,7 @@ define [ "ide/pdf/PdfManager" "ide/settings/index" "ide/share/index" + "ide/chat/index" "ide/directives/layout" "ide/services/ide" "directives/focus" @@ -40,6 +41,7 @@ define [ $scope.ui = { leftMenuShown: false view: "editor" + chatOpen: false } $scope.user = window.user $scope.settings = window.userSettings diff --git a/services/web/public/coffee/app/ide/chat/controllers/ChatButtonController.coffee b/services/web/public/coffee/app/ide/chat/controllers/ChatButtonController.coffee new file mode 100644 index 0000000000..74e6d31ee3 --- /dev/null +++ b/services/web/public/coffee/app/ide/chat/controllers/ChatButtonController.coffee @@ -0,0 +1,7 @@ +define [ + "base" +], (App) -> + App.controller "ChatButtonController", ["$scope", ($scope) -> + $scope.toggleChat = () -> + $scope.ui.chatOpen = !$scope.ui.chatOpen + ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/chat/index.coffee b/services/web/public/coffee/app/ide/chat/index.coffee new file mode 100644 index 0000000000..2f6b566932 --- /dev/null +++ b/services/web/public/coffee/app/ide/chat/index.coffee @@ -0,0 +1,3 @@ +define [ + "ide/chat/controllers/ChatButtonController" +], () -> \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/directives/layout.coffee b/services/web/public/coffee/app/ide/directives/layout.coffee index 15a982cade..af746f30a7 100644 --- a/services/web/public/coffee/app/ide/directives/layout.coffee +++ b/services/web/public/coffee/app/ide/directives/layout.coffee @@ -1,46 +1,101 @@ define [ "base" ], (App) -> - App.directive "layout", () -> + App.directive "layout", ["$parse", ($parse) -> return { - link: (scope, element, attrs) -> - name = attrs.layout + compile: () -> + pre: (scope, element, attrs) -> + name = attrs.layout - options = - spacing_open: 24 - spacing_closed: 24 - onresize: () => - onResize() - maskIframesOnResize: scope.$eval( - attrs.maskIframesOnResize or "false" - ) + if attrs.spacingOpen? + spacingOpen = parseInt(attrs.spacingOpen, 10) + else + spacingOpen = 24 - onResize = () -> - state = element.layout().readState() - scope.$broadcast "layout:#{name}:resize", state - repositionControls() + if attrs.spacingClosed? + spacingClosed = parseInt(attrs.spacingClosed, 10) + else + spacingClosed = 24 - # Restore previously recorded state - if (state = $.localStorage("layout.#{name}"))? - options.west = state.west - options.east = state.east + options = + spacing_open: spacingOpen + spacing_closed: spacingClosed + slidable: false + onresize: () => + onInternalResize() + maskIframesOnResize: scope.$eval( + attrs.maskIframesOnResize or "false" + ) + east: + size: scope.$eval(attrs.initialSizeEast) + initClosed: scope.$eval(attrs.initClosedEast) + west: + size: scope.$eval(attrs.initialSizeEast) + initClosed: scope.$eval(attrs.initClosedWest) - element.layout options - element.layout().resizeAll() + # Restore previously recorded state + if (state = $.localStorage("layout.#{name}"))? + options.west = state.west + options.east = state.east - if attrs.resizeOn? - scope.$on attrs.resizeOn, () -> element.layout().resizeAll() + # Someone moved the resizer + onInternalResize = () -> + state = element.layout().readState() + scope.$broadcast "layout:#{name}:resize", state + repositionControls() + resetOpenStates() - # Save state when exiting - $(window).unload () -> - $.localStorage("layout.#{name}", element.layout().readState()) + oldWidth = element.width() + # Something resized our parent element + onExternalResize = () -> + console.log "EXTERNAL RESIOZE", name, attrs.resizeProportionally + if attrs.resizeProportionally? and scope.$eval(attrs.resizeProportionally) + eastState = element.layout().readState().east + if eastState? + newInternalWidth = eastState.size / oldWidth * element.width() + oldWidth = element.width() + element.layout().sizePane("east", newInternalWidth) + return + + element.layout().resizeAll() - repositionControls = () -> - state = element.layout().readState() - if state.east? - element.find(".ui-layout-resizer-controls").css({ - position: "absolute" - right: state.east.size - "z-index": 10 - }) - } \ No newline at end of file + element.layout options + element.layout().resizeAll() + + if attrs.resizeOn? + scope.$on attrs.resizeOn, () -> onExternalResize() + + # Save state when exiting + $(window).unload () -> + $.localStorage("layout.#{name}", element.layout().readState()) + + repositionControls = () -> + state = element.layout().readState() + if state.east? + element.find("> .ui-layout-resizer-controls").css({ + position: "absolute" + right: state.east.size + "z-index": 10 + }) + + resetOpenStates = () -> + state = element.layout().readState() + if attrs.openEast? + openEast = $parse(attrs.openEast) + openEast.assign(scope, !state.east.initClosed) + + if attrs.openEast? + scope.$watch attrs.openEast, (value, oldValue) -> + console.log "Open East", value, oldValue + if value? and value != oldValue + if value + element.layout().open("east") + else + element.layout().close("east") + setTimeout () -> + scope.$digest() + , 0 + + resetOpenStates() + } + ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/editor/EditorManager.coffee b/services/web/public/coffee/app/ide/editor/EditorManager.coffee index ed331f4583..93b72212bf 100644 --- a/services/web/public/coffee/app/ide/editor/EditorManager.coffee +++ b/services/web/public/coffee/app/ide/editor/EditorManager.coffee @@ -9,7 +9,7 @@ define [ last_updated: null open_doc_id: null opening: true - cursorPosition: null + cursorPosition: {} gotoLine: null } diff --git a/services/web/public/coffee/app/ide/editor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/app/ide/editor/auto-complete/AutoCompleteManager.coffee index a5a13b07b5..20f451e040 100644 --- a/services/web/public/coffee/app/ide/editor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/app/ide/editor/auto-complete/AutoCompleteManager.coffee @@ -29,19 +29,23 @@ define [ constructor: (@$scope, @editor) -> @suggestionManager = new SuggestionManager() - insertMatch = Autocomplete::insertMatch - editor = @editor - Autocomplete::insertMatch = (data) -> - pos = editor.getCursorPosition() - range = new Range(pos.row, pos.column, pos.row, pos.column + 1) - nextChar = editor.session.getTextRange(range) + if !Autocomplete::_insertMatch? + # Only override this once since it's global but we may create multiple + # autocomplete handlers + Autocomplete::_insertMatch = Autocomplete::insertMatch + Autocomplete::insertMatch = (data) -> + pos = editor.getCursorPosition() + range = new Range(pos.row, pos.column, pos.row, pos.column + 1) + nextChar = editor.session.getTextRange(range) - # If we are in \begin{it|}, then we need to remove the trailing } - # since it will be adding in with the autocomplete of \begin{item}... - if this.completions.filterText.match(/^\\begin\{/) and nextChar == "}" - editor.session.remove(range) - - insertMatch.call editor.completer, data + console.log "INSERT MATCH", this + + # If we are in \begin{it|}, then we need to remove the trailing } + # since it will be adding in with the autocomplete of \begin{item}... + if this.completions.filterText.match(/^\\begin\{/) and nextChar == "}" + editor.session.remove(range) + + Autocomplete::_insertMatch.call this, data @$scope.$watch "autoComplete", (autocomplete) => console.log "autocomplete change", autocomplete diff --git a/services/web/public/coffee/app/ide/editor/cursor-position/CursorPositionManager.coffee b/services/web/public/coffee/app/ide/editor/cursor-position/CursorPositionManager.coffee index be90f8fcec..27f7045d77 100644 --- a/services/web/public/coffee/app/ide/editor/cursor-position/CursorPositionManager.coffee +++ b/services/web/public/coffee/app/ide/editor/cursor-position/CursorPositionManager.coffee @@ -13,9 +13,9 @@ define [], () -> @editor.on "changeSelection", () => cursor = @editor.getCursorPosition() - console.log "Updating cursor position", cursor @$scope.$apply () => - @$scope.cursorPosition = cursor + if @$scope.cursorPosition? + @$scope.cursorPosition = cursor @$scope.$watch "gotoLine", (value) => console.log "Going to line", value diff --git a/services/web/public/coffee/app/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/app/ide/editor/directives/aceEditor.coffee index d7235ce771..423f9a1307 100644 --- a/services/web/public/coffee/app/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/app/ide/editor/directives/aceEditor.coffee @@ -10,7 +10,7 @@ define [ "ace/keyboard/emacs" "ace/mode/latex" "ace/edit_session" -], (App, Ace, UndoManager, AutoCompleteManager, SpellCheckManager, AnnotationsManager, CursorPositionManager) -> +], (App, Ace, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager) -> LatexMode = require("ace/mode/latex").Mode EditSession = require('ace/edit_session').EditSession @@ -42,12 +42,14 @@ define [ else @$originalApply(fn); - window.editor = editor = Ace.edit(element.find(".ace-editor-body")[0]) + editor = Ace.edit(element.find(".ace-editor-body")[0]) + window.editors ||= [] + window.editors.push editor autoCompleteManager = new AutoCompleteManager(scope, editor, element) spellCheckManager = new SpellCheckManager(scope, editor, element) undoManager = new UndoManager(scope, editor, element) - annotationsManager = new AnnotationsManager(scope, editor, element) + highlightsManager = new HighlightsManager(scope, editor, element) cursorPositionManager = new CursorPositionManager(scope, editor, element) # Prevert Ctrl|Cmd-S from triggering save dialog @@ -117,9 +119,6 @@ define [ resetSession() session = editor.getSession() - autoCompleteManager.bindToSession(session) - annotationsManager.redrawAnnotations() - doc = session.getDocument() doc.on "change", () -> scope.$apply () -> diff --git a/services/web/public/coffee/app/ide/editor/highlights/HighlightsManager.coffee b/services/web/public/coffee/app/ide/editor/highlights/HighlightsManager.coffee index b1afe3fb57..18d205c448 100644 --- a/services/web/public/coffee/app/ide/editor/highlights/HighlightsManager.coffee +++ b/services/web/public/coffee/app/ide/editor/highlights/HighlightsManager.coffee @@ -29,6 +29,9 @@ define [ e.position = position @showAnnotationLabels(position) + @editor.on "changeSession", () => + @redrawAnnotations() + redrawAnnotations: () -> @_clearMarkers() @_clearLabels() diff --git a/services/web/public/coffee/app/ide/editor/spell-check/SpellCheckManager.coffee b/services/web/public/coffee/app/ide/editor/spell-check/SpellCheckManager.coffee index cc38486fb1..2f9838a9ab 100644 --- a/services/web/public/coffee/app/ide/editor/spell-check/SpellCheckManager.coffee +++ b/services/web/public/coffee/app/ide/editor/spell-check/SpellCheckManager.coffee @@ -28,6 +28,7 @@ define [ $(document).on "click", (e) => @closeContextMenu(e) + return true # $(document).on "contextmenu", (e) => # @closeContextMenu(e) diff --git a/services/web/public/coffee/app/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/app/ide/pdf/controllers/PdfController.coffee index fe2836862c..4fa3829f0d 100644 --- a/services/web/public/coffee/app/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/app/ide/pdf/controllers/PdfController.coffee @@ -227,7 +227,6 @@ define [ App.controller "PdfSynctexController", ["$scope", "synctex", "ide", ($scope, synctex, ide) -> $scope.showControls = true $scope.$on "layout:pdf:resize", (event, data) -> - console.log "RESIZE DATA", data.east if data.east.initClosed $scope.showControls = false else diff --git a/services/web/public/coffee/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/auto-complete/AutoCompleteManager.coffee index 52ae4a6b49..fbc90f0324 100644 --- a/services/web/public/coffee/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/auto-complete/AutoCompleteManager.coffee @@ -47,6 +47,8 @@ define [ range = new Range(pos.row, pos.column, pos.row, pos.column + 1) nextChar = editor.session.getTextRange(range) + console.log "CALLING OLD" + # If we are in \begin{it|}, then we need to remove the trailing } # since it will be adding in with the autocomplete of \begin{item}... if this.completions.filterText.match(/^\\begin\{/) and nextChar == "}" diff --git a/services/web/public/stylesheets/app/editor.less b/services/web/public/stylesheets/app/editor.less index c1ff220a71..b98e14a376 100644 --- a/services/web/public/stylesheets/app/editor.less +++ b/services/web/public/stylesheets/app/editor.less @@ -22,6 +22,13 @@ margin-left: -200px; } +#chat-wrapper { + .full-size; + > .ui-layout-resizer > .ui-layout-toggler { + display: none !important; + } +} + #ide-body { .full-size; top: 40px; diff --git a/services/web/public/stylesheets/app/editor/left-menu.less b/services/web/public/stylesheets/app/editor/left-menu.less index caaec20461..41959e0cfc 100644 --- a/services/web/public/stylesheets/app/editor/left-menu.less +++ b/services/web/public/stylesheets/app/editor/left-menu.less @@ -1,14 +1,16 @@ #left-menu { position: absolute; - width: 210px; - padding: 10px; + width: 260px; + padding: (@line-height-computed / 2); top: 0; bottom: 0; background-color: #f4f4f4; z-index: 100; - overflow: auto; + overflow-y: auto; + overflow-x: hidden; -webkit-transition: left ease-in-out 0.35s; transition: left ease-in-out 0.35s; + font-size: 14px; left: -280px; &.shown { @@ -16,14 +18,69 @@ } h4 { - font-family: @font-family-serif; + font-family: @font-family-sans-serif; font-weight: 400; font-size: 1rem; margin: (@line-height-computed / 2) 0; padding-bottom: (@line-height-computed / 4); color: @gray-light; - border-bottom: 1px solid @gray-light; - text-transform: uppercase; + border-bottom: 1px solid @gray-lighter; + } + + h4:first-child { + margin-top: 0; + } + + ul.nav-downloads { + li { + display: inline-block; + text-align: center; + width: 100px; + a { + color: @gray-dark; + &:hover, &:active { + background-color: @link-color; + color: white; + } + } + .pdf-disabled { + color: @gray-light; + } + i { + margin-bottom: (@line-height-computed / 4); + } + } + } + + form.settings { + label { + font-weight: normal; + color: @gray-dark; + margin-bottom: 0; + padding-top: 8px; + } + select.form-control { + height: 34px; + background: none; + border: none; + box-shadow: none; + color: @link-color; + cursor: pointer; + font-size: 14px; + font-weight: 700; + } + .form-controls { + padding: 0 (@line-height-computed / 4); + &:hover { + background-color: @link-color; + select.form-control { + color: white; + } + label { + color: white; + } + } + } } } diff --git a/services/web/public/stylesheets/app/editor/toolbar.less b/services/web/public/stylesheets/app/editor/toolbar.less index 81b7188611..ba3f667d9a 100644 --- a/services/web/public/stylesheets/app/editor/toolbar.less +++ b/services/web/public/stylesheets/app/editor/toolbar.less @@ -25,7 +25,8 @@ border-radius: 0; border-right: 1px solid @toolbar-border-color; color: @link-color; - padding: 6px 12px 8px; + padding: 3px 10px 5px; + font-size: 20px; &:hover { text-shadow: 0 1px 0 rgba(0, 0, 0, 0.15); background-color: darken(white, 10%);