Вот уже полгода прошло с появления на свет самой первой версии браузера Вивальди.
За этот период группа разработчиков многое успела сделать и добавить, радуя пользователей еженедельными билдами. Прогресс виден налицо. А совсем скоро подтянется и Technical Preview 4.

Но многим пользователям до сих пор не хватает каких-то фич. И пока разработчики браузера заняты совсем другим, мы попытаемся кое-что реализовать сами.

image

Эта статья является неким сборником некоторых функций, расширяющих возможности UI. Что-то подсмотрено на официальном форуме Vivaldi, что-то изменено, а что-то написано собственноручно. В статье мы попробуем реализовать фичи из других браузеров и вернём пару привычных вещей, которых некоторым так не хватает.

А под катом мы начнём с самого вкусного.


Сворачивать щелчком по вкладке


Именно так называлась галочка в настройках вкладок Opera Presto. Она позволяла при повторном нажатии на активную вкладку открывать предыдущую. Для некоторых это была весьма полезная вещь, которая со временем, к сожалению, пропала.
Так давайте же теперь вернём её в Вивальди.
Вот как выглядела она в Опере:
Иллюстрация:
image

Как и в прошлой статье здесь работаем также по старой схеме — только уже создаём не css, а js файл с каким-нибудь названием, например, «last-tab.js». И вставляем туда код:
last-tab.js
var browser = document.body.querySelector('#browser');
var list = [];
function listTabs() {
	var tabs = browser.querySelectorAll('#tabs>.tab');
	list=[];
	for(var i = 0;i < tabs.length;i++){
	    list.push(tabs[i]);
	}

	list.sort(function(a,b) { return recent(b) - recent(a); });
}

function recent(tab){
	var page = document.querySelector('.webpageview webview[tab_id="'+tab.dataset.tabId+'"]');
	if(page) {
	    page = page.parentNode.parentNode.parentNode.parentNode;
	    return parseInt(page.style.zIndex);
	}
	return 0;
};

var dispatchMouseEvent = function(target, var_args) {
	var e = document.createEvent("MouseEvents");
	e.initEvent.apply(e, Array.prototype.slice.call(arguments, 1));
	target.dispatchEvent(e);
};

browser.addEventListener('mousedown', function(e){
    for (var i = 0; i < e.path.length; i++) {
        if (e.path[i].className.indexOf('active') > -1) {
            var active = browser.querySelector('.tab.active');

            listTabs();
            dispatchMouseEvent(list[1], 'mousedown', true, true);
            break;
        }
    }
});


А в browser.html после
    <script src="bundle.js"></script>

вставляем строчку:
    <script src="last-tab.js"></script>

Теперь при повторном нажатии на активную вкладку будет открываться предыдущая.

Переход в начало страницы щелчком по вкладке


У Яндекса же есть свой альтернативный взгляд на то, как можно использовать щелчок по активной вкладке.
За эти полгода я часто слышал в комментариях и на форумах просьбы пользователей сделать эту фичу. Сам я Яндекс.Браузером никогда не пользовался, но по описанию всё, в принципе, кристально ясно что требуется.
tab-to-top.js
// Click on active tab to scroll top
var browser=document.body.querySelector('#browser');
browser.addEventListener('click', function(e){
	for (var i = 0; i < e.path.length; i++) {
		if (e.path[i].className.indexOf('active') > -1) {
			var active = browser.querySelector('.tab.active');
			var webview = document.querySelector('#webview-container webview[tab_id="'+active.dataset.tabId+'"]');
			webview.executeScript({ code: "document.body.scrollTop=0" });
			return;
		}
	}
});


В browser.html соответственно:
    <script src="tab-to-top.js"></script>

Запускаем, проверяем. Вроде работает.
Иллюстрация (1 Mb):
image

Так как эти две функции исключают друг друга, настоятельно не рекомендуется совмещать их.

Вставить и перейти по клику


В Maxthon есть довольно удобная функция перехода по ссылке из буфера обмена.
image
После того, как я её попробовал, мне стало её очень не хватать во всех остальных браузерах, да и другие пользователи тоже просят у разработчиков сделать подобное.
Благо мы можем не ждать их и сами попробуем повторить эту функцию в Вивальди.

