Merge branch 'develop'
This commit is contained in:
commit
2edffba81e
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "tabs",
|
"name": "tabs",
|
||||||
"version": "0.3.0",
|
"version": "0.4.0",
|
||||||
"description": "Persistent and separate browser tabs in one window",
|
"description": "Persistent and separate browser tabs in one window",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Alice Gaudon",
|
"name": "Alice Gaudon",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul id="service-selector"></ul>
|
<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>
|
<button id="add-button"><i class="fa fa-plus"></i></button>
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ const {
|
|||||||
dialog,
|
dialog,
|
||||||
} = remote;
|
} = remote;
|
||||||
|
|
||||||
|
const appInfo = {};
|
||||||
const icons = [];
|
const icons = [];
|
||||||
|
|
||||||
let services = [];
|
let services = [];
|
||||||
@ -18,24 +19,38 @@ let addButton;
|
|||||||
|
|
||||||
|
|
||||||
// Service context menu
|
// Service context menu
|
||||||
const serviceContextMenu = new Menu();
|
function openServiceContextMenu(event, serviceId) {
|
||||||
serviceContextMenu.append(new MenuItem({
|
event.preventDefault();
|
||||||
label: 'Reload', click: () => {
|
const service = services[serviceId];
|
||||||
reloadService(serviceContextMenu.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,
|
||||||
}));
|
}));
|
||||||
serviceContextMenu.append(new MenuItem({
|
menu.append(new MenuItem({
|
||||||
|
label: ready ? 'Reload' : 'Load', click: () => {
|
||||||
|
reloadService(serviceId);
|
||||||
|
},
|
||||||
|
enabled: ready || notReady,
|
||||||
|
}));
|
||||||
|
menu.append(new MenuItem({
|
||||||
label: 'Close', click: () => {
|
label: 'Close', click: () => {
|
||||||
unloadService(serviceContextMenu.serviceId);
|
unloadService(serviceId);
|
||||||
}
|
},
|
||||||
|
enabled: ready,
|
||||||
}));
|
}));
|
||||||
serviceContextMenu.append(new MenuItem({type: "separator"}));
|
menu.append(new MenuItem({type: "separator"}));
|
||||||
serviceContextMenu.append(new MenuItem({
|
menu.append(new MenuItem({
|
||||||
label: 'Edit', click: () => {
|
label: 'Edit', click: () => {
|
||||||
ipcRenderer.send('openServiceSettings', serviceContextMenu.serviceId);
|
ipcRenderer.send('openServiceSettings', serviceId);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
serviceContextMenu.append(new MenuItem({
|
menu.append(new MenuItem({
|
||||||
label: 'Delete', click: () => {
|
label: 'Delete', click: () => {
|
||||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
type: 'question',
|
type: 'question',
|
||||||
@ -45,20 +60,20 @@ serviceContextMenu.append(new MenuItem({
|
|||||||
cancelId: 0,
|
cancelId: 0,
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
if (result.response === 1) {
|
if (result.response === 1) {
|
||||||
ipcRenderer.send('deleteService', serviceContextMenu.serviceId);
|
ipcRenderer.send('deleteService', serviceId);
|
||||||
}
|
}
|
||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
menu.popup({window: remote.getCurrentWindow()});
|
||||||
function openServiceContextMenu(event, index) {
|
|
||||||
event.preventDefault();
|
|
||||||
serviceContextMenu.serviceId = index;
|
|
||||||
serviceContextMenu.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) {
|
for (const icon of brandIcons) {
|
||||||
icons.push(icon);
|
icons.push(icon);
|
||||||
}
|
}
|
||||||
@ -81,6 +96,19 @@ ipcRenderer.on('data', (event, brandIcons, solidIcons, actualServices, actualSel
|
|||||||
createService(i);
|
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) {
|
if (actualSelectedService < 0 || actualSelectedService >= services.length) {
|
||||||
actualSelectedService = 0;
|
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) => {
|
ipcRenderer.on('deleteService', (e, id) => {
|
||||||
const nav = document.querySelector('#service-selector');
|
const nav = document.querySelector('#service-selector');
|
||||||
|
|
||||||
@ -184,6 +237,55 @@ function createService(index, nextNavButton) {
|
|||||||
if (service.autoLoad) {
|
if (service.autoLoad) {
|
||||||
loadService(index, service);
|
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', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
@ -200,7 +302,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
function setActiveService(serviceId) {
|
function setActiveService(serviceId) {
|
||||||
const currentService = services[serviceId];
|
const currentService = services[serviceId];
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
|
if (currentService) {
|
||||||
loadService(serviceId, currentService);
|
loadService(serviceId, currentService);
|
||||||
|
}
|
||||||
|
|
||||||
// Hide previous service
|
// Hide previous service
|
||||||
if (services[selectedService] && services[selectedService].view) {
|
if (services[selectedService] && services[selectedService].view) {
|
||||||
@ -208,7 +312,9 @@ function setActiveService(serviceId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show service
|
// Show service
|
||||||
|
if (currentService) {
|
||||||
currentService.view.classList.add('active');
|
currentService.view.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
// Save active service ID
|
// Save active service ID
|
||||||
selectedService = serviceId;
|
selectedService = serviceId;
|
||||||
@ -322,6 +428,16 @@ function updateNavigation() {
|
|||||||
if (view && view.canGoBack()) backButton.classList.remove('disabled');
|
if (view && view.canGoBack()) backButton.classList.remove('disabled');
|
||||||
else backButton.classList.add('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() {
|
function goForward() {
|
||||||
|
@ -55,9 +55,11 @@ webContents.on('context-menu', (event, props) => {
|
|||||||
|
|
||||||
// Text clipboard
|
// Text clipboard
|
||||||
if (editFlags.canUndo || editFlags.canRedo || editFlags.canCut || editFlags.canCopy || editFlags.canPaste || editFlags.canDelete) {
|
if (editFlags.canUndo || editFlags.canRedo || editFlags.canCut || editFlags.canCopy || editFlags.canPaste || editFlags.canDelete) {
|
||||||
|
if (editFlags.canUndo || editFlags.canRedo) {
|
||||||
if (menu.items.length > 0) {
|
if (menu.items.length > 0) {
|
||||||
menu.append(new MenuItem({type: 'separator'}));
|
menu.append(new MenuItem({type: 'separator'}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editFlags.canUndo) {
|
if (editFlags.canUndo) {
|
||||||
menu.append(new MenuItem({
|
menu.append(new MenuItem({
|
||||||
label: 'Undo',
|
label: 'Undo',
|
||||||
@ -70,10 +72,12 @@ webContents.on('context-menu', (event, props) => {
|
|||||||
role: 'redo',
|
role: 'redo',
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (menu.items.length > 0) {
|
if (menu.items.length > 0) {
|
||||||
menu.append(new MenuItem({type: 'separator'}));
|
menu.append(new MenuItem({type: 'separator'}));
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.append(new MenuItem({
|
menu.append(new MenuItem({
|
||||||
label: 'Cut',
|
label: 'Cut',
|
||||||
role: 'cut',
|
role: 'cut',
|
||||||
|
@ -10,6 +10,51 @@ body {
|
|||||||
padding: 0;
|
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 {
|
*:focus {
|
||||||
outline-color: rgb(118, 93, 176);
|
outline-color: rgb(118, 93, 176);
|
||||||
}
|
}
|
||||||
|
17
src/Meta.js
17
src/Meta.js
@ -1,6 +1,11 @@
|
|||||||
export default class Meta {
|
export default class Meta {
|
||||||
|
static #title = 'Tabs';
|
||||||
static #devMode = null;
|
static #devMode = null;
|
||||||
|
|
||||||
|
static get title() {
|
||||||
|
return this.#title;
|
||||||
|
}
|
||||||
|
|
||||||
static isDevMode() {
|
static isDevMode() {
|
||||||
if (this.#devMode === null) {
|
if (this.#devMode === null) {
|
||||||
this.#devMode = process.argv.length > 2 && process.argv[2] === '--dev';
|
this.#devMode = process.argv.length > 2 && process.argv[2] === '--dev';
|
||||||
@ -9,4 +14,16 @@ export default class Meta {
|
|||||||
|
|
||||||
return 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,
|
autoHideMenuBar: true,
|
||||||
icon: iconPath,
|
icon: iconPath,
|
||||||
|
title: Meta.title,
|
||||||
});
|
});
|
||||||
window.maximize();
|
window.maximize();
|
||||||
window.on('closed', () => {
|
window.on('closed', () => {
|
||||||
@ -157,12 +158,43 @@ function createWindow() {
|
|||||||
window.webContents.send('deleteService', id);
|
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');
|
console.log('> App started');
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendData() {
|
function sendData() {
|
||||||
console.log('Syncing data');
|
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) {
|
function setActiveService(index) {
|
||||||
|
Loading…
Reference in New Issue
Block a user