Skip to content
Open
2 changes: 1 addition & 1 deletion app/lib/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class Window {

const maximized = this.windowConfig.get('maximized')
const bwOptions: BrowserWindowConstructorOptions = {
width: 800,
width: 1150,
height: 600,
title: 'Tabby',
minWidth: 400,
Expand Down
3 changes: 3 additions & 0 deletions tabby-core/src/api/profileProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export type PartialProfile<T extends Profile> = Omit<Omit<Omit<{

export interface ProfileGroup {
id: string
parentGroupId?: string
icon?: string
color?: string
name: string
profiles: PartialProfile<Profile>[]
defaults: any
Expand Down
182 changes: 94 additions & 88 deletions tabby-core/src/components/appRoot.component.pug
Original file line number Diff line number Diff line change
Expand Up @@ -5,107 +5,113 @@ title-bar(
[class.inset]='hostApp.platform == Platform.macOS && !hostWindow.isFullscreen'
)

.content(
*ngIf='ready',
[class.tabs-on-top]='config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "left" || config.store.appearance.tabsLocation == "right"',
[class.tabs-on-left]='hasVerticalTabs() && config.store.appearance.tabsLocation == "left"',
[class.tabs-titlebar-enabled]='isTitleBarNeeded()',
[class.tabs-on-right]='hasVerticalTabs() && config.store.appearance.tabsLocation == "right"',
)
.tab-bar(
*ngIf='!hostWindow.isFullscreen || config.store.appearance.tabsInFullscreen',
[class.tab-bar-no-controls-overlay]='hostApp.platform == Platform.macOS',
(dblclick)='!isTitleBarNeeded() && toggleMaximize()'
.window.h-100.d-flex

profile-tree(
*ngIf='ready && !config.store.hideProfileTree'
)
.inset.background(*ngIf='hostApp.platform == Platform.macOS \
&& !hostWindow.isFullscreen \
&& config.store.appearance.frame == "thin" \
&& (config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "left")')
.tabs(
cdkDropList,
[cdkDropListOrientation]='(config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "bottom") ? "horizontal" : "vertical"',
(cdkDropListDropped)='onTabsReordered($event)',
cdkAutoDropGroup='app-tabs'
)
tab-header(
*ngFor='let tab of app.tabs; let idx = index',
[index]='idx',
[tab]='tab',
[active]='tab == app.activeTab',
[@animateTab]='{value: "in", params: {size: targetTabSize}}',
[@.disabled]='hasVerticalTabs() || !config.store.accessibility.animations',
(click)='app.selectTab(tab)',
[class.fully-draggable]='hostApp.platform !== Platform.macOS',
[ngbTooltip]='tab.customTitle || tab.title'
)

.btn-group.background
.d-flex(
*ngFor='let button of leftToolbarButtons'
.content.main.h-100(
*ngIf='ready',
[class.tabs-on-top]='config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "left" || config.store.appearance.tabsLocation == "right"',
[class.tabs-on-left]='hasVerticalTabs() && config.store.appearance.tabsLocation == "left"',
[class.tabs-titlebar-enabled]='isTitleBarNeeded()',
[class.tabs-on-right]='hasVerticalTabs() && config.store.appearance.tabsLocation == "right"',
)
.tab-bar(
*ngIf='!hostWindow.isFullscreen || config.store.appearance.tabsInFullscreen',
[class.tab-bar-no-controls-overlay]='hostApp.platform == Platform.macOS',
(dblclick)='!isTitleBarNeeded() && toggleMaximize()'
)
.inset.background(*ngIf='hostApp.platform == Platform.macOS \
&& !hostWindow.isFullscreen \
&& config.store.appearance.frame == "thin" \
&& (config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "left")')
.tabs(
cdkDropList,
[cdkDropListOrientation]='(config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "bottom") ? "horizontal" : "vertical"',
(cdkDropListDropped)='onTabsReordered($event)',
cdkAutoDropGroup='app-tabs'
)
button.btn.btn-secondary.btn-tab-bar(
[ngbTooltip]='button.label',
(click)='button.run && button.run()',
[fastHtmlBind]='button.icon'
tab-header(
*ngFor='let tab of app.tabs; let idx = index',
[index]='idx',
[tab]='tab',
[active]='tab == app.activeTab',
[@animateTab]='{value: "in", params: {size: targetTabSize}}',
[@.disabled]='hasVerticalTabs() || !config.store.accessibility.animations',
(click)='app.selectTab(tab)',
[class.fully-draggable]='hostApp.platform !== Platform.macOS',
[ngbTooltip]='tab.customTitle || tab.title'
)

.d-flex(
ngbDropdown,
container='body',
#activeTransfersDropdown='ngbDropdown'
)
button.btn.btn-secondary.btn-tab-bar(
[hidden]='activeTransfers.length == 0',
[ngbTooltip]='"File transfers"|translate',
ngbDropdownToggle
) !{require('../icons/transfers.svg')}
transfers-menu(
ngbDropdownMenu,
[(transfers)]='activeTransfers',
(transfersChange)='onTransfersChange()'
.btn-group.background
.d-flex(
*ngFor='let button of leftToolbarButtons'
)
button.btn.btn-secondary.btn-tab-bar(
[ngbTooltip]='button.label',
(click)='button.run && button.run()',
[fastHtmlBind]='button.icon'
)

.btn-space.background(
[class.persistent]='config.store.appearance.frame == "thin"',
[class.drag]='config.store.appearance.frame == "thin" \
&& ((config.store.appearance.tabsLocation !== "left" && config.store.appearance.tabsLocation !== "right") || hostApp.platform !== Platform.macOS)'
)
.d-flex(
ngbDropdown,
container='body',
#activeTransfersDropdown='ngbDropdown'
)
button.btn.btn-secondary.btn-tab-bar(
[hidden]='activeTransfers.length == 0',
[ngbTooltip]='"File transfers"|translate',
ngbDropdownToggle
) !{require('../icons/transfers.svg')}
transfers-menu(
ngbDropdownMenu,
[(transfers)]='activeTransfers',
(transfersChange)='onTransfersChange()'
)

.btn-group.background
.d-flex(
*ngFor='let button of rightToolbarButtons'
.btn-space.background(
[class.persistent]='config.store.appearance.frame == "thin"',
[class.drag]='config.store.appearance.frame == "thin" \
&& ((config.store.appearance.tabsLocation !== "left" && config.store.appearance.tabsLocation !== "right") || hostApp.platform !== Platform.macOS)'
)
button.btn.btn-secondary.btn-tab-bar(
[ngbTooltip]='button.label',
(click)='button.run && button.run()',
[fastHtmlBind]='button.icon'

.btn-group.background
.d-flex(
*ngFor='let button of rightToolbarButtons'
)
button.btn.btn-secondary.btn-tab-bar(
[ngbTooltip]='button.label',
(click)='button.run && button.run()',
[fastHtmlBind]='button.icon'
)

button.btn.btn-secondary.btn-tab-bar.btn-update(
*ngIf='updatesAvailable',
[ngbTooltip]='"Update available - Click to install"|translate',
(click)='updater.update()'
) !{require('../icons/gift.svg')}
button.btn.btn-secondary.btn-tab-bar.btn-update(
*ngIf='updatesAvailable',
[ngbTooltip]='"Update available - Click to install"|translate',
(click)='updater.update()'
) !{require('../icons/gift.svg')}

window-controls.background(
*ngIf='config.store.appearance.frame == "thin" \
&& config.store.appearance.tabsLocation !== "left" \
&& config.store.appearance.tabsLocation !== "right" \
&& hostApp.platform == Platform.Linux',
)
window-controls.background(
*ngIf='config.store.appearance.frame == "thin" \
&& config.store.appearance.tabsLocation !== "left" \
&& config.store.appearance.tabsLocation !== "right" \
&& hostApp.platform == Platform.Linux',
)

div.window-controls-spacer(
*ngIf='config.store.appearance.frame == "thin" && (hostApp.platform == Platform.Windows) && (config.store.appearance.tabsLocation == "top")',
)
.content
start-page.content-tab.content-tab-active(*ngIf='ready && app.tabs.length == 0')
div.window-controls-spacer(
*ngIf='config.store.appearance.frame == "thin" && (hostApp.platform == Platform.Windows) && (config.store.appearance.tabsLocation == "top")',
)
.content
start-page.content-tab.content-tab-active(*ngIf='ready && app.tabs.length == 0')

tab-body.content-tab(
#tabBodies,
*ngFor='let tab of unsortedTabs',
[class.content-tab-active]='tab == app.activeTab',
[active]='tab == app.activeTab',
[tab]='tab',
)
tab-body.content-tab(
#tabBodies,
*ngFor='let tab of unsortedTabs',
[class.content-tab-active]='tab == app.activeTab',
[active]='tab == app.activeTab',
[tab]='tab',
)

ng-template(ngbModalContainer)
2 changes: 1 addition & 1 deletion tabby-core/src/components/appRoot.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ $tab-border-radius: 4px;
}

.content {
width: 100vw;
width: 100%;
flex: 1 1 0;
min-height: 0;
display: flex;
Expand Down
53 changes: 53 additions & 0 deletions tabby-core/src/components/profileTree.component.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.div.p-2.h-100.d-flex.flex-column
input.form-control.form-control-sm.mb-1(
type='text',
[(ngModel)]='filter',
placeholder='Filter',
(ngModelChange)='onFilterChange()'
)

.profile-tree.h-100
.d-flex.flex-column.p-2.profile-tree-container

ng-container(*ngFor='let group of rootGroups')
ng-container(*ngTemplateOutlet='recursiveGroup; context: {$implicit: group, depth: 0}')

ng-template(#recursiveGroup let-group let-depth='depth')
a.tree-item(
(click)='toggleGroupCollapse(group)',
[style.paddingLeft.px]='depth * 20',
(contextmenu)='groupContextMenu(group, $event)',
href='#'
)
.fw-20
.fa.fa-fw.fas.fa-chevron-right.ms-1.text-muted(*ngIf='group.collapsed')
.fa.fa-fw.fas.fa-chevron-down.ms-1.text-muted(*ngIf='!group.collapsed')
.fw-20
profile-icon.ms-1([icon]='group.icon ?? "far fa-folder"', [color]='group?.color')
span.ms-2.me-auto {{ group.name || ("Ungrouped"|translate) }}

ng-container(*ngIf='!group.collapsed')
ng-container(*ngFor='let profile of group.profiles')
a.tree-item(
(dblclick)='launchProfile(profile)',
[style.paddingLeft.px]='(depth + 1) * 20',
(contextmenu)='profileContextMenu(profile, $event)',
href='#'
)
.fw-20
profile-icon.ms-1([icon]='profile.icon', [color]='profile.color')
span.ms-2.no-wrap {{ profile.name }}

.actions
.action((click)='launchProfile(profile)')
.fa.fa-fw.fas.fa-play
//- .action
//- .fa.fa-fw.fas.fa-eject



ng-container(*ngFor='let child of group.children')
ng-container(*ngTemplateOutlet='recursiveGroup; context: {$implicit: child, depth: depth + 1}')
.grabber(
(mousedown)="startResize($event)"
)
90 changes: 90 additions & 0 deletions tabby-core/src/components/profileTree.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
:host {
background-color: var(--theme-bg-more-2);
height: 100vh;
position: relative;
border-right: 1px solid var(--theme-secondary);

}

input {
border: 1px solid var(--theme-secondary);
}

.profile-tree {
max-height: 100%;
overflow-y: scroll;
scrollbar-width: none;

.fw-20 {
width: 20px;
}

.fas.fa-chevron-right,
.fas.fa-chevron-down {
font-size: .7rem;
}

profile-icon {
width: 15px;
height: 15px;
}

.tree-item {
text-decoration: none;
color: inherit;
padding: calc(.25rem * calc(var(--spaciness) * var(--spaciness))) 0;
padding-right: .25rem;
border-radius: .3rem;
cursor: pointer;
overflow: hidden;
position: relative;
display: flex;
align-items: center;
&:hover {
background-color: var(--theme-secondary);
.actions {
display: flex;
}
}

.actions {
display: none;
position: absolute;
right: 0;
flex-direction: row;
gap: calc(.25rem * calc(var(--spaciness) * var(--spaciness)));
height: 100%;
padding: calc(.25rem * calc(var(--spaciness) * var(--spaciness)));
background: var(--theme-secondary);
.action {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-size: 0.6rem;
background-color: var(--theme-bg-more-2);
border-radius: .2rem;
padding: 0 calc(.34rem * calc(var(--spaciness) * var(--spaciness)));
&:hover {
background-color: var(--theme-primary);
color: var(--theme-secondary);
}
}
}
}
}


.grabber {
position: absolute;
z-index: 1;
width: 7px;
height: 25px;
display: block;
background-color: var(--theme-secondary-fg);
border: 3px solid var(--theme-secondary);
border-radius: 0.4rem;
top: 50%;
right: -4px;
cursor: col-resize;
}
Loading
Loading