Создаём «click-and-go.js» и вставляем код:
click-and-go.js
// Right click on plus-button to paste and go
var browser=document.body.querySelector('#browser');
var isItMouse = false; // Exclude responses from keyboard

//Tweak for paste in this input-field
var hiddenInput = document.createElement("input");
hiddenInput.type = "text";
browser.appendChild(hiddenInput);
hiddenInput.style.width = "0px";
hiddenInput.style.height = "0px";
hiddenInput.style.display = "none";

var dispatchMouseEvent = function(target, var_args) {
    var e = document.createEvent("MouseEvents");
    e.initEvent.apply(e, Array.prototype.slice.call(arguments, 1));
    target.dispatchEvent(e);
};

browser.addEventListener('contextmenu', function(e) {
    //Area near square
    if (e.target.className.toString().indexOf('newtab') > -1) {
        isItMouse = true;
        document.execCommand('paste');
        return;
    }
    //Plus-symbol
    if (e.target.parentNode.className.indexOf('newtab') > -1) {
        initPaste();
        return;
    }
    //Square
    if (e.target.getTotalLength() > 0) { // 104 — length of new tab Button SVG
        initPaste();
        return;
    }
});

function initPaste() {
    isItMouse = true;
    hiddenInput.style.display = "block";
    hiddenInput.focus();
    document.execCommand('paste');
}

document.addEventListener('paste',function(e) {
    if (isItMouse) {
        isItMouse = false;
        var url = e.clipboardData.getData('text/plain');
        hiddenInput.style.display = "none"; //hide input-field for pasting

        var re = new RegExp('\\r\\n', 'g'); // Delete newline characters
        url = url.replace(re, '');
        // Search engines
    	var searchEngine = 'https://google.com/webhp?hl=ru#hl=ru&q='; 
    //	var searchEngine = 'http://yandex.ru/search/?text=';
    //  var searchEngine = 'https://duckduckgo.com/?q=';
        var active = browser.querySelector('.tab.active');
        var webview = document.querySelector('#webview-container webview[tab_id="'+active.dataset.tabId+'"]');
        
        if (url.length > 0) {
            if (checkUrl(url)) {
                webview.executeScript({ code: "window.open('"+url+"','_blank')" });
            } else if (checkUrlWithoutProtocol(url)) {
                webview.executeScript({ code: "window.open('http://"+url+"','_blank')" });
            } else {
                webview.executeScript({ code: "window.open('"+searchEngine+url+"','_blank')" });
            }
        }
        
        console.log(url)}
    }
);
//Check url
var patternUrl = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?/#]\S*)?$/i;
var patternUrlWithout = /^(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,3})).?)(?::\d{2,5})?(?:[/?/#]\S*)?$/i;
function checkUrl(str) {
    return patternUrl.test(str);
}
//url without protocol
function checkUrlWithoutProtocol(str) {    
    return patternUrlWithout.test(str);    
}



Соответственно в browser.html добавляем:
    <script src="click-and-go.js"></script>

Функция сделана по такому принципу: нажатие правой кнопки мыши по плюсику создаёт новую вкладку, и происходит переход по ссылке из буфера обмена, если же там находится не ссылка, то идёт автоматический поиск по строке в поисковой системе.
Но у решения есть пара минусов.
Во-первых, регулярное выражение, которое ищет ссылку в строке без протокола, может ошибаться. То есть, например, «test.test» воспримется как ссылка ровно как и «ya.ru». Но в целом на практике это не должно мешать.
А во-вторых, заданная поисковая система не связана с поисковой системой, что поставлена в браузере как основная. В данном случае поиск будет происходить через Google. Но я специально добавил ещё строчки для DuckDuckGo и Yandex, если кого-то не устроит Google. Для других поисковых систем также вполне несложно написать нужную ссылку.

Иллюстрация (1 Mb):
image

Избавляемся от строки состояния


Убрать строку состояния (status bar) можно было в настройках с самой первой версии. Но проблема в том, что именно там отображаются ссылки при наведении на них. И тем, кому status bar не нужен, либо приходилось уживаться с ним, либо же не видеть ссылок при наведении. Сейчас мы попробуем это исправить с помощью css.
status-bar.css
/** Избавляемся от status bar **/

#footer.disabled{
	display:block !important;
	position:static !important;
	padding:0 !important;
	height:0 !important;
	width:0 !important;
}

#footer.disabled > *{
	display:none !important;
}
#footer.disabled #status_info{
	display:block !important;
}

