Verified Commit bde64796 authored by Vincent Jaquet's avatar Vincent Jaquet
Browse files

Add release version 1.0.0

parent 23b29288
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 5 Chrome version
# https://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = tab
insert_final_newline = true
max_line_length = 140
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/electron",
"plugin:import/typescript"
],
"parser": "@typescript-eslint/parser"
}
# tp_secuinfo_electron
# TP SecuInfo - Electron
## Development
```shell
# 1) Install dependencies
$ npm install
```
- Option 1: Build and run in Electron
```shell
$ npm start
```
- Option 2: Run as a classis Angular webapp
```shell
$ npm run ng:start
```
## Production build
Constraints:
- A Windows PC is required to build a Windows version
- A macOS PC is required to build a macOS version
- A Linux PC is required to build a Linux version
```shell
# 1) Install dependencies
$ npm ci
# 2) Build Angular app and Electron app
$ rm -r dist/
$ npm run ng:build
$ npm run el:build
# 3) Package for your OS
$ npm run el:make-darwin # For macOS
$ npm run el:make-win32 # For Windows
$ npm run el:make-linux # For Linux
```
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"tp-secuinfo": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
},
"@schematics/angular:application": {
"strict": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/tp-secuinfo",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*",
"input": "./node_modules/ace-builds/src-min-noconflict",
"output": "/assets/ace"
}
],
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/bootstrap-icons/font/bootstrap-icons.css",
"src/styles.scss"
],
"scripts": [
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "4mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"subresourceIntegrity": true,
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "tp-secuinfo:build:production"
},
"development": {
"browserTarget": "tp-secuinfo:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "tp-secuinfo:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*",
"input": "./node_modules/ace-builds/src-min-noconflict",
"output": "/assets/ace"
}
],
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/bootstrap-icons/font/bootstrap-icons.css",
"src/styles.scss"
],
"scripts": [
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
]
}
}
}
}
},
"defaultProject": "tp-secuinfo"
}
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/tp-secuinfo'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
This diff is collapsed.
{
"name": "tp-secuinfo",
"productName": "tp-secuinfo",
"version": "1.0.0",
"description": "TP SecuInfo",
"main": "dist/electron/index.js",
"license": "CC BY-NC-ND 4.0",
"author": {
"name": "HumanTech Institute"
},
"scripts": {
"start": "ng build && tsc -p tsconfig.electron.json && electron-forge start",
"ng": "ng",
"ng:start": "ng serve",
"ng:build": "ng build",
"ng:watch": "ng build --watch --configuration development",
"ng:test": "ng test",
"el:start": "tsc -p tsconfig.electron.json && electron-forge start",
"el:build": "tsc -p tsconfig.electron.json",
"el:package": "electron-forge package",
"el:make-win32": "electron-forge make --platform win32",
"el:make-linux": "electron-forge make --platform linux",
"el:make-darwin": "electron-forge make --platform darwin",
"lint": "eslint --ext .ts ."
},
"config": {
"forge": {
"packagerConfig": {
"asar": true
},
"makers": [
{
"name": "@electron-forge/maker-squirrel",
"config": {
"name": "tp-secuinfo"
}
},
{
"name": "@electron-forge/maker-zip",
"platforms": [
"win32",
"darwin"
]
},
{
"name": "@electron-forge/maker-deb",
"config": {}
},
{
"name": "@electron-forge/maker-rpm",
"config": {}
}
]
}
},
"devDependencies": {
"@angular-devkit/build-angular": "~12.2.11",
"@angular/cli": "~12.2.11",
"@angular/compiler-cli": "~12.2.11",
"@electron-forge/cli": "^6.0.0-beta.61",
"@electron-forge/maker-deb": "^6.0.0-beta.61",
"@electron-forge/maker-rpm": "^6.0.0-beta.61",
"@electron-forge/maker-squirrel": "^6.0.0-beta.61",
"@electron-forge/maker-zip": "^6.0.0-beta.61",
"@types/jasmine": "~3.10.0",
"@types/js-beautify": "^1.13.3",
"@types/node": "^16.11.3",
"@typescript-eslint/eslint-plugin": "^4.17.0",
"@typescript-eslint/parser": "^4.17.0",
"electron": "15.3.0",
"eslint": "^7.6.0",
"eslint-plugin-import": "^2.20.0",
"jasmine-core": "~3.10.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"typescript": "~4.3.2"
},
"dependencies": {
"@angular/animations": "~12.2.11",
"@angular/common": "~12.2.11",
"@angular/compiler": "~12.2.11",
"@angular/core": "~12.2.11",
"@angular/forms": "~12.2.11",
"@angular/platform-browser": "~12.2.11",
"@angular/platform-browser-dynamic": "~12.2.11",
"@angular/router": "~12.2.11",
"ace-builds": "git://github.com/ajaxorg/ace-builds.git#v1.4.13",
"bootstrap": "^5.1.3",
"bootstrap-icons": "^1.6.0",
"dexie": "^3.0.3",
"electron-squirrel-startup": "^1.0.0",
"js-beautify": "^1.14.0",
"rxjs": "~6.6.0",
"tslib": "^2.2.0",
"zone.js": "~0.11.4"
}
}
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ChallengeComponent } from './challenge/challenge.component';
const routes: Routes = [
{ path: 'challenges/:id', component: ChallengeComponent },
{ path: '**', redirectTo: '/challenges/0' },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
<router-outlet></router-outlet>
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
title = 'tp-secuinfo';
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ChallengeComponent } from './challenge/challenge.component';
import { EditorComponent } from './editor/editor.component';
import { EditorsComponent } from './editors/editors.component';
@NgModule({
declarations: [
AppComponent,
ChallengeComponent,
EditorComponent,
EditorsComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
<nav class="navbar navbar-expand-lg navbar-dark navbar-default fixed-top">
<div class="container-fluid">
<a class="navbar-brand">Editor</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" (click)="onClickRun()">
<i class="bi bi-play-fill"></i> Run
</a>
</li>
<li class="nav-item">
<a class="nav-link" (click)="onClickTidy()">
<i class="bi bi-lightning-charge-fill"></i> TidyUp
</a>
</li>
<li class="nav-item">
<a class="nav-link" (click)="onClickReset()">
<i class="bi bi-recycle"></i> Reset
</a>
</li>
</ul>
</div>
</div>
</nav>
<main class="h-100 d-flex flex-row">
<div class="d-flex flex-column h-100 flex-shrink-0 p-2 bg-light overflow-auto sidebar">
<div class="accordion" id="sidebarAccordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse"
data-bs-target="#collapseChallenges">
Challenges
</button>
</h2>
<div id="collapseChallenges" class="accordion-collapse collapse show" data-bs-parent="#sidebarAccordion">
<div class="list-group list-group-flush">
<a *ngIf="challenges.length == 0" class="list-group-item list-group-item-action disabled">
<div class="d-flex w-100 justify-content-between">
<small class="text-muted">No challenges</small>
</div>
</a>
<a *ngFor="let entry of challenges" [routerLink]="['/challenges', entry.id]" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<small class="text-muted">Challenge #{{ entry.id }}</small>
</div>
</a>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
data-bs-target="#collapseHistory">
Version History
</button>
</h2>
<div id="collapseHistory" class="accordion-collapse collapse" data-bs-parent="#sidebarAccordion">
<div class="list-group list-group-flush">
<a *ngIf="history.length == 0" class="list-group-item list-group-item-action disabled">
<div class="d-flex w-100 justify-content-between">
<small class="text-muted">No version</small>
</div>
</a>
<a *ngFor="let entry of history.slice().reverse(); index as i" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<small class="text-muted">{{ entry.date | date:'dd.MM.yyyy, HH:mm:ss' }}</small>
<button type="button" class="btn-close" (click)="deleteSave(entry)"></button>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
<app-editors #editors class="w-100 h-100 m-0 p-1"></app-editors>
</main>
.sidebar {
width: 280px;
}
.navbar-default {
background: #609B61;
.navbar-brand {
width: 280px;
margin-right: 0px;
}
ul li {
background: #477947;
margin-right: 10px;
}
.navbar-nav>li>a {
color: #fff;
&:hover {
color: #c5c5c5;
}
}
}
import { AfterViewInit, Component, OnInit, HostListener, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { EditorsComponent } from '../editors/editors.component';
import { HistoryService } from '../services/history.service';
import { EditorState, equals as stateEquals } from '../models/editor-state';
import { HistoryEntry } from '../models/history-entry';
import { ChallengeService } from '../services/challenge.service';
import { Challenge } from '../models/challenge';
@Component({
selector: 'app-challenge',
templateUrl: './challenge.component.html',
styleUrls: ['./challenge.component.scss']
})
export class ChallengeComponent implements OnInit, AfterViewInit {
title = 'tp-secuinfo';
history: HistoryEntry[] = [];
challenges: Challenge[] = []
challenge: Challenge = { id: 0, html: '', css: '', js: '', };
@ViewChild('editors')
private editors!: EditorsComponent;
constructor(
private service: HistoryService,
private challengeService: ChallengeService,
private router: Router,
private route: ActivatedRoute,
) { }
async ngOnInit(): Promise<void> {
this.challenges = await this.challengeService.getAll();
this.route.params.subscribe(async (params) => {
const challengeId = Number(params.id);
const challenge = await this.challengeService.getById(challengeId);
if (challenge) {
this.challenge = challenge;
this.history = await this.service.getAllById(challengeId);
let editorState: EditorState = this.challenge;
if (this.history.length > 0) {
editorState = this.history[this.history.length - 1];
}
this.editors.setState(editorState);
} else if (this.challenges.length > 0) {
this.router.navigate(['/challenges', this.challenges[0].id]);
}
});
}
ngAfterViewInit(): void {
this.onClickRun();
}
@HostListener('document:keypress', ['$event'])
onSave(event: KeyboardEvent): void {
// Catch CTRL+S (Linux, Windows), CMD+S (macOS) or F24 (Electron)
if ((event.metaKey && event.key === 's') || event.key === 'F24') {
event.preventDefault();
this.onSaveState();
this.onClickRun();
}
}
onClickRun(): void {
this.editors.reloadPreview();
}
onClickTidy(): void {
this.editors.formatAll();
}
onClickReset(): void {
this.editors.setState(this.challenge);