mirror of
https://github.com/Eugeny/tabby-web.git
synced 2026-08-17 06:46:05 +01:00
wip
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-web-container",
|
"name": "tabby-web",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
"@angular/forms": "^11.0.0",
|
"@angular/forms": "^11.0.0",
|
||||||
"@angular/platform-browser": "^11.0.0",
|
"@angular/platform-browser": "^11.0.0",
|
||||||
"@angular/platform-browser-dynamic": "^11.0.0",
|
"@angular/platform-browser-dynamic": "^11.0.0",
|
||||||
|
"@angular/router": "^11.0.0",
|
||||||
"@fortawesome/angular-fontawesome": "0.8",
|
"@fortawesome/angular-fontawesome": "0.8",
|
||||||
"@fortawesome/fontawesome-free": "^5.7.2",
|
"@fortawesome/fontawesome-free": "^5.7.2",
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
||||||
|
|||||||
@@ -4,12 +4,21 @@ import { BrowserModule } from '@angular/platform-browser'
|
|||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
|
||||||
import { CommonModule } from '@angular/common'
|
import { CommonModule } from '@angular/common'
|
||||||
import { FormsModule } from '@angular/forms'
|
import { FormsModule } from '@angular/forms'
|
||||||
|
import { RouterModule } from '@angular/router'
|
||||||
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http'
|
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http'
|
||||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'
|
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'
|
||||||
import { AppComponent } from './components/app.component'
|
import { AppComponent } from './components/app.component'
|
||||||
import { MainComponent } from './components/main.component'
|
import { MainComponent } from './components/main.component'
|
||||||
import { ConfigModalComponent } from './components/configModal.component'
|
import { ConfigModalComponent } from './components/configModal.component'
|
||||||
import { SettingsModalComponent } from './components/settingsModal.component'
|
import { SettingsModalComponent } from './components/settingsModal.component'
|
||||||
|
import { HomeComponent } from './components/home.component'
|
||||||
|
import { LoginComponent } from './components/login.component'
|
||||||
|
|
||||||
|
const ROUTES = [
|
||||||
|
{ path: '', component: HomeComponent },
|
||||||
|
{ path: 'app', component: MainComponent },
|
||||||
|
{ path: 'login', component: LoginComponent },
|
||||||
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -22,10 +31,13 @@ import { SettingsModalComponent } from './components/settingsModal.component'
|
|||||||
NgbDropdownModule,
|
NgbDropdownModule,
|
||||||
NgbModalModule,
|
NgbModalModule,
|
||||||
FontAwesomeModule,
|
FontAwesomeModule,
|
||||||
|
RouterModule.forRoot(ROUTES),
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
MainComponent,
|
MainComponent,
|
||||||
|
HomeComponent,
|
||||||
|
LoginComponent,
|
||||||
ConfigModalComponent,
|
ConfigModalComponent,
|
||||||
SettingsModalComponent,
|
SettingsModalComponent,
|
||||||
],
|
],
|
||||||
|
|||||||
BIN
src/assets/screenshots/serial.png
Normal file
BIN
src/assets/screenshots/serial.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 536 KiB |
BIN
src/assets/screenshots/ssh.png
Normal file
BIN
src/assets/screenshots/ssh.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 371 KiB |
BIN
src/assets/screenshots/tabs.png
Normal file
BIN
src/assets/screenshots/tabs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 666 KiB |
BIN
src/assets/screenshots/window.png
Normal file
BIN
src/assets/screenshots/window.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 728 KiB |
@@ -1,10 +1 @@
|
|||||||
main(*ngIf='ready && loggedIn')
|
router-outlet
|
||||||
.login-view(*ngIf='ready && !loggedIn')
|
|
||||||
.buttons
|
|
||||||
a.btn(
|
|
||||||
*ngFor='let provider of providers',
|
|
||||||
[class]='provider.cls',
|
|
||||||
href='/api/1/auth/social/login/{{provider.id}}'
|
|
||||||
)
|
|
||||||
fa-icon([icon]='provider.icon', [fixedWidth]='true')
|
|
||||||
span Log in with {{provider.name}}
|
|
||||||
|
|||||||
@@ -1,33 +1,9 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { LoginService } from '../services/login.service'
|
|
||||||
|
|
||||||
import { faGithub, faGitlab, faGoogle, faMicrosoft } from '@fortawesome/free-brands-svg-icons'
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app',
|
selector: 'app',
|
||||||
templateUrl: './app.component.pug',
|
templateUrl: './app.component.pug',
|
||||||
styleUrls: ['./app.component.scss'],
|
// styleUrls: ['./app.component.scss'],
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
loggedIn: any
|
|
||||||
ready = false
|
|
||||||
|
|
||||||
providers = [
|
|
||||||
{ name: 'GitHub', icon: faGithub, cls: 'btn-primary', id: 'github' },
|
|
||||||
{ name: 'GitLab', icon: faGitlab, cls: 'btn-warning', id: 'gitlab' },
|
|
||||||
{ name: 'Google', icon: faGoogle, cls: 'btn-secondary', id: 'google-oauth2' },
|
|
||||||
{ name: 'Microsoft', icon: faMicrosoft, cls: 'btn-light', id: 'microsoft-graph' },
|
|
||||||
]
|
|
||||||
|
|
||||||
constructor (
|
|
||||||
private loginService: LoginService,
|
|
||||||
) {
|
|
||||||
this.providers = [this.providers[0]] // only keep GH for now
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit () {
|
|
||||||
await this.loginService.ready$.toPromise()
|
|
||||||
this.loggedIn = !!this.loginService.user
|
|
||||||
this.ready = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
96
src/components/home.component.pug
Normal file
96
src/components/home.component.pug
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
.top-half
|
||||||
|
.navbar
|
||||||
|
img.brand(src='{{_logo}}')
|
||||||
|
.me-auto
|
||||||
|
a.btn.btn-primary([href]='releaseURL', target='_blank')
|
||||||
|
fa-icon([icon]='_downloadIcon', [fixedWidth]='true')
|
||||||
|
span Download
|
||||||
|
a.btn.btn-secondary(routerLink='/login')
|
||||||
|
fa-icon([icon]='_loginIcon', [fixedWidth]='true')
|
||||||
|
span Login
|
||||||
|
|
||||||
|
.container
|
||||||
|
.intro
|
||||||
|
h1 Hey.
|
||||||
|
div My name is Eugene and I've built a nice terminal app, #[em.ms-1 just for you].
|
||||||
|
div Crossplatform, local, SSH, serial - it's all there.
|
||||||
|
div Go on, try it out 👇
|
||||||
|
|
||||||
|
iframe(#iframe)
|
||||||
|
|
||||||
|
.bottom-half
|
||||||
|
.container
|
||||||
|
.d-flex.m-auto
|
||||||
|
a.btn.btn-lg.btn-primary.ms-auto.me-3([href]='releaseURL', target='_blank')
|
||||||
|
fa-icon([icon]='_downloadIcon', [fixedWidth]='true')
|
||||||
|
span Latest release
|
||||||
|
|
||||||
|
a.btn.btn-lg.btn-secondary.me-auto([href]='githubURL', target='_blank')
|
||||||
|
fa-icon([icon]='_githubIcon', [fixedWidth]='true')
|
||||||
|
span GitHub
|
||||||
|
|
||||||
|
.quotes
|
||||||
|
.quote
|
||||||
|
.text "reasonable"
|
||||||
|
.author — Michael
|
||||||
|
|
||||||
|
.quote
|
||||||
|
.text "cool"
|
||||||
|
.author — some dude on my ko-fi
|
||||||
|
|
||||||
|
.quote
|
||||||
|
.text "very cool"
|
||||||
|
.author — datsukan
|
||||||
|
|
||||||
|
.section.section-a
|
||||||
|
.container
|
||||||
|
.row
|
||||||
|
.col
|
||||||
|
img.screenshot([src]='screenshots.window')
|
||||||
|
.col
|
||||||
|
h1 The important stuff
|
||||||
|
ul
|
||||||
|
li Runs on #[strong Windows, Mac and Linux]
|
||||||
|
li Integrated #[strong SSH client] with a connection manager
|
||||||
|
li Integrated #[strong serial terminal]
|
||||||
|
li PowerShell, PS Core, WSL, Git-Bash, Cygwin, Cmder and CMD support
|
||||||
|
li Full #[strong Unicode support] including double-width characters
|
||||||
|
li File transfer from/to SSH sessions via #[strong SFTP and Zmodem]
|
||||||
|
li Theming and color schemes
|
||||||
|
li Fully #[strong configurable shortcuts] and multi-chord shortcuts
|
||||||
|
li #[strong Remembers your tabs] and split panes
|
||||||
|
li Proper shell experience on Windows including #[strong tab completion]
|
||||||
|
li Integrated #[strong encrypted container] for SSH secrets and configuration
|
||||||
|
|
||||||
|
.section.section-b
|
||||||
|
.container
|
||||||
|
.row
|
||||||
|
.col
|
||||||
|
h1 Terminal features
|
||||||
|
ul
|
||||||
|
li Multiple #[strong nested panes]
|
||||||
|
li #[strong Progress bars] and activity notifications for tabs
|
||||||
|
li Terminus remembers open tabs and panes where you left off
|
||||||
|
li Tabs on #[strong any side of the window]
|
||||||
|
li Optional #[strong quake mode] (terminal docked to a side of the screen)
|
||||||
|
li Optional #[strong global hotkey] to focus/hide the terminal
|
||||||
|
li Bracketed paste
|
||||||
|
.col
|
||||||
|
img.screenshot([src]='screenshots.tabs')
|
||||||
|
|
||||||
|
.section.section-a
|
||||||
|
.container
|
||||||
|
.row
|
||||||
|
.col
|
||||||
|
img.screenshot([src]='screenshots.ssh')
|
||||||
|
.col
|
||||||
|
h1 SSH
|
||||||
|
ul
|
||||||
|
li SSH2 client with a connection manager
|
||||||
|
li #[strong SFTP and Zmodem] file transfers
|
||||||
|
li #[strong X11] and #[strong port forwarding]
|
||||||
|
li Jump hosts
|
||||||
|
li #[strong Agent forwarding] - including Pageant and Windows native OpenSSH Agent
|
||||||
|
li Login scripts
|
||||||
|
li Optional built-in #[strong password manager] with a master passphrase
|
||||||
|
li #[strong Proxy command] support
|
||||||
95
src/components/home.component.scss
Normal file
95
src/components/home.component.scss
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
@import "../theme/vars.scss";
|
||||||
|
|
||||||
|
:host {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-half {
|
||||||
|
background: linear-gradient(#0c141c, #15202b);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
display: flex;
|
||||||
|
padding: 15px 30px;
|
||||||
|
|
||||||
|
a, button {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: $font-family-monospace;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 48px;
|
||||||
|
text-shadow: 0 0 1px black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro {
|
||||||
|
width: 60vw;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
position: relative;
|
||||||
|
top: 20vw;
|
||||||
|
margin-top: -18vw;
|
||||||
|
|
||||||
|
width: 60vw;
|
||||||
|
height: 42vw;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 0 2px black, 0 0 50px #6ef2ff05, 0 0 150px #6854ff14;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-half {
|
||||||
|
padding-top: 24vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quotes {
|
||||||
|
margin: 30px 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.quote {
|
||||||
|
margin: 0 30px;
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 50px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
background: #9400ff57;
|
||||||
|
font-weight: normal;
|
||||||
|
padding: 2px 7px;
|
||||||
|
text-shadow: 0 0 2px black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
padding: 30px 0;
|
||||||
|
|
||||||
|
.screenshot {
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-a {
|
||||||
|
background: rgba(0, 0, 0, .5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-b {
|
||||||
|
}
|
||||||
81
src/components/home.component.ts
Normal file
81
src/components/home.component.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import * as semverGT from 'semver/functions/gt'
|
||||||
|
import { HttpClient } from '@angular/common/http'
|
||||||
|
import { Component, ElementRef, ViewChild } from '@angular/core'
|
||||||
|
import { Version } from '../api'
|
||||||
|
import { faDownload, faSignInAlt } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { faGithub } from '@fortawesome/free-brands-svg-icons'
|
||||||
|
|
||||||
|
class DemoConnector {
|
||||||
|
constructor (targetWindow: Window, private version: Version) {
|
||||||
|
targetWindow['terminusWebDemoDataPath'] = `${this.getDistURL()}/${version.version}/terminus-web-demo/data`
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadConfig (): Promise<string> {
|
||||||
|
return '{}'
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveConfig (content: string): Promise<void> {
|
||||||
|
}
|
||||||
|
|
||||||
|
getAppVersion (): string {
|
||||||
|
return this.version.version
|
||||||
|
}
|
||||||
|
|
||||||
|
getDistURL (): string {
|
||||||
|
return '/app-dist'
|
||||||
|
}
|
||||||
|
|
||||||
|
getPluginsToLoad (): string[] {
|
||||||
|
return [
|
||||||
|
'terminus-core',
|
||||||
|
'terminus-settings',
|
||||||
|
'terminus-terminal',
|
||||||
|
'terminus-community-color-schemes',
|
||||||
|
'terminus-web',
|
||||||
|
'terminus-web-demo',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'home',
|
||||||
|
templateUrl: './home.component.pug',
|
||||||
|
styleUrls: ['./home.component.scss'],
|
||||||
|
})
|
||||||
|
export class HomeComponent {
|
||||||
|
@ViewChild('iframe') iframe: ElementRef
|
||||||
|
connector: DemoConnector
|
||||||
|
githubURL = 'https://github.com/Eugeny/terminus'
|
||||||
|
releaseURL = `${this.githubURL}/releases/latest`
|
||||||
|
_logo = require('../assets/logo.svg')
|
||||||
|
_downloadIcon = faDownload
|
||||||
|
_loginIcon = faSignInAlt
|
||||||
|
_githubIcon = faGithub
|
||||||
|
screenshots = {
|
||||||
|
window: require('../assets/screenshots/window.png'),
|
||||||
|
tabs: require('../assets/screenshots/tabs.png'),
|
||||||
|
ssh: require('../assets/screenshots/ssh.png'),
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor (private http: HttpClient) {
|
||||||
|
window.addEventListener('message', this.connectorRequestHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
connectorRequestHandler = event => {
|
||||||
|
if (event.data === 'request-connector') {
|
||||||
|
this.iframe.nativeElement.contentWindow['__connector__'] = this.connector
|
||||||
|
this.iframe.nativeElement.contentWindow.postMessage('connector-ready', '*')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngAfterViewInit () {
|
||||||
|
const versions = await this.http.get('/api/1/versions').toPromise()
|
||||||
|
versions.sort((a, b) => semverGT(a, b))
|
||||||
|
this.connector = new DemoConnector(this.iframe.nativeElement.contentWindow, versions[0])
|
||||||
|
this.iframe.nativeElement.src = '/terminal'
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy () {
|
||||||
|
window.removeEventListener('message', this.connectorRequestHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/components/login.component.pug
Normal file
10
src/components/login.component.pug
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
main(*ngIf='ready && loggedIn')
|
||||||
|
.login-view(*ngIf='ready && !loggedIn')
|
||||||
|
.buttons
|
||||||
|
a.btn(
|
||||||
|
*ngFor='let provider of providers',
|
||||||
|
[class]='provider.cls',
|
||||||
|
href='/api/1/auth/social/login/{{provider.id}}'
|
||||||
|
)
|
||||||
|
fa-icon([icon]='provider.icon', [fixedWidth]='true')
|
||||||
|
span Log in with {{provider.name}}
|
||||||
33
src/components/login.component.ts
Normal file
33
src/components/login.component.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { LoginService } from '../services/login.service'
|
||||||
|
|
||||||
|
import { faGithub, faGitlab, faGoogle, faMicrosoft } from '@fortawesome/free-brands-svg-icons'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'login',
|
||||||
|
templateUrl: './login.component.pug',
|
||||||
|
styleUrls: ['./login.component.scss'],
|
||||||
|
})
|
||||||
|
export class LoginComponent {
|
||||||
|
loggedIn: any
|
||||||
|
ready = false
|
||||||
|
|
||||||
|
providers = [
|
||||||
|
{ name: 'GitHub', icon: faGithub, cls: 'btn-primary', id: 'github' },
|
||||||
|
{ name: 'GitLab', icon: faGitlab, cls: 'btn-warning', id: 'gitlab' },
|
||||||
|
{ name: 'Google', icon: faGoogle, cls: 'btn-secondary', id: 'google-oauth2' },
|
||||||
|
{ name: 'Microsoft', icon: faMicrosoft, cls: 'btn-light', id: 'microsoft-graph' },
|
||||||
|
]
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private loginService: LoginService,
|
||||||
|
) {
|
||||||
|
this.providers = [this.providers[0]] // only keep GH for now
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnInit () {
|
||||||
|
await this.loginService.ready$.toPromise()
|
||||||
|
this.loggedIn = !!this.loginService.user
|
||||||
|
this.ready = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import { ConfigModalComponent } from './configModal.component'
|
|||||||
import { ConfigService } from '../services/config.service'
|
import { ConfigService } from '../services/config.service'
|
||||||
import { combineLatest } from 'rxjs'
|
import { combineLatest } from 'rxjs'
|
||||||
import { Config, Version } from '../api'
|
import { Config, Version } from '../api'
|
||||||
|
import { Router } from '@angular/router'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'main',
|
selector: 'main',
|
||||||
@@ -35,16 +36,25 @@ export class MainComponent {
|
|||||||
public loginService: LoginService,
|
public loginService: LoginService,
|
||||||
private ngbModal: NgbModal,
|
private ngbModal: NgbModal,
|
||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
|
private router: Router,
|
||||||
) {
|
) {
|
||||||
window.addEventListener('message', event => {
|
window.addEventListener('message', this.connectorRequestHandler)
|
||||||
if (event.data === 'request-connector') {
|
}
|
||||||
this.iframe.nativeElement.contentWindow['__connector__'] = this.appConnector
|
|
||||||
this.iframe.nativeElement.contentWindow.postMessage('connector-ready', '*')
|
connectorRequestHandler = event => {
|
||||||
}
|
if (event.data === 'request-connector') {
|
||||||
})
|
this.iframe.nativeElement.contentWindow['__connector__'] = this.appConnector
|
||||||
|
this.iframe.nativeElement.contentWindow.postMessage('connector-ready', '*')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngAfterViewInit () {
|
async ngAfterViewInit () {
|
||||||
|
await this.loginService.ready$.toPromise()
|
||||||
|
if (!this.loginService.user) {
|
||||||
|
this.router.navigate(['/login'])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
combineLatest(
|
combineLatest(
|
||||||
this.config.activeConfig$,
|
this.config.activeConfig$,
|
||||||
this.config.activeVersion$
|
this.config.activeVersion$
|
||||||
@@ -58,6 +68,10 @@ export class MainComponent {
|
|||||||
await this.config.selectDefaultConfig()
|
await this.config.selectDefaultConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy () {
|
||||||
|
window.removeEventListener('message', this.connectorRequestHandler)
|
||||||
|
}
|
||||||
|
|
||||||
unloadApp () {
|
unloadApp () {
|
||||||
this.showApp = false
|
this.showApp = false
|
||||||
this.iframe.nativeElement.src = 'about:blank'
|
this.iframe.nativeElement.src = 'about:blank'
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
doctype html
|
doctype html
|
||||||
html
|
html
|
||||||
head
|
head
|
||||||
|
base(href='/')
|
||||||
meta(name='viewport', content='initial-scale=1, minimal-ui, shrink-to-fit=no')
|
meta(name='viewport', content='initial-scale=1, minimal-ui, shrink-to-fit=no')
|
||||||
link(href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400", rel="stylesheet")
|
|
||||||
script(src='/build/index.js', defer)
|
script(src='/build/index.js', defer)
|
||||||
title Terminus
|
title Terminus
|
||||||
body
|
body
|
||||||
|
|||||||
@@ -119,6 +119,21 @@ export class AppConnectorService {
|
|||||||
return this.version.version
|
return this.version.version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDistURL (): string {
|
||||||
|
return '../app-dist'
|
||||||
|
}
|
||||||
|
|
||||||
|
getPluginsToLoad (): string[] {
|
||||||
|
return [
|
||||||
|
'terminus-core',
|
||||||
|
'terminus-settings',
|
||||||
|
'terminus-terminal',
|
||||||
|
'terminus-ssh',
|
||||||
|
'terminus-community-color-schemes',
|
||||||
|
'terminus-web',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
createSocket () {
|
createSocket () {
|
||||||
return new SocketProxy(this)
|
return new SocketProxy(this)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,7 @@ body {
|
|||||||
overscroll-behavior: none;
|
overscroll-behavior: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import "~source-code-pro/source-code-pro.css";
|
||||||
|
@import "~source-sans-pro/source-sans-pro.css";
|
||||||
|
|
||||||
@import "./theme/index.scss"
|
@import "./theme/index.scss"
|
||||||
|
|||||||
@@ -27,35 +27,32 @@ async function start () {
|
|||||||
return window['module'].exports
|
return window['module'].exports
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseUrl = `../app-dist/${appVersion}`
|
const baseUrl = `${connector.getDistURL()}/${appVersion}`
|
||||||
await webRequire(`${baseUrl}/web/dist/preload.js`)
|
await webRequire(`${baseUrl}/web/dist/preload.js`)
|
||||||
await webRequire(`${baseUrl}/web/dist/bundle.js`)
|
await webRequire(`${baseUrl}/web/dist/bundle.js`)
|
||||||
|
|
||||||
const terminus = window['Terminus']
|
const terminus = window['Terminus']
|
||||||
|
|
||||||
const pluginModules = []
|
const pluginModules = []
|
||||||
for (const plugin of [
|
for (const plugin of connector.getPluginsToLoad()) {
|
||||||
'terminus-core',
|
|
||||||
'terminus-settings',
|
|
||||||
'terminus-terminal',
|
|
||||||
'terminus-ssh',
|
|
||||||
'terminus-community-color-schemes',
|
|
||||||
'terminus-web',
|
|
||||||
]) {
|
|
||||||
pluginModules.push(await terminus.loadPlugin(`${baseUrl}/${plugin}`))
|
pluginModules.push(await terminus.loadPlugin(`${baseUrl}/${plugin}`))
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelector('app-root')['style'].display = 'flex'
|
document.querySelector('app-root')['style'].display = 'flex'
|
||||||
|
|
||||||
const config = connector.loadConfig()
|
const config = connector.loadConfig()
|
||||||
terminus.bootstrap(pluginModules, {
|
terminus.bootstrap({
|
||||||
config,
|
packageModules: pluginModules,
|
||||||
executable: 'web',
|
bootstrapData: {
|
||||||
isFirstWindow: true,
|
config,
|
||||||
windowID: 1,
|
executable: 'web',
|
||||||
installedPlugins: [],
|
isFirstWindow: true,
|
||||||
userPluginsPath: '/',
|
windowID: 1,
|
||||||
|
installedPlugins: [],
|
||||||
|
userPluginsPath: '/',
|
||||||
|
},
|
||||||
|
debugMode: false,
|
||||||
|
connector,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
start()
|
start()
|
||||||
|
|||||||
@@ -66,3 +66,13 @@
|
|||||||
.modal-footer {
|
.modal-footer {
|
||||||
background: #00000030;
|
background: #00000030;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a, button {
|
||||||
|
fa-icon {
|
||||||
|
opacity: .75;
|
||||||
|
}
|
||||||
|
|
||||||
|
fa-icon + * {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ class AppVersionViewSet(ListModelMixin, GenericViewSet):
|
|||||||
lookup_field = 'id'
|
lookup_field = 'id'
|
||||||
lookup_value_regex = r'[\w\d.-]+'
|
lookup_value_regex = r'[\w\d.-]+'
|
||||||
queryset = ''
|
queryset = ''
|
||||||
permission_classes = [IsAuthenticated]
|
|
||||||
|
|
||||||
def _get_versions(self):
|
def _get_versions(self):
|
||||||
return [AppVersion(version=x) for x in os.listdir(settings.APP_DIST_PATH)]
|
return [AppVersion(version=x) for x in os.listdir(settings.APP_DIST_PATH)]
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ urlpatterns = [
|
|||||||
path('api/1/auth/logout', api.LogoutView.as_view()),
|
path('api/1/auth/logout', api.LogoutView.as_view()),
|
||||||
path('api/1/user', api.UserViewSet.as_view({'get': 'retrieve', 'put': 'update'})),
|
path('api/1/user', api.UserViewSet.as_view({'get': 'retrieve', 'put': 'update'})),
|
||||||
|
|
||||||
path('', views.IndexView.as_view()),
|
re_path('^(|login|app)$', views.IndexView.as_view()),
|
||||||
|
|
||||||
path('terminal', views.TerminalView.as_view()),
|
path('terminal', views.TerminalView.as_view()),
|
||||||
path('app-dist/<version>/<path:path>', views.AppDistView.as_view()),
|
path('app-dist/<version>/<path:path>', views.AppDistView.as_view()),
|
||||||
path('build/<path:path>', views.BuildView.as_view()),
|
path('build/<path:path>', views.BuildView.as_view()),
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ AUTHENTICATION_BACKENDS = (
|
|||||||
|
|
||||||
SOCIAL_AUTH_GITHUB_SCOPE = ['read:user', 'user:email']
|
SOCIAL_AUTH_GITHUB_SCOPE = ['read:user', 'user:email']
|
||||||
|
|
||||||
LOGIN_REDIRECT_URL = '/'
|
LOGIN_REDIRECT_URL = '/app'
|
||||||
|
|
||||||
APP_DIST_PATH = BASE_DIR / 'app-dist'
|
APP_DIST_PATH = BASE_DIR / 'app-dist'
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.0.0"
|
tslib "^2.0.0"
|
||||||
|
|
||||||
|
"@angular/router@^11.0.0":
|
||||||
|
version "11.2.14"
|
||||||
|
resolved "https://registry.yarnpkg.com/@angular/router/-/router-11.2.14.tgz#4a8eb6d010a1a45c8e7f0c3b5b705959bdb69294"
|
||||||
|
integrity sha512-3aYBmj+zrEL9yf/ntIQxHIYaWShZOBKP3U07X2mX+TPMpGlvHDnR7L6bWhQVZwewzMMz7YVR16ldg50IFuAlfA==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
"@babel/code-frame@^7.14.5":
|
"@babel/code-frame@^7.14.5":
|
||||||
version "7.14.5"
|
version "7.14.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
|
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
|
||||||
|
|||||||
Reference in New Issue
Block a user