#footer.disabled #status_info span{
	position:fixed !important;
	bottom:0 !important;
	left:0 !important;
	margin:0 !important;
	color:#333 !important;
	background-color:#FEFEFE !important;
	padding:2px 5px !important;
	border:#9E9E9E solid 0 !important;
	border-width:1px 1px 0 0 !important;
	max-width:75% !important;
	overflow: hidden !important;
	white-space: nowrap !important;
	text-overflow: ellipsis !important;
	z-index:50 !important;
}

#footer.disabled #status_info span:empty{
	display:none !important;
}

/******/


Соответственно после
    <link rel="stylesheet" href="style/common.css" />

вставляем строчку
    <link rel="stylesheet" href="status-bar.css" />

Принцип здесь простой: мы просто убираем отображение строки состояния и рисуем прямоугольник для status_info в углу.
image

Но у данного решения есть некоторые минусы: ссылка отображается всегда в углу поверх всех элементов пользовательского интерфейса. Поэтому для пользователей с вкладками снизу это решение не подойдёт, и нужно будет блок приподнимать через bottom.

Убираем адресную строку в заголовок окна


Данное дизайнерское решение может показаться интересным для пользователей, у которых вкладки расположены не сверху, а по бокам или снизу. Потому что именно при таком расположении вкладок сверху появляется заголовок окна, который отъедает лишнее место по вертикали, что может быть критично для некоторых.
Так будет выглядеть окно со совмещением:
image

header.css
/** Совмещаем заголовок вместе с адресбар **/
#header {
	min-height: 2px !important;
	height: 2px !important;
	z-index: auto !important;
}

.win .vivaldi {
	color: #fff;
	position: absolute;
	top: 5px;
	left: 98px;
}

.vivaldi+#tabs-container.top {
	border-bottom: 1px solid;
	position: absolute;
	top: 62px;
	width: 100%;
	z-index: 1 !important;
}

#tabs-container.bottom #tabs, #tabs-container.top #tabs {
	max-height: 30px !important;
}

.toolbar.toolbar-addressbar {
	padding-right: 100px;
}

.addressfield {	margin-left: 40px !important; }

.button-toolbar {
	position: relative !important;
	left: 30px !important;
}

.bookmark-bar { margin-bottom: 37px; }

.vivaldi { z-index: 3; }

.window-buttongroup { z-index: 2; }

.home,.rewind,.next,#pagetitle{display:none}
/********************/


Это вариант для тех, кто пользуется Vivaldi-кнопкой.
Но есть и пользователи, которые используют меню в его классическом представлении. В таком случае адресной строке придётся ужаться и уступить место кнопкам меню. Причём для разных языков ширина меню будет разная.
Варианты для английского и русского языков
image
image
#header {
min-height: 2px !important;
z-index: auto !important;
}

.win .topmenu {
color: #fff;
position: absolute;
top: 5px;
left: 0px;
}

.topmenu+#tabs-container.top {
border-bottom: 1px solid;
position: absolute;
top: 62px;
width: 100%;
z-index: 1 !important;
}

#tabs-container.bottom #tabs, #tabs-container.top #tabs {
height: 30px !important;
}

.toolbar.toolbar-addressbar {
padding-right: 100px;
}

.button-toolbar.back{ margin-left: 350px }

.home,.rewind,.next,#pagetitle{display:none}

.addressfield { margin-left: 3px !important; }

.bookmark-bar { margin-bottom:0px; }

.window-buttongroup { z-index: 2; }

#header {
min-height: 2px !important;
z-index: auto !important;
}

.win .topmenu {
color: #fff;
position: absolute;
top: 5px;
left: 0px;
}

.topmenu+#tabs-container.top {
border-bottom: 1px solid;
position: absolute;
top: 62px;
width: 100%;
z-index: 1 !important;
}

