Merge branch 'develop'
This commit is contained in:
commit
2edffba81e
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tabs",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"description": "Persistent and separate browser tabs in one window",
|
||||
"author": {
|
||||
"name": "Alice Gaudon",
|
||||
|
@ -21,6 +21,7 @@
|
||||
</div>
|
||||
|
||||
<ul id="service-selector"></ul>
|
||||
<div id="service-last-drag-position" class="hidden"></div>
|
||||
|
||||
<button id="add-button"><i class="fa fa-plus"></i></button>
|
||||
|
||||
|
@ -8,6 +8,7 @@ const {
|
||||
dialog,
|
||||
} = remote;
|
||||
|
||||
const appInfo = {};
|
||||
const icons = [];
|
||||
|
||||
let services = [];
|
||||
@ -18,47 +19,61 @@ let addButton;
|
||||
|
||||
|
||||
// Service context menu
|
||||
const serviceContextMenu = new Menu();
|
||||
serviceContextMenu.append(new MenuItem({
|
||||
label: 'Reload', click: () => {
|
||||
reloadService(serviceContextMenu.serviceId);
|
||||
}
|
||||
}));
|
||||
serviceContextMenu.append(new MenuItem({
|
||||
label: 'Close', click: () => {
|
||||
unloadService(serviceContextMenu.serviceId);
|
||||
}
|
||||
}));
|
||||
serviceContextMenu.append(new MenuItem({type: "separator"}));
|
||||
serviceContextMenu.append(new MenuItem({
|
||||
label: 'Edit', click: () => {
|
||||
ipcRenderer.send('openServiceSettings', serviceContextMenu.serviceId);
|
||||
}
|
||||
}));
|
||||
serviceContextMenu.append(new MenuItem({
|
||||
label: 'Delete', click: () => {
|
||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'question',
|
||||
title: 'Confirm',
|
||||
message: 'Are you sure you want to delete this service?',
|
||||
buttons: ['Cancel', 'Confirm'],
|
||||
cancelId: 0,
|
||||
}).then(result => {
|
||||
if (result.response === 1) {
|
||||
ipcRenderer.send('deleteService', serviceContextMenu.serviceId);
|
||||
}
|
||||
}).catch(console.error);
|
||||
}
|
||||
}));
|
||||
|
||||
function openServiceContextMenu(event, index) {
|
||||
function openServiceContextMenu(event, serviceId) {
|
||||
event.preventDefault();
|
||||
serviceContextMenu.serviceId = index;
|
||||
serviceContextMenu.popup({window: remote.getCurrentWindow()});
|
||||
const service = services[serviceId];
|
||||
|
||||
const menu = new Menu();
|
||||
const ready = service.view && service.viewReady, notReady = !service.view && !service.viewReady;
|
||||
menu.append(new MenuItem({
|
||||
label: 'Home', click: () => {
|
||||
service.view.loadURL(service.url)
|
||||
.catch(console.error);
|
||||
},
|
||||
enabled: ready,
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
label: ready ? 'Reload' : 'Load', click: () => {
|
||||
reloadService(serviceId);
|
||||
},
|
||||
enabled: ready || notReady,
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
label: 'Close', click: () => {
|
||||
unloadService(serviceId);
|
||||
},
|
||||
enabled: ready,
|
||||
}));
|
||||
menu.append(new MenuItem({type: "separator"}));
|
||||
menu.append(new MenuItem({
|
||||
label: 'Edit', click: () => {
|
||||
ipcRenderer.send('openServiceSettings', serviceId);
|
||||
}
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
label: 'Delete', click: () => {
|
||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'question',
|
||||
title: 'Confirm',
|
||||
message: 'Are you sure you want to delete this service?',
|
||||
buttons: ['Cancel', 'Confirm'],
|
||||
cancelId: 0,
|
||||
}).then(result => {
|
||||
if (result.response === 1) {
|
||||
ipcRenderer.send('deleteService', serviceId);
|
||||
}
|
||||
}).catch(console.error);
|
||||
}
|
||||
}));
|
||||
menu.popup({window: remote.getCurrentWindow()});
|
||||
}
|
||||
|
||||
|
||||
ipcRenderer.on('data', (event, brandIcons, solidIcons, actualServices, actualSelectedService) => {
|
||||
ipcRenderer.on('data', (event, appData, brandIcons, solidIcons, actualServices, actualSelectedService) => {
|
||||
// App info
|
||||
appInfo.title = appData.title;
|
||||
|
||||
// Icons
|
||||
for (const icon of brandIcons) {
|
||||
icons.push(icon);
|
||||
}
|
||||
@ -81,6 +96,19 @@ ipcRenderer.on('data', (event, brandIcons, solidIcons, actualServices, actualSel
|
||||
createService(i);
|
||||
}
|
||||
|
||||
// Init drag last position
|
||||
const lastDragPosition = document.getElementById('service-last-drag-position');
|
||||
lastDragPosition.addEventListener('dragover', () => {
|
||||
const index = services.length;
|
||||
if (draggedId !== index && draggedId !== index - 1) {
|
||||
resetDrag();
|
||||
lastDragTarget = dragTargetId = index;
|
||||
lastDragPosition.classList.remove('hidden');
|
||||
lastDragPosition.classList.add('drag-target');
|
||||
}
|
||||
});
|
||||
|
||||
// Set active service
|
||||
if (actualSelectedService < 0 || actualSelectedService >= services.length) {
|
||||
actualSelectedService = 0;
|
||||
}
|
||||
@ -114,6 +142,31 @@ ipcRenderer.on('updateService', (e, id, data) => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcRenderer.on('reorderService', (e, serviceId, targetId) => {
|
||||
const oldServices = services;
|
||||
services = [];
|
||||
|
||||
for (let i = 0; i < targetId; i++) {
|
||||
if (i !== serviceId) {
|
||||
services.push(oldServices[i]);
|
||||
}
|
||||
}
|
||||
services.push(oldServices[serviceId]);
|
||||
const newId = services.length - 1;
|
||||
for (let i = targetId; i < oldServices.length; i++) {
|
||||
if (i !== serviceId) {
|
||||
services.push(oldServices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('service-selector').innerHTML = '';
|
||||
for (let i = 0; i < services.length; i++) {
|
||||
services[i].li = undefined;
|
||||
createService(i);
|
||||
}
|
||||
setActiveService(newId);
|
||||
});
|
||||
|
||||
ipcRenderer.on('deleteService', (e, id) => {
|
||||
const nav = document.querySelector('#service-selector');
|
||||
|
||||
@ -184,6 +237,55 @@ function createService(index, nextNavButton) {
|
||||
if (service.autoLoad) {
|
||||
loadService(index, service);
|
||||
}
|
||||
|
||||
initDrag(index, li);
|
||||
}
|
||||
|
||||
let draggedId;
|
||||
let lastDragTarget = -1;
|
||||
let dragTargetId = -1;
|
||||
let dragTargetCount = 0;
|
||||
|
||||
function initDrag(index, li) {
|
||||
li.serviceId = index;
|
||||
li.draggable = true;
|
||||
li.addEventListener('dragstart', (event) => {
|
||||
draggedId = index;
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
document.getElementById('service-last-drag-position').classList.remove('hidden');
|
||||
});
|
||||
li.addEventListener('dragover', () => {
|
||||
if (draggedId !== index && draggedId !== index - 1) {
|
||||
resetDrag();
|
||||
lastDragTarget = dragTargetId = index;
|
||||
document.getElementById('service-last-drag-position').classList.remove('hidden');
|
||||
li.classList.add('drag-target');
|
||||
}
|
||||
});
|
||||
li.addEventListener('dragend', () => {
|
||||
reorderService(draggedId, lastDragTarget);
|
||||
resetDrag();
|
||||
});
|
||||
}
|
||||
|
||||
function resetDrag() {
|
||||
lastDragTarget = -1;
|
||||
dragTargetId = -1;
|
||||
dragTargetCount = 0;
|
||||
document.getElementById('service-selector').querySelectorAll('li').forEach(li => {
|
||||
li.classList.remove('drag-target');
|
||||
});
|
||||
const lastDragPosition = document.getElementById('service-last-drag-position');
|
||||
lastDragPosition.classList.remove('drag-target');
|
||||
lastDragPosition.classList.add('hidden');
|
||||
}
|
||||
|
||||
function reorderService(serviceId, targetId) {
|
||||
console.log('Reordering service', serviceId, targetId);
|
||||
if (targetId >= 0) {
|
||||
setActiveService(null);
|
||||
ipcRenderer.send('reorderService', serviceId, targetId);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
@ -200,7 +302,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
function setActiveService(serviceId) {
|
||||
const currentService = services[serviceId];
|
||||
process.nextTick(() => {
|
||||
loadService(serviceId, currentService);
|
||||
if (currentService) {
|
||||
loadService(serviceId, currentService);
|
||||
}
|
||||
|
||||
// Hide previous service
|
||||
if (services[selectedService] && services[selectedService].view) {
|
||||
@ -208,7 +312,9 @@ function setActiveService(serviceId) {
|
||||
}
|
||||
|
||||
// Show service
|
||||
currentService.view.classList.add('active');
|
||||
if (currentService) {
|
||||
currentService.view.classList.add('active');
|
||||
}
|
||||
|
||||
// Save active service ID
|
||||
selectedService = serviceId;
|
||||
@ -322,6 +428,16 @@ function updateNavigation() {
|
||||
if (view && view.canGoBack()) backButton.classList.remove('disabled');
|
||||
else backButton.classList.add('disabled');
|
||||
}
|
||||
|
||||
updateWindowTitle();
|
||||
}
|
||||
|
||||
function updateWindowTitle() {
|
||||
if (selectedService === null) {
|
||||
ipcRenderer.send('updateWindowTitle', null);
|
||||
} else {
|
||||
ipcRenderer.send('updateWindowTitle', selectedService, services[selectedService].view.getWebContents().getTitle());
|
||||
}
|
||||
}
|
||||
|
||||
function goForward() {
|
||||
|
@ -55,25 +55,29 @@ webContents.on('context-menu', (event, props) => {
|
||||
|
||||
// Text clipboard
|
||||
if (editFlags.canUndo || editFlags.canRedo || editFlags.canCut || editFlags.canCopy || editFlags.canPaste || editFlags.canDelete) {
|
||||
if (menu.items.length > 0) {
|
||||
menu.append(new MenuItem({type: 'separator'}));
|
||||
}
|
||||
if (editFlags.canUndo) {
|
||||
menu.append(new MenuItem({
|
||||
label: 'Undo',
|
||||
role: 'undo',
|
||||
}));
|
||||
}
|
||||
if (editFlags.canRedo) {
|
||||
menu.append(new MenuItem({
|
||||
label: 'Redo',
|
||||
role: 'redo',
|
||||
}));
|
||||
if (editFlags.canUndo || editFlags.canRedo) {
|
||||
if (menu.items.length > 0) {
|
||||
menu.append(new MenuItem({type: 'separator'}));
|
||||
}
|
||||
|
||||
if (editFlags.canUndo) {
|
||||
menu.append(new MenuItem({
|
||||
label: 'Undo',
|
||||
role: 'undo',
|
||||
}));
|
||||
}
|
||||
if (editFlags.canRedo) {
|
||||
menu.append(new MenuItem({
|
||||
label: 'Redo',
|
||||
role: 'redo',
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (menu.items.length > 0) {
|
||||
menu.append(new MenuItem({type: 'separator'}));
|
||||
}
|
||||
|
||||
menu.append(new MenuItem({
|
||||
label: 'Cut',
|
||||
role: 'cut',
|
||||
|
@ -10,6 +10,51 @@ body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#service-selector [draggable] {
|
||||
user-select: none;
|
||||
background-color: rgb(43, 43, 43);
|
||||
}
|
||||
|
||||
#service-selector [draggable] img {
|
||||
-webkit-user-drag: none;
|
||||
user-drag: none;
|
||||
}
|
||||
|
||||
#service-selector .drag-target button {
|
||||
height: 144px;
|
||||
padding-top: 88px;
|
||||
}
|
||||
|
||||
#service-selector .drag-target button::after,
|
||||
#service-last-drag-position.drag-target {
|
||||
content: "";
|
||||
height: 72px;
|
||||
border: 1px dashed #fff;
|
||||
box-sizing: border-box;
|
||||
|
||||
background-color: rgb(43, 43, 43);
|
||||
}
|
||||
#service-selector .drag-target button::after {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#service-last-drag-position:not(.hidden):not(.drag-target) {
|
||||
display: block;
|
||||
padding: 16px 4px;
|
||||
background-color: #fff5;
|
||||
}
|
||||
#service-last-drag-position:not(.drag-target)::after {
|
||||
content: "";
|
||||
display: block;
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
|
||||
#service-selector .drag-target::after {
|
||||
top: 75% !important;
|
||||
}
|
||||
|
||||
*:focus {
|
||||
outline-color: rgb(118, 93, 176);
|
||||
}
|
||||
|
19
src/Meta.js
19
src/Meta.js
@ -1,12 +1,29 @@
|
||||
export default class Meta {
|
||||
static #title = 'Tabs';
|
||||
static #devMode = null;
|
||||
|
||||
static get title() {
|
||||
return this.#title;
|
||||
}
|
||||
|
||||
static isDevMode() {
|
||||
if(this.#devMode === null) {
|
||||
if (this.#devMode === null) {
|
||||
this.#devMode = process.argv.length > 2 && process.argv[2] === '--dev';
|
||||
console.debug('Dev mode:', this.#devMode);
|
||||
}
|
||||
|
||||
return this.#devMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param service {Service}
|
||||
* @param viewTitle {string}
|
||||
*/
|
||||
static getTitleForService(service, viewTitle) {
|
||||
let suffix = '';
|
||||
if (typeof viewTitle === 'string' && viewTitle.length > 0) {
|
||||
suffix = ' - ' + viewTitle;
|
||||
}
|
||||
return this.title + ' - ' + service.name + suffix;
|
||||
}
|
||||
}
|
34
src/main.js
34
src/main.js
@ -58,6 +58,7 @@ function createWindow() {
|
||||
},
|
||||
autoHideMenuBar: true,
|
||||
icon: iconPath,
|
||||
title: Meta.title,
|
||||
});
|
||||
window.maximize();
|
||||
window.on('closed', () => {
|
||||
@ -157,12 +158,43 @@ function createWindow() {
|
||||
window.webContents.send('deleteService', id);
|
||||
});
|
||||
|
||||
ipcMain.on('reorderService', (e, serviceId, targetId) => {
|
||||
console.log('Reordering services', serviceId, targetId);
|
||||
|
||||
const oldServices = config.services;
|
||||
config.services = [];
|
||||
|
||||
for (let i = 0; i < targetId; i++) {
|
||||
if (i !== serviceId) {
|
||||
config.services.push(oldServices[i]);
|
||||
}
|
||||
}
|
||||
config.services.push(oldServices[serviceId]);
|
||||
for (let i = targetId; i < oldServices.length; i++) {
|
||||
if (i !== serviceId) {
|
||||
config.services.push(oldServices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
e.reply('reorderService', serviceId, targetId);
|
||||
config.save();
|
||||
});
|
||||
|
||||
ipcMain.on('updateWindowTitle', (event, serviceId, viewTitle) => {
|
||||
if (serviceId === null) {
|
||||
window.setTitle(Meta.title);
|
||||
} else {
|
||||
const service = config.services[serviceId];
|
||||
window.setTitle(Meta.getTitleForService(service, viewTitle));
|
||||
}
|
||||
});
|
||||
|
||||
console.log('> App started');
|
||||
}
|
||||
|
||||
function sendData() {
|
||||
console.log('Syncing data');
|
||||
window.webContents.send('data', brandIcons, solidIcons, config.services, selectedService);
|
||||
window.webContents.send('data', Meta.title, brandIcons, solidIcons, config.services, selectedService);
|
||||
}
|
||||
|
||||
function setActiveService(index) {
|
||||
|
Loading…
Reference in New Issue
Block a user