#tabs-container.bottom #tabs, #tabs-container.top #tabs {
height: 30px !important;
}

.toolbar.toolbar-addressbar {
padding-right: 100px;
}

.button-toolbar.back{margin-left: 270px}

.home,.rewind,.next,#pagetitle{display:none}

.addressfield { margin-left: 3px !important; }

.bookmark-bar { margin-bottom:0px; }

.window-buttongroup { z-index: 2; }



Несколько строк вкладок


Наверно многие уже забыли, но Opera Presto умела отображать вкладки в несколько строчек. Сейчас такое разве что в FF можно встретить. Пришло время и Вивальди научить этому.

tab-lines.css
#tabs-container.top #tabs, #tabs-container.bottom #tabs {
  height: auto !important;
  display: block !important;
}
#tabs-container.bottom #tabs .tab, #tabs-container.top #tabs .tab {
  max-width: 150px !important;
  min-width: 150px;
  display: inline-block !important;
  float: left;
}
#tabs .tab .tab-thumb { display: none; }
#tabs .newtab { margin-top: -9px; }
#tabs .trash {
  margin-top: -10px; 
  display: inline-block !important;
  float: right;
}


Так будет выглядеть полученный результат:
image
При открытии новых вкладок верхняя панель будет увеличиваться в высоту соответственно.

Ну и на последок пара мелочей:


  • Отключение отображения кнопки с плюсом в Экспресс-Панели (может вскоре станет неактуальным):
    .dial.add {display: none !important;}

  • Отключение отображения крестика с вкладок:
    #tabs .tab .tab-header .close {display: none !important;}



Если вы захотите использовать какие-то функции одновременно, то можно вполне безболезненно скопировать несколько файлов в один какой-нибудь custom.js файл, чтобы не плодить лишних файлов.
Тоже самое и с css.

Ещё хотелось бы поделиться гитхабом одного пользователя Вивальди с форума, который сделал вполне неплохие вещи и даже реализовал собственную прокрутку вкладок через ПКМ+скролл с превью. Хотя конкретно это более неактуально, т.к. в браузере уже есть такая функция, пусть и в самом зачаточном виде.

А на этом пожалуй всё. Надеюсь что-то из этого для вас было полезно. Конечно, эти решения не идеальны, и могут требовать индивидуальной доработки, т.к. от части некоторые вещи я создавал под себя.
Если же у вас есть мысли по поводу более лаконичного решения в js-функциях или предложения, что можно ещё сделать, то буду рад послушать.

Не забывайте, что при каждом обновлении все подобные изменения будут слетать. После обновления созданные вами файлы будут находиться в папке предыдущей версии. Всё, что нужно будет это скопировать нужные два или три файла в новую версию.

Пара готовых вариантов от меня:
last-tab + status-bar + click&go + hideplus + tab-lines
(Вкладки должны быть заранее перемещены из заголовка) tab-top + status-bar + click&go + hideplus + header

Vivaldi Forum
Github.

Комментарии (12)


  1. Volanttt
    02.07.2015 13:00
    +12

    Звонил Фрейд. Просил передать, что «налицо» в контексте первого абзаца пишется слитно.


  1. gigimon
    02.07.2015 13:57
    +4

    Круто конечно, что вы сделали такую расширяемость интерфейса, но из-за повсеместного js/css он работает ну так медленно, что невозможно. Опера работала быстро, даже 5 лет назад, а с интерфейсом можно было сделать практически все, что угодно


    1. 23rd Автор
      03.07.2015 01:37

      Это конкретно после моих модификаций начинает работать медленно?


      1. gigimon
        03.07.2015 01:38

        Простите, я думал вы из Vivaldi и пост официальный. Ваши изменения я не ставил, т.к. vivaldi ставлю, смотрю на тормоза и откладываю до следующего релиза


        1. bodqhrohro
          03.07.2015 11:03

          Значит, моя идея браузера на голом браузерном движке и с интерфейсом на веб-технологиях, ворочающем вкладками в айфреймах, всё ещё актуальна. Можно взять какую-нибудь легковесную обёртку над Webkit типа browser.py или Surf и в ней пускать. В принципе, есть Breach, который вообще гол и ориентирован на расширение самопальными скриптами, но он, как и Vivaldi, на Blink и высокой производительностью не отличается (хотя так люто не тормозит); вдобавок, официальный сайт, похоже, подох, и коммитов давно не было. Небольшой зародыш у меня есть (писал на телефоне для браузера NetFront, в котором вкладок нет), но для десктопа и нормальной расширяемости ещё пилить и пилить. Попробую даже совместимость со структурой Vivaldi обеспечить, чтоб скрипты ТСа переделывать не пришлось.


          1. gigimon
            03.07.2015 11:38

            Так а чем ваш, по задумке, отличается от вивальди?


            1. bodqhrohro
              04.07.2015 17:45

              Ну начнём с того, что Vivaldi идеологически в первую очередь ориентирован быть продолжением Opera Classic. И расширяемость тут является не фичей, а технической возможностью (в Chrome до определённого времени тоже можно было многие вещи править, расковыряв pak-файл, и в Opera Classic был browser.js). Существенным недостатком и у Vivaldi, и у Breach являются хромовский движок Blink под капотом (непомерно увесистый из-за многопроцессности и ряда других особенностей), и Node.JS в комплекте для полной интеграции с ОС (например, полноценный доступ к локальным файлам). А у Vivaldi не только Blink, но и обрезки Chromium наличествуют. Такой бутерброд не может быть легковесным, а >16 ГБ рамы и >8-ядерные процы ещё не скоро станут обыденностью. После появления Breach я оставил мысли о создании велосипеда, но вижу, что легковесность всё ещё востребована, а прожорливость хрома и его компонентов растёт с каждым днём, вызывая всё больше жалоб, особенно у линуксоидов, которые нередко на слабом/устаревшем железе сидят.


    1. DrPass
      03.07.2015 10:59
      +3

      Аналогичные впечатления. Как-то не оправдывает он мои ожидания. ИМХО, это не замена «той старой Оперы», получается просто еще один альтернативный броузер для любителей поэкспериментировать с софтом, вроде Maxthon (кто-то его еще помнит?) или Яндекс.Браузер. Встроенного функционала много, но в Опере оно было как-то… гармонично, что ли, а главное — быстро и эффективно. А здесь даже надеяться на заметное улучшение производительности в финальных версиях не приходится, ибо JS.


  1. ProVal
    02.07.2015 19:36

    Спасибо. Если в ближайшее время не дождусь официально этих штук, то заберу себе отсюда.

    Есть ещё пара мыслей по поводу некоторых элементов. Это скорее к официальным сборкам пожелания.
    1. Отображение урлов при наведении на ссылку я бы хотел видеть в адресной строке. То есть, не наводим ни на какую ссылку — видим урл страницы, наводим на ссылку, на его месте прямо в адресной строке видим урл ссылки, как-нибудь помеченный, чтобы было понятно, что туда попадём, если нажмём. Соответственно при клике попадаем на эту страницу, а урл принимает обычный внешний вид. Вроде логичней, чем отображение в статусбаре или в углу, как в хроме.
    2. Scroll-to-top я считаю действительно должно быть стандартной кнопкой во всех браузерах, так как она управляет отображением страницы в браузере, наравне с ползунком масштаба, к примеру. Чтобы [в идеале, когда/если такое будет во всех браузерах] не плодили на каждом сайте свои кнопки-велосипеды разных форм, размеров, цветов и в разных местах. И располагаться она должна рядом с остальными (назад, вперёд, обновить). Ну, а клик по вкладке как раз для сворачивания останется.


  1. Averrin
    02.07.2015 19:51

    Этим мне нравится редактор Atom — можно делать с интерфейсом и функционалом все что заблагорассудится. Пожалуй, попробую таки Вивальди, вдруг этот фактор перевесит минусы. Как там, кстати, с расширениями?


    1. 23rd Автор
      02.07.2015 20:01

      Поставить возможно через vivaldi://chrome/extensions.


  1. yosemity
    03.07.2015 17:40

    Автоматическая перезагрузка страницы. Для меня это киллерфича старой оперы.