├── src
├── assets
│ ├── .gitkeep
│ ├── background.jpg
│ ├── i18n
│ │ └── en.json
│ ├── more-vertical.svg
│ └── more-vertical-light.svg
├── app
│ ├── app.component.scss
│ ├── add-emoji
│ │ ├── add-emoji.component.css
│ │ ├── add-emoji.component.html
│ │ ├── add-emoji.component.ts
│ │ └── add-emoji.component.spec.ts
│ ├── app.component.html
│ ├── shared
│ │ ├── components
│ │ │ ├── page-not-found
│ │ │ │ ├── page-not-found.component.scss
│ │ │ │ ├── page-not-found.component.html
│ │ │ │ ├── page-not-found.component.ts
│ │ │ │ └── page-not-found.component.spec.ts
│ │ │ └── index.ts
│ │ ├── directives
│ │ │ ├── index.ts
│ │ │ └── webview
│ │ │ │ ├── webview.directive.ts
│ │ │ │ └── webview.directive.spec.ts
│ │ └── shared.module.ts
│ ├── core
│ │ ├── services
│ │ │ ├── index.ts
│ │ │ ├── electron
│ │ │ │ ├── electron.service.spec.ts
│ │ │ │ └── electron.service.ts
│ │ │ └── markdown.service.ts
│ │ └── core.module.ts
│ ├── home
│ │ ├── home.component.html
│ │ ├── home-routing.module.ts
│ │ ├── home.component.scss
│ │ ├── home.component.spec.ts
│ │ ├── home.component.ts
│ │ └── home.module.ts
│ ├── models
│ │ ├── markdown.ts
│ │ └── metadata.ts
│ ├── load-dialog
│ │ ├── load-dialog.component.css
│ │ ├── load-dialog.component.html
│ │ ├── load-dialog.component.spec.ts
│ │ └── load-dialog.component.ts
│ ├── save-dialog
│ │ ├── save-dialog.component.css
│ │ ├── save-dialog.component.html
│ │ ├── save-dialog.component.spec.ts
│ │ └── save-dialog.component.ts
│ ├── app-routing.module.ts
│ ├── markup
│ │ ├── markup.component.html
│ │ ├── markup.component.spec.ts
│ │ ├── markup.component.css
│ │ └── markup.component.ts
│ ├── navbar
│ │ ├── navbar.component.spec.ts
│ │ ├── navbar.component.html
│ │ ├── navbar.component.ts
│ │ └── navbar.component.css
│ ├── options-dialog
│ │ ├── options-dialog.component.css
│ │ ├── options-dialog.component.spec.ts
│ │ ├── options-dialog.component.html
│ │ └── options-dialog.component.ts
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── icons
│ │ └── icons.module.ts
│ ├── app.module.ts
│ └── constants
│ │ ├── form-constants.ts
│ │ └── app-constants.ts
├── polyfills-test.ts
├── favicon.png
├── favicon.256x256.png
├── favicon.512x512.png
├── environments
│ ├── environment.ts
│ ├── environment.prod.ts
│ └── environment.dev.ts
├── typings.d.ts
├── tsconfig.app.json
├── tsconfig.spec.json
├── main.ts
├── test.ts
├── karma.conf.js
├── manifest.webmanifest
├── polyfills.ts
├── index.html
└── styles.scss
├── postcss.config.js
├── _config.yml
├── preview.png
├── logo-angular.jpg
├── logo-electron.jpg
├── e2e
├── tsconfig.e2e.json
├── main.spec.ts
└── common-setup.ts
├── .editorconfig
├── .travis.yml
├── browserslist
├── tsconfig-serve.json
├── tsconfig.json
├── postinstall-web.js
├── postinstall.js
├── ngsw-config.json
├── electron-builder.json
├── .gitignore
├── .github
├── workflow
│ └── electron.yml
└── workflows
│ └── electron.yml
├── LICENSE.md
├── main.ts
├── README.md
├── tslint.json
├── package.json
├── angular.json
└── CHANGELOG.md
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-architect
--------------------------------------------------------------------------------
/src/app/add-emoji/add-emoji.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/app/shared/components/page-not-found/page-not-found.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JP1016/Markdown-Electron/HEAD/preview.png
--------------------------------------------------------------------------------
/src/app/core/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from './electron/electron.service';
2 |
--------------------------------------------------------------------------------
/src/app/shared/directives/index.ts:
--------------------------------------------------------------------------------
1 | export * from './webview/webview.directive';
2 |
--------------------------------------------------------------------------------
/src/polyfills-test.ts:
--------------------------------------------------------------------------------
1 | import 'core-js/es/reflect';
2 | import 'zone.js/dist/zone';
3 |
--------------------------------------------------------------------------------
/logo-angular.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JP1016/Markdown-Electron/HEAD/logo-angular.jpg
--------------------------------------------------------------------------------
/src/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JP1016/Markdown-Electron/HEAD/src/favicon.png
--------------------------------------------------------------------------------
/logo-electron.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JP1016/Markdown-Electron/HEAD/logo-electron.jpg
--------------------------------------------------------------------------------
/src/app/shared/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './page-not-found/page-not-found.component';
2 |
--------------------------------------------------------------------------------
/src/favicon.256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JP1016/Markdown-Electron/HEAD/src/favicon.256x256.png
--------------------------------------------------------------------------------
/src/favicon.512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JP1016/Markdown-Electron/HEAD/src/favicon.512x512.png
--------------------------------------------------------------------------------
/src/assets/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JP1016/Markdown-Electron/HEAD/src/assets/background.jpg
--------------------------------------------------------------------------------
/src/app/shared/components/page-not-found/page-not-found.component.html:
--------------------------------------------------------------------------------
1 |
2 | page-not-found works!
3 |
4 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | export const AppConfig = {
2 | production: false,
3 | environment: 'LOCAL'
4 | };
5 |
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const AppConfig = {
2 | production: false,
3 | environment: 'DEV'
4 | };
5 |
--------------------------------------------------------------------------------
/src/app/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
--------------------------------------------------------------------------------
/src/assets/i18n/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "PAGES": {
3 | "HOME": {
4 | "TITLE": "App works !"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/app/models/markdown.ts:
--------------------------------------------------------------------------------
1 | export interface MarkDownObject {
2 | id: string;
3 | title: string;
4 | data: string;
5 | timestamp?: string;
6 | }
7 |
--------------------------------------------------------------------------------
/src/app/models/metadata.ts:
--------------------------------------------------------------------------------
1 | export interface MetaObject {
2 | link: string;
3 | description: string;
4 | type: string;
5 | descr?: string;
6 | }
7 |
--------------------------------------------------------------------------------
/src/app/add-emoji/add-emoji.component.html:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/app/shared/directives/webview/webview.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive } from '@angular/core';
2 |
3 | @Directive({
4 | selector: '[webview]'
5 | })
6 | export class WebviewDirective {
7 | constructor() {}
8 | }
9 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | declare var nodeModule: NodeModule;
3 | interface NodeModule {
4 | id: string;
5 | }
6 |
7 | declare var window: Window;
8 | interface Window {
9 | process: any;
10 | require: any;
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/core/core.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | @NgModule({
5 | declarations: [],
6 | imports: [
7 | CommonModule
8 | ]
9 | })
10 | export class CoreModule { }
11 |
--------------------------------------------------------------------------------
/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "module": "es2015",
6 | "types": [
7 | "mocha"
8 | ]
9 | },
10 | "include": [
11 | "**/*.ts"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "es2015",
6 | "baseUrl": "",
7 | "types": []
8 | },
9 | "exclude": [
10 | "**/*.spec.ts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/shared/directives/webview/webview.directive.spec.ts:
--------------------------------------------------------------------------------
1 | import { WebviewDirective } from './webview.directive';
2 |
3 | describe('WebviewDirective', () => {
4 | it('should create an instance', () => {
5 | const directive = new WebviewDirective();
6 | expect(directive).toBeTruthy();
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/src/assets/more-vertical.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/more-vertical-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/app/shared/components/page-not-found/page-not-found.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-page-not-found',
5 | templateUrl: './page-not-found.component.html',
6 | styleUrls: ['./page-not-found.component.scss']
7 | })
8 | export class PageNotFoundComponent implements OnInit {
9 | constructor() {}
10 |
11 | ngOnInit() {}
12 | }
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | os:
2 | - linux
3 | - osx
4 | language: node_js
5 | node_js:
6 | - '12'
7 | - '11'
8 | - '10'
9 | dist: xenial
10 | sudo: required
11 | services:
12 | - xvfb
13 | addons:
14 | chrome: stable
15 | before_script:
16 | - export DISPLAY=:99.0
17 | install:
18 | - npm set progress=false
19 | - npm install
20 | script:
21 | - ng lint
22 | - npm run test
23 | - npm run e2e
24 | - npm run build
25 |
--------------------------------------------------------------------------------
/src/app/load-dialog/load-dialog.component.css:
--------------------------------------------------------------------------------
1 | .list-items {
2 | display: flex;
3 | justify-content: space-between;
4 | padding: 8px 0;
5 | justify-content: space-between;
6 | font-size: 19px;
7 |
8 | }
9 |
10 | .list-items span,
11 | .list-items .fe-icon {
12 | cursor: pointer;
13 | }
14 |
15 | .list-items span:hover,
16 | .list-items .fe-icon:hover {
17 | color: var(--note-sub-text);
18 | cursor: pointer;
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/core/services/electron/electron.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { ElectronService } from './electron.service';
4 |
5 | describe('ElectronService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: ElectronService = TestBed.get(ElectronService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/environments/environment.dev.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `index.ts`, but if you do
3 | // `ng build --env=prod` then `index.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `.angular-cli.json`.
5 |
6 | export const AppConfig = {
7 | production: false,
8 | environment: 'DEV'
9 | };
10 |
--------------------------------------------------------------------------------
/browserslist:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
--------------------------------------------------------------------------------
/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "module": "commonjs",
6 | "types": [
7 | "jasmine",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "test.ts",
13 | "polyfills-test.ts"
14 | ],
15 | "include": [
16 | "**/*.spec.ts",
17 | "**/*.d.ts"
18 | ],
19 | "exclude": [
20 | "dist",
21 | "release",
22 | "node_modules"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import 'hammerjs';
2 | import { enableProdMode } from '@angular/core';
3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4 |
5 | import { AppModule } from './app/app.module';
6 | import { AppConfig } from './environments/environment';
7 |
8 | if (AppConfig.production) {
9 | enableProdMode();
10 | }
11 |
12 | platformBrowserDynamic()
13 | .bootstrapModule(AppModule, {
14 | preserveWhitespaces: false
15 | })
16 | .catch(err => console.error(err));
17 |
--------------------------------------------------------------------------------
/src/app/save-dialog/save-dialog.component.css:
--------------------------------------------------------------------------------
1 | input[type='text'] {
2 | background: var(--btn-hover);
3 | padding: 10px;
4 | border-radius: 4px;
5 | margin-top: 5px;
6 | margin-bottom: 18px;
7 | width: 100%;
8 | }
9 |
10 | .close-icon {
11 | font-size: 30px;
12 | }
13 |
14 | .mat-dialog-actions {
15 | justify-content: flex-end;
16 | margin-bottom: -6px;
17 | }
18 |
19 | .mat-dialog-actions button {
20 | padding: 5px 11px;
21 | background: #1d88e5;
22 | border-radius: 4px;
23 | font-size: 14px;
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/shared/shared.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | import { TranslateModule } from '@ngx-translate/core';
5 |
6 | import { PageNotFoundComponent } from './components/';
7 | import { WebviewDirective } from './directives/';
8 |
9 | @NgModule({
10 | declarations: [PageNotFoundComponent, WebviewDirective],
11 | imports: [CommonModule, TranslateModule],
12 | exports: [TranslateModule, WebviewDirective]
13 | })
14 | export class SharedModule {}
15 |
--------------------------------------------------------------------------------
/src/app/home/home-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { Routes, RouterModule } from '@angular/router';
4 | import { HomeComponent } from './home.component';
5 |
6 | const routes: Routes = [
7 | {
8 | path: 'home',
9 | component: HomeComponent
10 | }
11 | ];
12 |
13 | @NgModule({
14 | declarations: [],
15 | imports: [CommonModule, RouterModule.forChild(routes)],
16 | exports: [RouterModule]
17 | })
18 | export class HomeRoutingModule {}
19 |
--------------------------------------------------------------------------------
/tsconfig-serve.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "sourceMap": true,
4 | "declaration": false,
5 | "moduleResolution": "node",
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "target": "es5",
9 | "types": [
10 | "node"
11 | ],
12 | "lib": [
13 | "es2017",
14 | "es2016",
15 | "es2015",
16 | "dom"
17 | ]
18 | },
19 | "include": [
20 | "main.ts"
21 | ],
22 | "exclude": [
23 | "node_modules",
24 | "**/*.spec.ts"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 | import { PageNotFoundComponent } from './shared/components';
4 |
5 | const routes: Routes = [
6 | {
7 | path: '',
8 | redirectTo: 'home',
9 | pathMatch: 'full'
10 | },
11 | {
12 | path: '**',
13 | component: PageNotFoundComponent
14 | }
15 | ];
16 |
17 | @NgModule({
18 | imports: [RouterModule.forRoot(routes, { useHash: true })],
19 | exports: [RouterModule]
20 | })
21 | export class AppRoutingModule {}
22 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist/out-tsc",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "target": "es5",
11 | "typeRoots": [
12 | "node_modules/@types"
13 | ],
14 | "lib": [
15 | "es2017",
16 | "es2016",
17 | "es2015",
18 | "dom"
19 | ]
20 | },
21 | "include": [
22 | "main.ts",
23 | "src/**/*"
24 | ],
25 | "exclude": [
26 | "node_modules"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/markup/markup.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/app/add-emoji/add-emoji.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Inject } from '@angular/core';
2 | import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
3 |
4 | @Component({
5 | selector: 'app-add-emoji',
6 | templateUrl: './add-emoji.component.html',
7 | styleUrls: ['./add-emoji.component.css']
8 | })
9 | export class AddEmojiComponent implements OnInit {
10 |
11 | constructor(@Inject(MAT_DIALOG_DATA) public data: any,
12 | public dialogRef: MatDialogRef) { }
13 |
14 | ngOnInit() {
15 | }
16 |
17 | addEmoji(event) {
18 | this.dialogRef.close({ success: true, data: event.emoji.native });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/postinstall-web.js:
--------------------------------------------------------------------------------
1 | // Allow angular using electron module (native node modules)
2 | const fs = require('fs');
3 | const f_angular = 'node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js';
4 |
5 | fs.readFile(f_angular, 'utf8', function (err, data) {
6 | if (err) {
7 | return console.log(err);
8 | }
9 | var result = data.replace(/target: "electron-renderer",/g, '');
10 | var result = result.replace(/target: "web",/g, '');
11 | var result = result.replace(/return \{/g, 'return {target: "web",');
12 |
13 | fs.writeFile(f_angular, result, 'utf8', function (err) {
14 | if (err) return console.log(err);
15 | });
16 | });
--------------------------------------------------------------------------------
/postinstall.js:
--------------------------------------------------------------------------------
1 | // Allow angular using electron module (native node modules)
2 | const fs = require('fs');
3 | const f_angular = 'node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js';
4 |
5 | fs.readFile(f_angular, 'utf8', function (err, data) {
6 | if (err) {
7 | return console.log(err);
8 | }
9 | var result = data.replace(/target: "electron-renderer",/g, '');
10 | var result = result.replace(/target: "web",/g, '');
11 | var result = result.replace(/return \{/g, 'return {target: "electron-renderer",');
12 |
13 | fs.writeFile(f_angular, result, 'utf8', function (err) {
14 | if (err) return console.log(err);
15 | });
16 | });
--------------------------------------------------------------------------------
/src/app/save-dialog/save-dialog.component.html:
--------------------------------------------------------------------------------
1 |
2 | Save Markdown
3 |
9 | ×
10 |
11 |
12 |
13 | Enter filename
14 |
15 |
16 |
17 |
18 |
19 |
25 | Submit
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/src/app/home/home.component.scss:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | width: 100%;
4 | height: 100%;
5 | }
6 |
7 | body {
8 | margin: 0px !important;
9 | padding: 0px !important;
10 | }
11 |
12 | button:focus {
13 | outline: 0 !important;
14 | }
15 |
16 | .clearfix {
17 | overflow: auto;
18 | }
19 |
20 | .header {
21 | height: 64px;
22 | background: #2196f3;
23 | }
24 |
25 |
26 | .handle {
27 | width: 8px;
28 | background: #ddd;
29 | cursor: ew-resize;
30 | flex: 1;
31 | }
32 |
33 | .content {
34 | flex-grow: 1;
35 | overflow: auto;
36 | width: 50%;
37 | }
38 |
39 |
40 | @media only screen and (max-width: 600px) {
41 | .sidebar {
42 | width: 600%;
43 | }
44 |
45 | .content {
46 | width: 0%;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ngsw-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/service-worker/config/schema.json",
3 | "index": "/index.html",
4 | "assetGroups": [
5 | {
6 | "name": "app",
7 | "installMode": "prefetch",
8 | "resources": {
9 | "files": [
10 | "/favicon.ico",
11 | "/index.html",
12 | "/manifest.webmanifest",
13 | "/*.css",
14 | "/*.js"
15 | ]
16 | }
17 | }, {
18 | "name": "assets",
19 | "installMode": "lazy",
20 | "updateMode": "prefetch",
21 | "resources": {
22 | "files": [
23 | "/assets/**",
24 | "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
25 | ]
26 | }
27 | }
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/markup/markup.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { MarkupComponent } from './markup.component';
4 |
5 | describe('MarkupComponent', () => {
6 | let component: MarkupComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ MarkupComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(MarkupComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/navbar/navbar.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { NavbarComponent } from './navbar.component';
4 |
5 | describe('NavbarComponent', () => {
6 | let component: NavbarComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ NavbarComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(NavbarComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/electron-builder.json:
--------------------------------------------------------------------------------
1 | {
2 | "productName": "MarkdownEditor",
3 | "directories": {
4 | "output": "release/"
5 | },
6 | "files": [
7 | "**/*",
8 | "!**/*.ts",
9 | "!*.code-workspace",
10 | "!LICENSE.md",
11 | "!package.json",
12 | "!package-lock.json",
13 | "!src/",
14 | "!e2e/",
15 | "!hooks/",
16 | "!angular.json",
17 | "!_config.yml",
18 | "!karma.conf.js",
19 | "!tsconfig.json",
20 | "!tslint.json"
21 | ],
22 | "win": {
23 | "icon": "dist",
24 | "target": [
25 | "portable"
26 | ]
27 | },
28 | "mac": {
29 | "icon": "dist",
30 | "target": [
31 | "dmg"
32 | ]
33 | },
34 | "linux": {
35 | "icon": "dist",
36 | "target": [
37 | "AppImage"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/e2e/main.spec.ts:
--------------------------------------------------------------------------------
1 | import {expect, assert} from 'chai';
2 | import {SpectronClient} from 'spectron';
3 |
4 | import commonSetup from './common-setup';
5 |
6 | describe('angular-electron App', function () {
7 | commonSetup.apply(this);
8 |
9 | let browser: any;
10 | let client: SpectronClient;
11 |
12 | beforeEach(function () {
13 | client = this.app.client;
14 | browser = client as any;
15 | });
16 |
17 | it('should display message saying App works !', async function () {
18 | const text = await browser.getText('app-home h1');
19 | expect(text).to.equal('App works !');
20 | });
21 |
22 |
23 | it('creates initial windows', async function () {
24 | const count = await client.getWindowCount();
25 | expect(count).to.equal(1);
26 | });
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/src/app/add-emoji/add-emoji.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AddEmojiComponent } from './add-emoji.component';
4 |
5 | describe('AddEmojiComponent', () => {
6 | let component: AddEmojiComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ AddEmojiComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(AddEmojiComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/load-dialog/load-dialog.component.html:
--------------------------------------------------------------------------------
1 |
2 | Load Markdown
3 |
9 | ×
10 |
11 |
12 |
13 |
14 | {{ i + 1 + ". " + file.title }}
15 |
20 |
21 | 📭 No saved markdowns found !!
24 |
25 |
--------------------------------------------------------------------------------
/src/app/load-dialog/load-dialog.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { LoadDialogComponent } from './load-dialog.component';
4 |
5 | describe('LoadDialogComponent', () => {
6 | let component: LoadDialogComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ LoadDialogComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(LoadDialogComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/save-dialog/save-dialog.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { SaveDialogComponent } from './save-dialog.component';
4 |
5 | describe('SaveDialogComponent', () => {
6 | let component: SaveDialogComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ SaveDialogComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(SaveDialogComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/options-dialog/options-dialog.component.css:
--------------------------------------------------------------------------------
1 | textarea {
2 | background: var(--bg);
3 | border: 0px;
4 | height: 100%;
5 | width: 100%;
6 | padding: 10px;
7 | border-radius: 4px;
8 | margin-top: 18px;
9 | resize: none;
10 | }
11 |
12 | input[type='text'] {
13 | background: var(--bg);
14 | padding: 10px;
15 | border-radius: 4px;
16 | margin-top: 5px;
17 | margin-bottom: 18px;
18 | width: 100%;
19 | }
20 |
21 | textarea:focus {
22 | outline: 0;
23 | }
24 |
25 | .help-block {
26 | font-size: 14px;
27 | color: var(--list-color);
28 | }
29 |
30 | .mat-dialog-actions {
31 | justify-content: flex-end;
32 | margin-bottom: -6px;
33 | }
34 |
35 | .mat-dialog-actions button {
36 | padding: 5px 11px;
37 | background: #1d88e5;
38 | border-radius: 4px;
39 | font-size: 14px;
40 | }
41 |
--------------------------------------------------------------------------------
/src/app/options-dialog/options-dialog.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { OptionsDialogComponent } from './options-dialog.component';
4 |
5 | describe('OptionsDialogComponent', () => {
6 | let component: OptionsDialogComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ OptionsDialogComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(OptionsDialogComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/shared/components/page-not-found/page-not-found.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { PageNotFoundComponent } from './page-not-found.component';
4 |
5 | describe('PageNotFoundComponent', () => {
6 | let component: PageNotFoundComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ PageNotFoundComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(PageNotFoundComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /app-builds
8 | /release
9 | main.js
10 | src/**/*.js
11 | !src/karma.conf.js
12 | *.js.map
13 |
14 | # dependencies
15 | /node_modules
16 |
17 | # IDEs and editors
18 | /.idea
19 | .project
20 | .classpath
21 | .c9/
22 | *.launch
23 | .settings/
24 | *.sublime-workspace
25 |
26 | # IDE - VSCode
27 | .vscode/*
28 | .vscode/settings.json
29 | !.vscode/tasks.json
30 | !.vscode/launch.json
31 | !.vscode/extensions.json
32 |
33 | # misc
34 | /.sass-cache
35 | /connect.lock
36 | /coverage
37 | /libpeerconnection.log
38 | npm-debug.log
39 | testem.log
40 | /typings
41 | package-lock.json
42 |
43 | # e2e
44 | /e2e/*.js
45 | !/e2e/protractor.conf.js
46 | /e2e/*.map
47 |
48 | # System Files
49 | .DS_Store
50 | Thumbs.db
51 |
--------------------------------------------------------------------------------
/.github/workflow/electron.yml:
--------------------------------------------------------------------------------
1 | # name of your github action
2 | name: CI
3 | # this will help you specify where to run
4 | on:
5 | push:
6 | branches:
7 | # this will run on the master branch
8 | - master
9 | # this is where the magic happens, each job happens in parallel btw
10 | jobs:
11 | build_on_mac:
12 | runs-on: macOS-latest
13 | steps:
14 | - uses: actions/checkout@master
15 | with:
16 | ref: electron
17 | - uses: actions/setup-node@master
18 | with:
19 | node-version: 10.16
20 | - name: see directory
21 | run: ls
22 | build_on_win:
23 | runs-on: windows-2016
24 | steps:
25 | - uses: actions/checkout@master
26 | with:
27 | ref: feature/github-actions
28 | - uses: actions/setup-node@master
29 | with:
30 | node-version: 10.16
31 | - name: see directory
32 | run: ls
33 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 | import { RouterTestingModule } from '@angular/router/testing';
3 | import { AppComponent } from './app.component';
4 | import { TranslateModule } from '@ngx-translate/core';
5 | import { ElectronService } from './core/services';
6 |
7 | describe('AppComponent', () => {
8 | beforeEach(async(() => {
9 | TestBed.configureTestingModule({
10 | declarations: [AppComponent],
11 | providers: [ElectronService],
12 | imports: [RouterTestingModule, TranslateModule.forRoot()]
13 | }).compileComponents();
14 | }));
15 |
16 | it('should create the app', async(() => {
17 | const fixture = TestBed.createComponent(AppComponent);
18 | const app = fixture.debugElement.componentInstance;
19 | expect(app).toBeTruthy();
20 | }));
21 | });
22 |
23 | class TranslateServiceStub {
24 | setDefaultLang(lang: string): void {}
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { ElectronService } from './core/services';
3 | import { TranslateService } from '@ngx-translate/core';
4 | import { AppConfig } from '../environments/environment';
5 |
6 | @Component({
7 | selector: 'app-root',
8 | templateUrl: './app.component.html',
9 | styleUrls: ['./app.component.scss']
10 | })
11 | export class AppComponent {
12 | constructor(
13 | public electronService: ElectronService,
14 | private translate: TranslateService
15 | ) {
16 | translate.setDefaultLang('en');
17 | console.log('AppConfig', AppConfig);
18 |
19 | if (electronService.isElectron) {
20 | console.log(process.env);
21 | console.log('Mode electron');
22 | console.log('Electron ipcRenderer', electronService.ipcRenderer);
23 | console.log('NodeJS childProcess', electronService.childProcess);
24 | } else {
25 | console.log('Mode web');
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2018 - Maxime GRIS
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/src/app/home/home.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HomeComponent } from './home.component';
4 | import { TranslateModule } from '@ngx-translate/core';
5 |
6 | describe('HomeComponent', () => {
7 | let component: HomeComponent;
8 | let fixture: ComponentFixture;
9 |
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | declarations: [HomeComponent],
13 | imports: [TranslateModule.forRoot()]
14 | }).compileComponents();
15 | }));
16 |
17 | beforeEach(() => {
18 | fixture = TestBed.createComponent(HomeComponent);
19 | component = fixture.componentInstance;
20 | fixture.detectChanges();
21 | });
22 |
23 | it('should create', () => {
24 | expect(component).toBeTruthy();
25 | });
26 |
27 | it('should render title in a h1 tag', async(() => {
28 | const compiled = fixture.debugElement.nativeElement;
29 | expect(compiled.querySelector('h1').textContent).toContain(
30 | 'PAGES.HOME.TITLE'
31 | );
32 | }));
33 | });
34 |
--------------------------------------------------------------------------------
/src/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: [ 'html', 'lcovonly' ],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: true
30 | });
31 | };
32 |
--------------------------------------------------------------------------------
/src/app/markup/markup.component.css:
--------------------------------------------------------------------------------
1 | .editor {
2 | letter-spacing: 0.00625em;
3 | font-size: 1.3rem;
4 | font-weight: 400;
5 | line-height: 2rem;
6 | padding: 15px 25px;
7 | outline: none;
8 | height: 90vh;
9 | overflow: auto;
10 | }
11 |
12 | .split {
13 | height: 94vh;
14 | width: 100%;
15 | }
16 |
17 | .mark-pad {
18 | padding: 25px;
19 | cursor: not-allowed;
20 | }
21 |
22 | .menu {
23 | display: flex;
24 | align-items: center;
25 | }
26 |
27 | .menu a {
28 | padding-left: 20px;
29 | display: flex;
30 | align-items: center;
31 | }
32 |
33 | table {
34 | width: 100%;
35 | background: #fff;
36 | margin: 1em 0;
37 | border: 1px solid rgba(34, 36, 38, 0.15);
38 | -webkit-box-shadow: none;
39 | box-shadow: none;
40 | border-radius: 0.28571429rem;
41 | text-align: left;
42 | color: rgba(0, 0, 0, 0.87);
43 | border-collapse: separate;
44 | border-spacing: 0;
45 | display: table;
46 | }
47 |
48 | textarea {
49 | background: var(--bg);
50 | border: 0px;
51 | height: 100%;
52 | resize: none;
53 | width: 100%;
54 | padding: 25px;
55 | }
56 |
57 | textarea:focus {
58 | outline: 0;
59 | }
60 |
--------------------------------------------------------------------------------
/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ElementRef, Renderer2 } from '@angular/core';
2 | import { SwUpdate } from '@angular/service-worker';
3 | import { MarkdownService } from 'ngx-markdown';
4 |
5 | @Component({
6 | selector: 'app-home',
7 | templateUrl: './home.component.html',
8 | styleUrls: ['./home.component.scss']
9 | })
10 | export class HomeComponent implements OnInit {
11 |
12 | title = 'notes';
13 | inputElement: ElementRef;
14 | noteText = "";
15 | public isSidebarVisible: Boolean = true;
16 |
17 | constructor(private element: ElementRef, private renderer: Renderer2, private swUpdate: SwUpdate, private markdownService: MarkdownService) { }
18 |
19 | ngAfterViewInit(): void {
20 |
21 | this.renderer.listen(this.element.nativeElement, 'paste', (event) => {
22 | navigator['clipboard'].readText().then(clipText => {
23 | this.noteText = clipText
24 | });
25 | });
26 |
27 | }
28 |
29 |
30 | ngOnInit(): void {
31 | if (this.swUpdate.isEnabled) {
32 | this.swUpdate.available.subscribe(() => {
33 | window.location.reload();
34 | });
35 | }
36 |
37 | }
38 |
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/app/save-dialog/save-dialog.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Inject } from "@angular/core";
2 | import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material";
3 | import { OptionsDialogComponent } from "../options-dialog/options-dialog.component";
4 | import { FormBuilder, FormGroup } from "@angular/forms";
5 | import { IndexedDB } from "ng-indexed-db";
6 | import { Observable } from "rxjs";
7 | import { MarkdownService } from "../core/services/markdown.service";
8 |
9 | @Component({
10 | selector: "app-save-dialog",
11 | templateUrl: "./save-dialog.component.html",
12 | styleUrls: ["./save-dialog.component.css"]
13 | })
14 | export class SaveDialogComponent implements OnInit {
15 | public saveOption: FormGroup;
16 | public fileName = "";
17 | $list: Observable;
18 |
19 | constructor(
20 | @Inject(MAT_DIALOG_DATA) public data: any,
21 | public saveDialog: MatDialogRef,
22 | private markdownService: MarkdownService
23 | ) { }
24 |
25 | ngOnInit() { }
26 |
27 | saveFile() {
28 | this.markdownService.saveMarkdown.next(this.fileName);
29 | this.saveDialog.close({ data: this.fileName });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/app/core/services/electron/electron.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | // If you import a module but never use any of the imported values other than as TypeScript types,
4 | // the resulting javascript file will look as if you never imported the module at all.
5 | import { ipcRenderer, webFrame, remote } from 'electron';
6 | import * as childProcess from 'child_process';
7 | import * as fs from 'fs';
8 |
9 | @Injectable({
10 | providedIn: 'root'
11 | })
12 | export class ElectronService {
13 | ipcRenderer: typeof ipcRenderer;
14 | webFrame: typeof webFrame;
15 | remote: typeof remote;
16 | childProcess: typeof childProcess;
17 | fs: typeof fs;
18 |
19 | get isElectron() {
20 | return window && window.process && window.process.type;
21 | }
22 |
23 | constructor() {
24 | // Conditional imports
25 | if (this.isElectron) {
26 | this.ipcRenderer = window.require('electron').ipcRenderer;
27 | this.webFrame = window.require('electron').webFrame;
28 | this.remote = window.require('electron').remote;
29 |
30 | this.childProcess = window.require('child_process');
31 | this.fs = window.require('fs');
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/app/options-dialog/options-dialog.component.html:
--------------------------------------------------------------------------------
1 |
2 | {{ formOption.title }}
3 |
4 | ×
5 |
6 |
7 |
8 |
9 |
{{ item.label }}
10 |
11 |
17 |
24 | * You can replace the above text with your own and reuse it while creating next readme
26 |
27 |
28 |
29 |
30 |
31 |
32 | Submit
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/manifest.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Markdown Editor",
3 | "short_name": "Markdown Editor",
4 | "theme_color": "#1976d2",
5 | "background_color": "#fafafa",
6 | "display": "standalone",
7 | "scope": "/",
8 | "start_url": "/",
9 | "icons": [{
10 | "src": "assets/icons/icon-72x72.png",
11 | "sizes": "72x72",
12 | "type": "image/png"
13 | },
14 | {
15 | "src": "assets/icons/icon-96x96.png",
16 | "sizes": "96x96",
17 | "type": "image/png"
18 | },
19 | {
20 | "src": "assets/icons/icon-128x128.png",
21 | "sizes": "128x128",
22 | "type": "image/png"
23 | },
24 | {
25 | "src": "assets/icons/icon-144x144.png",
26 | "sizes": "144x144",
27 | "type": "image/png"
28 | },
29 | {
30 | "src": "assets/icons/icon-152x152.png",
31 | "sizes": "152x152",
32 | "type": "image/png"
33 | },
34 | {
35 | "src": "assets/icons/icon-192x192.png",
36 | "sizes": "192x192",
37 | "type": "image/png"
38 | },
39 | {
40 | "src": "assets/icons/icon-384x384.png",
41 | "sizes": "384x384",
42 | "type": "image/png"
43 | },
44 | {
45 | "src": "assets/icons/icon-512x512.png",
46 | "sizes": "512x512",
47 | "type": "image/png"
48 | }
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/src/app/core/services/markdown.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from "@angular/core";
2 | import { BehaviorSubject } from "rxjs";
3 | import { MetaObject } from "../../models/metadata";
4 | import { MarkDownObject } from "../../models/markdown";
5 |
6 | @Injectable({
7 | providedIn: "root"
8 | })
9 |
10 | @Injectable({
11 | providedIn: "root"
12 | })
13 | export class MarkdownService {
14 | public optionChanged: BehaviorSubject = new BehaviorSubject(
15 | null
16 | );
17 | public emojiAdded: BehaviorSubject = new BehaviorSubject(
18 | null
19 | );
20 | public copyMarkdown: BehaviorSubject = new BehaviorSubject(
21 | false
22 | );
23 | public downloadMarkdown: BehaviorSubject = new BehaviorSubject<
24 | boolean
25 | >(false);
26 | public saveMarkdown: BehaviorSubject = new BehaviorSubject(
27 | null
28 | );
29 | public metaAdded: BehaviorSubject = new BehaviorSubject<
30 | MetaObject
31 | >(null);
32 | public loadMarkdown: BehaviorSubject = new BehaviorSubject<
33 | MarkDownObject
34 | >(null);
35 | public newMarkdown: BehaviorSubject = new BehaviorSubject(
36 | null
37 | );
38 | public markdownFromLocalStorage: BehaviorSubject = new BehaviorSubject(
39 | null
40 | );
41 |
42 | constructor() {}
43 | }
44 |
--------------------------------------------------------------------------------
/src/app/icons/icons.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from "@angular/core";
2 |
3 | import { FeatherModule } from "angular-feather";
4 | import {
5 | Camera,
6 | Heart,
7 | Github,
8 | Trash2,
9 | AlertCircle,
10 | Send,
11 | Twitter,
12 | Moon,
13 | Sun,
14 | File,
15 | Plus,
16 | Menu,
17 | BookOpen,
18 | Bold,
19 | Italic,
20 | Code,
21 | Link,
22 | CheckSquare,
23 | List,
24 | ChevronRight,
25 | Minus,
26 | Image,
27 | Type,
28 | Circle,
29 | Book,
30 | Hash,
31 | ChevronsRight,
32 | HardDrive,
33 | Columns,
34 | Smile,
35 | Copy,
36 | Download,
37 | Package,
38 | Loader,
39 | Save,
40 | GitMerge,
41 | Users
42 | } from "angular-feather/icons";
43 |
44 | const icons = {
45 | Camera,
46 | Heart,
47 | Github,
48 | Trash2,
49 | Send,
50 | Moon,
51 | Loader,
52 | Sun,
53 | Plus,
54 | Menu,
55 | Book,
56 | BookOpen,
57 | Twitter,
58 | Bold,
59 | Italic,
60 | Smile,
61 | Copy,
62 | Download,
63 | Package,
64 | Save,
65 | Code,
66 | Link,
67 | CheckSquare,
68 | HardDrive,
69 | List,
70 | ChevronRight,
71 | Minus,
72 | Image,
73 | Type,
74 | Circle,
75 | Hash,
76 | ChevronsRight,
77 | Columns,
78 | GitMerge,
79 | AlertCircle,
80 | File,
81 | Users
82 | };
83 |
84 | @NgModule({
85 | imports: [FeatherModule.pick(icons)],
86 | exports: [FeatherModule]
87 | })
88 | export class IconsModule { }
89 |
--------------------------------------------------------------------------------
/e2e/common-setup.ts:
--------------------------------------------------------------------------------
1 | const Application = require('spectron').Application;
2 | const electronPath = require('electron'); // Require Electron from the binaries included in node_modules.
3 | const path = require('path');
4 |
5 | export default function setup() {
6 | beforeEach(async function () {
7 | this.app = new Application({
8 | // Your electron path can be any binary
9 | // i.e for OSX an example path could be '/Applications/MyApp.app/Contents/MacOS/MyApp'
10 | // But for the sake of the example we fetch it from our node_modules.
11 | path: electronPath,
12 |
13 | // Assuming you have the following directory structure
14 |
15 | // |__ my project
16 | // |__ ...
17 | // |__ main.js
18 | // |__ package.json
19 | // |__ index.html
20 | // |__ ...
21 | // |__ test
22 | // |__ spec.js <- You are here! ~ Well you should be.
23 |
24 | // The following line tells spectron to look and use the main.js file
25 | // and the package.json located 1 level above.
26 | args: [path.join(__dirname, '..')],
27 | webdriverOptions: {}
28 | });
29 | await this.app.start();
30 | const browser = this.app.client;
31 | await browser.waitUntilWindowLoaded();
32 |
33 | browser.timeouts('script', 15000);
34 | });
35 |
36 | afterEach(function () {
37 | if (this.app && this.app.isRunning()) {
38 | return this.app.stop();
39 | }
40 | });
41 | }
42 |
--------------------------------------------------------------------------------
/src/app/load-dialog/load-dialog.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from "@angular/core";
2 | import { MatSnackBar, MatDialog, MatDialogRef } from "@angular/material";
3 | import { IndexedDB } from "ng-indexed-db";
4 | import { Observable } from "rxjs";
5 | import { MarkdownService } from "../core/services/markdown.service";
6 |
7 | @Component({
8 | selector: "app-load-dialog",
9 | templateUrl: "./load-dialog.component.html",
10 | styleUrls: ["./load-dialog.component.css"]
11 | })
12 | export class LoadDialogComponent implements OnInit {
13 | $list: Observable;
14 |
15 | constructor(
16 | public loadDialog: MatDialogRef,
17 | private markDownService: MarkdownService,
18 | private indexedDbService: IndexedDB,
19 | private snackBar: MatSnackBar
20 | ) { }
21 |
22 | deleteMarkdown(id) {
23 | console.log(id);
24 | this.indexedDbService.delete("markdown_store", id).subscribe(
25 | response => {
26 | this.getMarkdowns();
27 | this.snackBar.open("Markdown Deleted !! 🗑", " ", {
28 | duration: 1000
29 | });
30 | console.log("Deleted");
31 | },
32 | error => {
33 | this.snackBar.open("Error Occured while deleting markdown !!", " ", {
34 | duration: 1000
35 | });
36 | }
37 | );
38 | }
39 |
40 | getMarkdowns() {
41 | this.$list = this.indexedDbService.list("markdown_store");
42 | }
43 |
44 | loadMarkdown(item) {
45 | this.markDownService.loadMarkdown.next(item);
46 | this.loadDialog.close({ data: item });
47 | }
48 |
49 | ngOnInit() {
50 | this.getMarkdowns();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/app/options-dialog/options-dialog.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Inject } from "@angular/core";
2 | import { FormGroup, FormBuilder, FormControl } from "@angular/forms";
3 | import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
4 | import { FORM_OPTIONS } from "../constants/form-constants";
5 | import { MarkdownService } from "../core/services/markdown.service";
6 |
7 | @Component({
8 | selector: "app-options-dialog",
9 | templateUrl: "./options-dialog.component.html",
10 | styleUrls: ["./options-dialog.component.css"]
11 | })
12 | export class OptionsDialogComponent implements OnInit {
13 | public addOption: FormGroup;
14 |
15 | public options = FORM_OPTIONS;
16 | public formOption;
17 |
18 | constructor(
19 | @Inject(MAT_DIALOG_DATA) public data: any,
20 | public optionsDialog: MatDialogRef,
21 | private fb: FormBuilder,
22 | private markService: MarkdownService
23 | ) { }
24 |
25 | ngOnInit() {
26 | this.formOption = FORM_OPTIONS[this.data.type];
27 | this.populateFormControl();
28 | }
29 |
30 | populateFormControl() {
31 | this.addOption = this.fb.group({});
32 | const form = FORM_OPTIONS[this.data.type];
33 | let value = "";
34 | let preVal = localStorage.getItem(this.data.type);
35 | if (FORM_OPTIONS[this.data.type].hasOwnProperty("default")) {
36 | value = FORM_OPTIONS[this.data.type]["default"];
37 | }
38 | if (preVal) {
39 | value = preVal;
40 | }
41 |
42 | form.fields.map(column => {
43 | this.addOption.addControl(column.name, new FormControl(value, []));
44 | });
45 | }
46 |
47 |
48 | executeFormAction() {
49 | if (this.data.type == "image") {
50 | this.optionsDialog.close({
51 | success: true,
52 | data: { type: "image", ...this.addOption.value }
53 | });
54 | } else if (this.data.type == "link") {
55 | this.optionsDialog.close({
56 | success: true,
57 | data: { type: "link", ...this.addOption.value }
58 | });
59 | } else {
60 | localStorage.setItem(this.data.type, this.addOption.value.descr)
61 | this.optionsDialog.close({
62 | success: true,
63 | data: { type: "text", ...this.addOption.value }
64 | });
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/main.ts:
--------------------------------------------------------------------------------
1 | import { app, BrowserWindow, screen } from 'electron';
2 | import * as path from 'path';
3 | import * as url from 'url';
4 |
5 | let win, serve;
6 | const args = process.argv.slice(1);
7 | serve = args.some(val => val === '--serve');
8 |
9 | function createWindow() {
10 |
11 | const electronScreen = screen;
12 | const size = electronScreen.getPrimaryDisplay().workAreaSize;
13 |
14 | // Create the browser window.
15 | win = new BrowserWindow({
16 | x: 0,
17 | y: 0,
18 | width: size.width,
19 | height: size.height,
20 | webPreferences: {
21 | nodeIntegration: true,
22 | },
23 | });
24 |
25 | if (serve) {
26 | require('electron-reload')(__dirname, {
27 | electron: require(`${__dirname}/node_modules/electron`)
28 | });
29 | win.loadURL('http://localhost:4200');
30 | } else {
31 | win.loadURL(url.format({
32 | pathname: path.join(__dirname, 'dist/index.html'),
33 | protocol: 'file:',
34 | slashes: true
35 | }));
36 | }
37 |
38 | if (serve) {
39 | win.webContents.openDevTools();
40 | }
41 |
42 | // Emitted when the window is closed.
43 | win.on('closed', () => {
44 | // Dereference the window object, usually you would store window
45 | // in an array if your app supports multi windows, this is the time
46 | // when you should delete the corresponding element.
47 | win = null;
48 | });
49 |
50 | }
51 |
52 | try {
53 |
54 | // This method will be called when Electron has finished
55 | // initialization and is ready to create browser windows.
56 | // Some APIs can only be used after this event occurs.
57 | app.on('ready', createWindow);
58 |
59 | // Quit when all windows are closed.
60 | app.on('window-all-closed', () => {
61 | // On OS X it is common for applications and their menu bar
62 | // to stay active until the user quits explicitly with Cmd + Q
63 |
64 |
65 | if (process.platform !== 'darwin') {
66 |
67 | // app.quit();
68 | }
69 | });
70 |
71 | app.on('activate', () => {
72 | // On OS X it's common to re-create a window in the app when the
73 | // dock icon is clicked and there are no other windows open.
74 |
75 |
76 | if (win === null) {
77 | createWindow();
78 | }
79 | });
80 |
81 | } catch (e) {
82 | // Catch Error
83 | // throw e;
84 | }
85 |
--------------------------------------------------------------------------------
/src/app/home/home.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | import { HomeRoutingModule } from './home-routing.module';
5 |
6 | import { HomeComponent } from './home.component';
7 | import { SharedModule } from '../shared/shared.module';
8 | import { NavbarComponent } from '../navbar/navbar.component';
9 | import { MarkupComponent } from '../markup/markup.component';
10 | import { AddEmojiComponent } from '../add-emoji/add-emoji.component';
11 | import { OptionsDialogComponent } from '../options-dialog/options-dialog.component';
12 | import { SaveDialogComponent } from '../save-dialog/save-dialog.component';
13 | import { LoadDialogComponent } from '../load-dialog/load-dialog.component';
14 | import { MatDialogModule, MatTooltipModule, MatIconModule, MatSnackBarModule } from '@angular/material';
15 | import { IconsModule } from '../icons/icons.module';
16 | import { PickerModule } from '@ctrl/ngx-emoji-mart';
17 | import { NtkmeButtonModule } from '@ctrl/ngx-github-buttons';
18 | import { MarkdownModule } from 'ngx-markdown';
19 | import { KeyboardShortcutsModule } from 'ng-keyboard-shortcuts';
20 | import { AngularSplitModule } from 'angular-split';
21 | import { IndexedDBModule } from 'ng-indexed-db';
22 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
23 | import { BrowserModule } from '@angular/platform-browser';
24 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
25 |
26 | @NgModule({
27 | declarations: [HomeComponent,
28 | NavbarComponent,
29 | MarkupComponent,
30 | AddEmojiComponent,
31 | OptionsDialogComponent,
32 | SaveDialogComponent,
33 | LoadDialogComponent],
34 | imports: [CommonModule, SharedModule, HomeRoutingModule,
35 | MatDialogModule,
36 | IconsModule,
37 | FormsModule,
38 | BrowserAnimationsModule,
39 | ReactiveFormsModule,
40 | BrowserModule,
41 | MatTooltipModule,
42 | MatIconModule,
43 | MatSnackBarModule,
44 | PickerModule,
45 | NtkmeButtonModule,
46 | MarkdownModule.forRoot(),
47 | KeyboardShortcutsModule.forRoot(),
48 | AngularSplitModule.forRoot(),
49 | IndexedDBModule.forRoot([
50 | {
51 | name: "markdown_db",
52 | stores: [{ name: "markdown_store" }]
53 | }
54 | ]),],
55 | entryComponents: [
56 | AddEmojiComponent,
57 | OptionsDialogComponent,
58 | SaveDialogComponent,
59 | LoadDialogComponent
60 | ]
61 | })
62 | export class HomeModule { }
63 |
--------------------------------------------------------------------------------
/.github/workflows/electron.yml:
--------------------------------------------------------------------------------
1 | # name of your github action
2 | name: CI
3 | # this will help you specify where to run
4 |
5 | on:
6 | push:
7 | branches:
8 | # this will run on the master branch
9 | - master
10 |
11 | # this is where the magic happens, each job happens in parallel btw
12 | jobs:
13 | build_on_mac:
14 | runs-on: macOS-latest
15 | steps:
16 | - uses: actions/checkout@master
17 | with:
18 | ref: master
19 | - uses: actions/setup-node@master
20 | with:
21 | node-version: 10.16
22 | - name: see directory
23 | run: ls
24 | - name: Install dependencies
25 | run: npm install
26 | - name: Build Electron
27 | run: npm run electron:mac
28 | - name: see directory
29 | run: ls
30 | - uses: lucyio/upload-to-release@master
31 | with:
32 | name: JP1016/Markdown-Electron
33 | path: ./release
34 | action: published
35 | repo-token: ${{ secrets.GITHUB_TOKEN }}
36 | release_id: 1
37 | release-repo: JP1016/Markdown-Electron
38 | build_on_win:
39 | runs-on: windows-latest
40 | steps:
41 | - uses: actions/checkout@master
42 | with:
43 | ref: master
44 | - uses: actions/setup-node@master
45 | with:
46 | node-version: 10.16
47 | - name: see directory
48 | run: ls
49 | - name: Install dependencies
50 | run: npm install
51 | - name: Build Electron
52 | run: npm run electron:windows
53 | - name: see directory
54 | run: ls
55 | - uses: lucyio/upload-to-release@master
56 | with:
57 | name: JP1016/Markdown-Electron
58 | path: ./release
59 | action: published
60 | repo-token: ${{ secrets.GITHUB_TOKEN }}
61 | release_id: 1
62 | release-repo: JP1016/Markdown-Electron
63 | build_on_linux:
64 | runs-on: ubuntu-latest
65 | steps:
66 | - uses: actions/checkout@master
67 | with:
68 | ref: master
69 | - uses: actions/setup-node@master
70 | with:
71 | node-version: 10.16
72 | - name: see directory
73 | run: ls
74 | - name: Install dependencies
75 | run: npm install
76 | - name: Build Electron
77 | run: npm run electron:linux
78 | - name: see directory
79 | run: ls
80 | - uses: lucyio/upload-to-release@master
81 | with:
82 | name: JP1016/Markdown-Electron
83 | path: ./release
84 | action: published
85 | repo-token: ${{ secrets.GITHUB_TOKEN }}
86 | release_id: 1
87 | release-repo: JP1016/Markdown-Electron
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import '../polyfills';
3 |
4 | import { BrowserModule } from '@angular/platform-browser';
5 | import { NgModule } from '@angular/core';
6 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
7 | import { HttpClientModule, HttpClient } from '@angular/common/http';
8 | import { CoreModule } from './core/core.module';
9 | import { SharedModule } from './shared/shared.module';
10 |
11 | import { AppRoutingModule } from './app-routing.module';
12 |
13 | // NG Translate
14 | import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
15 | import { TranslateHttpLoader } from '@ngx-translate/http-loader';
16 |
17 | import { HomeModule } from './home/home.module';
18 |
19 | import { AppComponent } from './app.component';
20 | import { ServiceWorkerModule } from '@angular/service-worker';
21 | import { AppConfig } from '../environments/environment';
22 | import { NavbarComponent } from './navbar/navbar.component';
23 | import { MarkupComponent } from './markup/markup.component';
24 | import { AddEmojiComponent } from './add-emoji/add-emoji.component';
25 | import { OptionsDialogComponent } from './options-dialog/options-dialog.component';
26 | import { SaveDialogComponent } from './save-dialog/save-dialog.component';
27 | import { LoadDialogComponent } from './load-dialog/load-dialog.component';
28 | import { MatDialogModule, MatTooltipModule, MatIconModule, MatSnackBarModule } from '@angular/material';
29 | import { IconsModule } from './icons/icons.module';
30 | import { KeyboardShortcutsModule } from 'ng-keyboard-shortcuts';
31 | import { IndexedDBModule } from 'ng-indexed-db';
32 | import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
33 | import { NtkmeButtonModule } from "@ctrl/ngx-github-buttons";
34 | import { AngularSplitModule } from "angular-split";
35 | import { MarkdownModule } from "ngx-markdown";
36 | import { PickerModule } from "@ctrl/ngx-emoji-mart";
37 | // AoT requires an exported function for factories
38 | export function HttpLoaderFactory(http: HttpClient) {
39 | return new TranslateHttpLoader(http, './assets/i18n/', '.json');
40 | }
41 |
42 | @NgModule({
43 | declarations: [
44 | AppComponent],
45 | imports: [
46 | BrowserModule,
47 | FormsModule,
48 | HttpClientModule,
49 | CoreModule,
50 | SharedModule,
51 | HomeModule,
52 | AppRoutingModule,
53 | BrowserAnimationsModule,
54 | ReactiveFormsModule,
55 | TranslateModule.forRoot({
56 | loader: {
57 | provide: TranslateLoader,
58 | useFactory: HttpLoaderFactory,
59 | deps: [HttpClient]
60 | }
61 | }),
62 | ServiceWorkerModule.register('ngsw-worker.js', { enabled: AppConfig.production })
63 | ],
64 | providers: [],
65 | bootstrap: [AppComponent]
66 | })
67 | export class AppModule { }
68 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 📖 Markdown Editor
2 |
3 |
4 |
5 | ### ⚡Creating markdown made easy!!
6 |
7 | []()
8 |
9 | ```javascript
10 | 🎉 Features
11 |
12 | ```
13 |
14 | #### 👆One Click Licence, Contribution Guidelines Export
15 |
16 | #### 💾Load and Save Markdown to Indexed DB
17 |
18 | #### 🚀Copy/Download Markdown with one-click
19 |
20 | #### 🌟Auto Save for Markdowns
21 |
22 | #### 🌓Dark/Light Mode
23 |
24 | #### 🎎Resizable Split UI
25 |
26 | #### ✨Assist for Inserting Images/Link
27 |
28 | #### 🚅Save Frequently used Contributing Guidelines/Contributors/Licence on to local storage
29 |
30 | #### 📬Opens the recent file, that you were editing on resuming
31 |
32 | #### ⚛️ Electron & PWA app for offline use
33 |
34 | #### 🔌 No Internet
35 |
36 | #### 📖 OpenSource
37 |
38 | Made with ❤️ by
39 |
40 | ```javascript
41 | 🌟 Markdown Samples
42 |
43 | ```
44 |
45 | | Formatting | Example |
46 | | ------------ | ---------------------------- |
47 | | Bold Text | **This is a bold text** |
48 | | Italics | _This will be in italics_ |
49 | | Striked Text | ~~This will appear striked~~ |
50 |
51 | Heading - ### Sample H3 Header
52 |
53 | ### Bullet List
54 |
55 | - Bullet 1
56 | - Bullet 2
57 |
58 | ### Checkbox
59 |
60 | - [ ] Unchecked Checkbox
61 | - [x] Checked
62 |
63 | ### Blockquote
64 |
65 | > This is a sample blockquote
66 |
67 | ### Code
68 |
69 | ```javascript
70 | let a = 1;
71 | (function() {
72 | console.log("IIFE");
73 | })();
74 | ```
75 |
76 | ### Table
77 |
78 | | Name | Purpose |
79 | | --------------------------------- | --------------------- |
80 | | [Paper](https://paperapp.now.sh/) | Note Taking App |
81 | | [Markdown](https://mdown.now.sh) | Markdown Creation app |
82 |
83 | ### Link
84 |
85 | [Markdown Web URL](https://mdown.now.sh)
86 |
87 | ### Image
88 |
89 | Original Size
90 | 
91 |
92 | Custom Size
93 |
94 |
95 | ```javascript
96 | 🔨 Stack
97 | ```
98 |
99 | - Angular
100 | - Indexed DB
101 | - Electron
102 |
103 | ```javascript
104 | 🤝 Contributing
105 | ```
106 |
107 | Contributions, issues and feature requests are welcome! 😍
108 |
109 | Show your support
110 |
111 | Give a ⭐️ if this project helped you! 🥰
112 |
113 | If you like this app , Star it on Github, Follow me on Twitter
114 |
115 | Icons by FeatherIcons from https://feathericons.com
116 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": ["node_modules/codelyzer"],
3 | "rules": {
4 | "arrow-return-shorthand": true,
5 | "callable-types": true,
6 | "class-name": true,
7 | "comment-format": [true, "check-space"],
8 | "component-class-suffix": true,
9 | "component-selector": [true, "element", "app", "kebab-case"],
10 | "curly": true,
11 | "deprecation": { "severity": "warn" },
12 | "directive-class-suffix": true,
13 | "eofline": true,
14 | "forin": true,
15 | "import-blacklist": [true, "rxjs/Rx"],
16 | "import-spacing": true,
17 | "indent": [true, "spaces"],
18 | "interface-over-type-literal": true,
19 | "label-position": true,
20 | "max-line-length": [true, 140],
21 | "member-access": false,
22 | "member-ordering": [
23 | true,
24 | {
25 | "order": [
26 | "static-field",
27 | "instance-field",
28 | "static-method",
29 | "instance-method"
30 | ]
31 | }
32 | ],
33 | "no-arg": true,
34 | "no-bitwise": true,
35 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
36 | "no-construct": true,
37 | "no-debugger": true,
38 | "no-duplicate-super": true,
39 | "no-empty": false,
40 | "no-empty-interface": true,
41 | "no-eval": true,
42 | "no-host-metadata-property": true,
43 | "no-inferrable-types": [true, "ignore-params"],
44 | "no-input-rename": true,
45 | "no-inputs-metadata-property": true,
46 | "no-misused-new": true,
47 | "no-non-null-assertion": true,
48 | "no-output-on-prefix": true,
49 | "no-output-rename": true,
50 | "no-outputs-metadata-property": true,
51 | "no-shadowed-variable": true,
52 | "no-string-literal": false,
53 | "no-string-throw": true,
54 | "no-switch-case-fall-through": true,
55 | "no-trailing-whitespace": true,
56 | "no-unnecessary-initializer": true,
57 | "no-unused-expression": true,
58 | "no-var-keyword": true,
59 | "object-literal-sort-keys": false,
60 | "one-line": [
61 | true,
62 | "check-open-brace",
63 | "check-catch",
64 | "check-else",
65 | "check-whitespace"
66 | ],
67 | "prefer-const": true,
68 | "quotemark": [true, "single"],
69 | "radix": true,
70 | "semicolon": [true, "always"],
71 | "triple-equals": [true, "allow-null-check"],
72 | "typedef-whitespace": [
73 | true,
74 | {
75 | "call-signature": "nospace",
76 | "index-signature": "nospace",
77 | "parameter": "nospace",
78 | "property-declaration": "nospace",
79 | "variable-declaration": "nospace"
80 | }
81 | ],
82 | "unified-signatures": true,
83 | "use-life-cycle-interface": true,
84 | "use-lifecycle-interface": true,
85 | "use-pipe-transform-interface": true,
86 | "variable-name": false,
87 | "whitespace": [
88 | true,
89 | "check-branch",
90 | "check-decl",
91 | "check-operator",
92 | "check-separator",
93 | "check-type"
94 | ]
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
22 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
23 |
24 | /**
25 | * Web Animations `@angular/platform-browser/animations`
26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
28 | */
29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
30 |
31 | /**
32 | * By default, zone.js will patch all possible macroTask and DomEvents
33 | * user can disable parts of macroTask/DomEvents patch by setting following flags
34 | * because those flags need to be set before `zone.js` being loaded, and webpack
35 | * will put import in the top of bundle, so user need to create a separate file
36 | * in this directory (for example: zone-flags.ts), and put the following flags
37 | * into that file, and then add the following code before importing zone.js.
38 | * import './zone-flags.ts';
39 | *
40 | * The flags allowed in zone-flags.ts are listed here.
41 | *
42 | * The following flags will work for all browsers.
43 | *
44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
47 | *
48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
50 | *
51 | * (window as any).__Zone_enable_cross_context_check = true;
52 | *
53 | */
54 |
55 | /***************************************************************************************************
56 | * Zone JS is required by default for Angular itself.
57 | */
58 | import 'zone.js/dist/zone'; // Included with Angular CLI.
59 |
60 |
61 | /***************************************************************************************************
62 | * APPLICATION IMPORTS
63 | */
64 |
--------------------------------------------------------------------------------
/src/app/constants/form-constants.ts:
--------------------------------------------------------------------------------
1 | import { CONTRIBUTION_GUIDE } from "./app-constants";
2 |
3 | export const IMAGE_SELECT = "image";
4 | export const LINK_SELECT = "link";
5 | export const CONTRIB_GUIDELINES_SELECT = "cguide";
6 | export const CONTRIB_LIST = "clist";
7 | export const LICENCE_SELECT = "licence";
8 | export const SAVE_TEMPLATE = "save";
9 | export const LOAD_TEMPLATE = "load";
10 | const END_QUOTES = "```";
11 |
12 | export const FORM_OPTIONS = {
13 | [IMAGE_SELECT]: {
14 | title: "Insert Image",
15 | fields: [
16 | {
17 | label: "Enter Image URL",
18 | name: "link"
19 | },
20 | {
21 | label: "Enter Image Description",
22 | name: "description"
23 | }
24 | ]
25 | },
26 | [LINK_SELECT]: {
27 | title: "Insert Link",
28 | fields: [
29 | {
30 | label: "Enter URL",
31 | name: "link"
32 | },
33 | {
34 | label: "Enter Description",
35 | name: "description"
36 | }
37 | ]
38 | },
39 | [CONTRIB_GUIDELINES_SELECT]: {
40 | title: "Add Contribution Guideline",
41 | fields: [
42 | {
43 | label: "Enter Contibution Guideline",
44 | name: "descr"
45 | }
46 | ],
47 | default: `## Contributing
48 | If you've ever wanted to contribute to open source, and a great cause, now is your chance!
49 |
50 | See the [contributing docs](http://example.com) for more information`
51 | },
52 | [CONTRIB_LIST]: {
53 | title: "Add Contribution List",
54 | fields: [
55 | {
56 | label: "Enter Contibution List",
57 | name: "descr"
58 | }
59 | ],
60 | default: `## Contributors ✨
61 |
62 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
63 |
64 |
65 |
66 |
97 | `
98 | },
99 | [LICENCE_SELECT]: {
100 | title: "Enter licence information",
101 | fields: [
102 | {
103 | label: "Enter licence information",
104 | name: "descr"
105 | }
106 | ],
107 | default: `## LICENCE
108 | ${END_QUOTES}
109 | Copyright 2019
110 |
111 | Licensed under the Apache License, Version 2.0 (the "License");
112 | you may not use this file except in compliance with the License.
113 | You may obtain a copy of the License at
114 |
115 | http://www.apache.org/licenses/LICENSE-2.0
116 |
117 | Unless required by applicable law or agreed to in writing, software
118 | distributed under the License is distributed on an "AS IS" BASIS,
119 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
120 | See the License for the specific language governing permissions and
121 | limitations under the License.
122 | ${END_QUOTES}`
123 | }
124 | };
125 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "MarkdownEditor",
3 | "version": "6.1.0",
4 | "description": "Markdown editor by devzstudio",
5 | "homepage": "https://github.com/JP1016/Markdown",
6 | "author": {
7 | "name": "Jithin P",
8 | "email": "emast007@gmail.com"
9 | },
10 | "keywords": [
11 | "markdown",
12 | "markdown editor",
13 | "markdown generator",
14 | "free markdown editor",
15 | "markdown pwa app"
16 | ],
17 | "main": "main.js",
18 | "private": true,
19 | "scripts": {
20 | "postinstall": "npm run postinstall:electron && electron-builder install-app-deps",
21 | "postinstall:web": "node postinstall-web",
22 | "postinstall:electron": "node postinstall",
23 | "ng": "ng",
24 | "start": "npm run postinstall:electron && npm-run-all -p ng:serve electron:serve",
25 | "build": "npm run postinstall:electron && npm run electron:serve-tsc && ng build",
26 | "build:dev": "npm run build -- -c dev",
27 | "build:prod": "npm run build -- -c production",
28 | "ng:serve": "ng serve",
29 | "ng:serve:web": "npm run postinstall:web && ng serve -o",
30 | "electron:serve-tsc": "tsc -p tsconfig-serve.json",
31 | "electron:serve": "wait-on http-get://localhost:4200/ && npm run electron:serve-tsc && electron . --serve",
32 | "electron:local": "npm run build:prod && electron .",
33 | "electron:linux": "npm run build:prod && electron-builder build --linux",
34 | "electron:windows": "npm run build:prod && electron-builder build --windows",
35 | "electron:mac": "npm run build:prod && electron-builder build --mac",
36 | "test": "npm run postinstall:web && ng test",
37 | "e2e": "npm run build:prod && mocha --timeout 300000 --require ts-node/register e2e/**/*.spec.ts",
38 | "version": "conventional-changelog -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md",
39 | "lint": "ng lint"
40 | },
41 | "devDependencies": {
42 | "@angular-devkit/build-angular": "0.802.2",
43 | "@angular/cli": "8.2.2",
44 | "@angular/common": "8.2.2",
45 | "@angular/compiler": "8.2.2",
46 | "@angular/compiler-cli": "8.2.2",
47 | "@angular/core": "8.2.2",
48 | "@angular/forms": "8.2.2",
49 | "@angular/language-service": "8.2.2",
50 | "@angular/platform-browser": "8.2.2",
51 | "@angular/platform-browser-dynamic": "8.2.2",
52 | "@angular/router": "8.2.2",
53 | "@angular/service-worker": "8.2.2",
54 | "@ngx-translate/core": "11.0.1",
55 | "@ngx-translate/http-loader": "4.0.0",
56 | "@types/jasmine": "3.3.16",
57 | "@types/jasminewd2": "2.0.6",
58 | "@types/mocha": "5.2.7",
59 | "@types/node": "12.6.8",
60 | "chai": "4.2.0",
61 | "codelyzer": "5.1.0",
62 | "conventional-changelog-cli": "2.0.21",
63 | "core-js": "3.1.4",
64 | "electron": "^6.0.2",
65 | "electron-builder": "^21.2.0",
66 | "electron-reload": "1.5.0",
67 | "jasmine-core": "3.4.0",
68 | "jasmine-spec-reporter": "4.2.1",
69 | "karma": "4.2.0",
70 | "karma-chrome-launcher": "3.0.0",
71 | "karma-coverage-istanbul-reporter": "2.1.0",
72 | "karma-jasmine": "2.0.1",
73 | "karma-jasmine-html-reporter": "1.4.2",
74 | "mocha": "6.2.0",
75 | "npm-run-all": "4.1.5",
76 | "rxjs": "6.5.2",
77 | "spectron": "8.0.0",
78 | "ts-node": "8.3.0",
79 | "tslint": "5.18.0",
80 | "typescript": "3.5.3",
81 | "wait-on": "3.3.0",
82 | "webdriver-manager": "12.1.5",
83 | "zone.js": "0.9.1",
84 | "@ctrl/ngx-emoji-mart": "^1.0.1",
85 | "@ctrl/ngx-github-buttons": "^3.2.2",
86 | "angular-feather": "^6.0.2",
87 | "angular-split": "^3.0.2",
88 | "ng-indexed-db": "0.0.0-beta.1",
89 | "ng-keyboard-shortcuts": "^8.0.0",
90 | "ngx-markdown": "^8.1.0",
91 | "prismjs": "^1.17.1",
92 | "tailwindcss": "^1.1.2"
93 | },
94 | "engines": {
95 | "node": ">=10.9.0"
96 | },
97 | "dependencies": {
98 | "@angular/animations": "^8.0.0 || ^9.0.0-0",
99 | "@angular/cdk": "~8.2.1",
100 | "@angular/forms": "^8.0.0 || ^9.0.0-0",
101 | "@angular/material": "^8.2.1",
102 | "@angular/pwa": "^0.803.6",
103 | "hammerjs": "^2.0.8",
104 | "ngx-electron": "^2.1.1"
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Markdown Editor
6 |
7 |
11 |
12 |
16 |
22 |
28 |
29 |
35 |
36 |
40 |
41 |
42 |
43 |
44 |
45 |
49 |
53 |
54 |
62 |
63 |
64 |
65 |
66 |
68 |
69 |
70 |
102 |
105 |
106 |
107 | Loading...
108 | Please enable JavaScript to continue using this application.
111 |
112 |
113 |
--------------------------------------------------------------------------------
/src/app/constants/app-constants.ts:
--------------------------------------------------------------------------------
1 | const DESC = 'enter description here';
2 |
3 | export const OPTION = Object.freeze({
4 | BOLD: "bold",
5 | ITALIC: "italic",
6 | SIZE: "type",
7 | STRIKE: "minus",
8 | LIST: "list",
9 | CHECK_BOX: "check-square",
10 | BLOCK_QUOTE: "chevron-right",
11 | CODE: "code",
12 | TABLE: "columns",
13 | LINK: "link",
14 | IMAGE: "image"
15 | })
16 | const END_QUOTES = "```"
17 |
18 | export const TOOLBAR = Object.freeze({
19 | [OPTION.BOLD]: {
20 | text: "Bold ⌘+Shift+B",
21 | startTag: "**",
22 | endTag: "**"
23 | },
24 | [OPTION.ITALIC]: {
25 | text: "Italic ⌘+Shift+I",
26 | startTag: "*",
27 | endTag: "*"
28 | },
29 | [OPTION.SIZE]: {
30 | text: "Heading ⌘+Shift+H",
31 | startTag: "#"
32 | },
33 | [OPTION.STRIKE]: {
34 | text: "Strike ⌘+Shift+S",
35 | startTag: "~~",
36 | endTag: "~~"
37 | },
38 | [OPTION.LIST]: {
39 | text: "Bullet List ⌘+Shift+L",
40 | startTag: "- "
41 | },
42 | [OPTION.CHECK_BOX]: {
43 | text: "List ⌘+Shift+C",
44 | startTag: "- [ ] "
45 | },
46 | [OPTION.BLOCK_QUOTE]: {
47 | text: "Blockquote ⌘+Shift+Q",
48 | startTag: "> "
49 | },
50 | [OPTION.CODE]: {
51 | text: "Code ⌘+Shift+D",
52 | startTag: '```javascript ',
53 | endTag: END_QUOTES
54 | },
55 | [OPTION.TABLE]: {
56 | text: "Table ⌘+Shift+T",
57 | startTag:
58 | `| Name | Heading |
59 | |--|--|
60 | | Foo | Bar |`
61 | },
62 | [OPTION.LINK]: {
63 | text: "Link ⌘+Shift+K",
64 | startTag: `[${DESC}](`,
65 | endTag: ")"
66 | },
67 | [OPTION.IMAGE]: {
68 | text: "Image ⌘+Shift+G",
69 | startTag: `"
71 | }
72 | });
73 |
74 | export const CONTRIBUTORS = `
75 | ## Contributors ✨
76 |
77 | Thanks goes to these wonderful people
78 |
79 |
80 |
81 | `
112 |
113 | export const CONTRIBUTION_GUIDE = `
114 | Contributing
115 | If you've ever wanted to contribute to open source, and a great cause, now is your chance!
116 | See the contributing docs for more information
117 | `
118 |
119 | export const LICENCE = `# License
120 |
121 | Copyright 2018
122 |
123 | Licensed under the Apache License, Version 2.0 (the "License");
124 | you may not use this file except in compliance with the License.
125 | You may obtain a copy of the License at
126 |
127 | http://www.apache.org/licenses/LICENSE-2.0
128 |
129 | Unless required by applicable law or agreed to in writing, software
130 | distributed under the License is distributed on an "AS IS" BASIS,
131 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132 | See the License for the specific language governing permissions and
133 | limitations under the License.`
134 |
135 | export const SAMPLE =
136 | `
137 |
138 | # Paper
139 |
140 | 🚀A "no-cloud" note taking app with "no-internet" sharing.
141 |
142 | []()
143 |
144 | ✅ Live Preview: https://paperapp.now.sh
145 |
146 |
147 | Made with ❤️ by
148 |
149 |
150 |
151 | ## Features
152 |
153 | 🔥PWA Based
154 |
155 | 📖 OpenSource
156 |
157 | ⛓️ Easy Share
158 |
159 | 🔌 No Internet
160 |
161 | 🖥️ LocalStorage
162 |
163 | 📠 Share through QR
164 |
165 | 🌗 Dark/Light Theme
166 |
167 | ## 🤝 Contributing
168 |
169 | Contributions, issues and feature requests are welcome! 😍
170 |
171 | ## Show your support
172 |
173 | Give a ⭐️ if this project helped you! 🥰
174 |
175 | If you like this app , Star it on Github, Follow me on Twitter
176 | `
177 |
--------------------------------------------------------------------------------
/src/app/navbar/navbar.component.html:
--------------------------------------------------------------------------------
1 |
5 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | @import '~@angular/material/prebuilt-themes/indigo-pink.css';
3 |
4 | body[class='dark-mode'] {
5 | --bg: #0f1119;
6 | --text: #fff;
7 | --text-color: #718096;
8 | --shadow: rgba(27, 31, 46, 0.4);
9 | --selected: #12151f;
10 | --sidebar-shadow: rgba(39, 37, 39, 0.4);
11 | --border: #3333337a;
12 | --button: #1a202c;
13 | --btn-hover: #2d3748;
14 | --sidebar: #1a1c25;
15 | --list-color: #556080;
16 | --ernote-hov: #1a202c;
17 | --note-main-text: #e2e8f0;
18 | --note-sub-text: #718096;
19 | --modal-bg: #1a202c;
20 | --split-img: url('/assets/more-vertical.svg');
21 |
22 | --blockquote: #ededed12;
23 |
24 | --table-odd: #090b0f;
25 | --table-even: #0f111a;
26 | --table-border: #333;
27 | }
28 |
29 | body[class='light-mode'] {
30 | --bg: #fff;
31 | --text: #212121;
32 | --shadow: rgba(0, 0, 0, 0.04);
33 | --sidebar-shadow: rgba(26, 6, 6, 0.2);
34 | --border: #3333330f;
35 | --button: #edf2f7;
36 | --btn-hover: #e2e8f0;
37 | --sidebar: #edf2f7;
38 | --list-color: #474747;
39 | --selected: #e2e8f0;
40 |
41 | --note-hover: #f7fafc;
42 | --note-main-text: #4a5568;
43 | --note-sub-text: #a0aec0;
44 | --modal-bg: #3b434f;
45 | --split-img: url('/assets/more-vertical-light.svg');
46 | --blockquote: #edf2f6;
47 |
48 | --table-odd: #f7fafc;
49 | --table-even: #edf2f7;
50 | --table-border: #a0aec0;
51 | }
52 |
53 | * {
54 | font-family: 'Quicksand', sans-serif;
55 | }
56 |
57 | ::selection {
58 | background: #79ffe1;
59 | }
60 |
61 | button:focus {
62 | outline: 0;
63 | }
64 |
65 | html,
66 | body {
67 | font-family: 'Quicksand', sans-serif;
68 | background: var(--bg);
69 | overflow: hidden;
70 | color: var(--text);
71 | }
72 |
73 | .toggle-icon {
74 | display: none;
75 | }
76 |
77 | .fe-icon {
78 | width: 15px !important;
79 | color: var(--text);
80 | height: 16px !important;
81 | }
82 |
83 | .fe-icon:hover {
84 | color: #718096;
85 | }
86 |
87 | .me-editable {
88 | height: 80vh;
89 | padding: 20px;
90 | outline: 0 solid transparent;
91 | }
92 |
93 | .custom-class-h1 {
94 | font-size: 200px;
95 | }
96 |
97 | .custom-class-h2 {
98 | font-size: 15px;
99 | }
100 |
101 | .notelist {
102 | border-bottom: 1px solid var(--border);
103 | padding: 4px 16px;
104 | }
105 |
106 | .notelist .text-sm {
107 | color: var(--note-main-text);
108 | }
109 |
110 | .notelist .text-xs {
111 | color: var(--note-sub-text);
112 | }
113 |
114 | .notelist:hover {
115 | background: var(--note-hover);
116 | }
117 |
118 | .mat-dialog-container {
119 | background: var(--modal-bg);
120 | }
121 |
122 | .mat-dialog-title {
123 | display: flex !important;
124 | justify-content: space-between;
125 | color: var(--note-sub-text);
126 | }
127 |
128 | .ph-badge {
129 | position: absolute;
130 | bottom: 0;
131 | margin-bottom: 40px;
132 | right: 0;
133 | margin-right: 17px;
134 | }
135 |
136 | @media only screen and (max-width: 600px) {
137 | .toggle-icon {
138 | display: flex;
139 | align-items: center;
140 | }
141 | }
142 |
143 | .pointer {
144 | cursor: pointer;
145 | }
146 |
147 | a {
148 | color: #1d88e5;
149 | }
150 |
151 | blockquote {
152 | font-size: 14px;
153 | font-style: italic;
154 | border-left: 3px solid #1d88e5;
155 | line-height: 1.6;
156 | position: relative;
157 | background: var(--blockquote);
158 | padding: 0.3rem 1rem;
159 | }
160 |
161 | p {
162 | margin: 15px 0;
163 | }
164 |
165 | h1 {
166 | font-size: 2em;
167 | }
168 |
169 | h2 {
170 | font-size: 1.5em;
171 | }
172 |
173 | h3 {
174 | font-size: 1.17em;
175 | }
176 |
177 | h4 {
178 | font-size: 1.12em;
179 | }
180 |
181 | h5 {
182 | font-size: 0.83em;
183 | }
184 |
185 | h6 {
186 | font-size: 0.75em;
187 | }
188 |
189 | table,
190 | thead tr,
191 | tbody tr {
192 | border: 1px solid var(--table-border);
193 | }
194 |
195 | th,
196 | td {
197 | padding: 10px;
198 | }
199 |
200 | tr:nth-of-type(odd) {
201 | background-color: var(--table-odd);
202 | }
203 |
204 | tr:nth-of-type(even) {
205 | background-color: var(--table-even);
206 | }
207 |
208 | ul {
209 | list-style-type: disc;
210 | list-style-position: inside;
211 | }
212 |
213 | ol {
214 | list-style-type: decimal;
215 | list-style-position: inside;
216 | }
217 |
218 | ul ul,
219 | ol ul {
220 | list-style-type: circle;
221 | list-style-position: inside;
222 | margin-left: 15px;
223 | }
224 |
225 | ol ol,
226 | ul ol {
227 | list-style-type: lower-latin;
228 | list-style-position: inside;
229 | margin-left: 15px;
230 | }
231 |
232 | .as-split-gutter-icon {
233 | background: var(--sidebar);
234 | background-image: var(--split-img) !important;
235 | }
236 |
237 | .emoji-add .mat-dialog-container {
238 | padding: 0px !important;
239 | background: unset !important;
240 | }
241 |
242 | .mat-dialog-container {
243 | color: var(--text);
244 | min-width: 500px;
245 | background: var(--bg);
246 | }
247 |
248 | *.unselectable {
249 | -moz-user-select: none;
250 | -khtml-user-select: none;
251 | -webkit-user-select: none;
252 | -ms-user-select: none;
253 | user-select: none;
254 | }
255 |
256 | button:disabled {
257 | background: #494949 !important;
258 | color: #e2e8f0;
259 | cursor: not-allowed;
260 | }
261 |
262 | .close-icon {
263 | font-size: 30px;
264 | }
265 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "angular-electron": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "architect": {
11 | "build": {
12 | "builder": "@angular-devkit/build-angular:browser",
13 | "options": {
14 | "outputPath": "dist",
15 | "index": "src/index.html",
16 | "main": "src/main.ts",
17 | "tsConfig": "src/tsconfig.app.json",
18 | "polyfills": "src/polyfills.ts",
19 | "assets": [
20 | "src/assets",
21 | "src/favicon.ico",
22 | "src/favicon.png",
23 | "src/favicon.icns",
24 | "src/favicon.256x256.png",
25 | "src/favicon.512x512.png",
26 | "src/manifest.webmanifest"
27 | ],
28 | "styles": [
29 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
30 | "src/styles.scss",
31 | "node_modules/marked/lib/marked.js", "node_modules/prismjs/themes/prism-okaidia.css", "node_modules/@ctrl/ngx-emoji-mart/picker.css"
32 | ],
33 | "scripts": [
34 | "node_modules/prismjs/prism.js",
35 | "node_modules/prismjs/components/prism-css.min.js"
36 | ]
37 | },
38 | "configurations": {
39 | "dev": {
40 | "optimization": false,
41 | "outputHashing": "all",
42 | "sourceMap": true,
43 | "extractCss": true,
44 | "namedChunks": false,
45 | "aot": false,
46 | "extractLicenses": true,
47 | "vendorChunk": false,
48 | "buildOptimizer": false,
49 | "fileReplacements": [{
50 | "replace": "src/environments/environment.ts",
51 | "with": "src/environments/environment.dev.ts"
52 | }]
53 | },
54 | "production": {
55 | "optimization": true,
56 | "outputHashing": "all",
57 | "sourceMap": false,
58 | "extractCss": true,
59 | "namedChunks": false,
60 | "aot": true,
61 | "extractLicenses": true,
62 | "vendorChunk": false,
63 | "buildOptimizer": true,
64 | "fileReplacements": [{
65 | "replace": "src/environments/environment.ts",
66 | "with": "src/environments/environment.prod.ts"
67 | }],
68 | "serviceWorker": true,
69 | "ngswConfigPath": "ngsw-config.json"
70 | }
71 | }
72 | },
73 | "serve": {
74 | "builder": "@angular-devkit/build-angular:dev-server",
75 | "options": {
76 | "browserTarget": "angular-electron:build"
77 | },
78 | "configurations": {
79 | "dev": {
80 | "browserTarget": "angular-electron:build:dev"
81 | },
82 | "production": {
83 | "browserTarget": "angular-electron:build:production"
84 | }
85 | }
86 | },
87 | "extract-i18n": {
88 | "builder": "@angular-devkit/build-angular:extract-i18n",
89 | "options": {
90 | "browserTarget": "angular-electron:build"
91 | }
92 | },
93 | "test": {
94 | "builder": "@angular-devkit/build-angular:karma",
95 | "options": {
96 | "main": "src/test.ts",
97 | "polyfills": "src/polyfills-test.ts",
98 | "tsConfig": "src/tsconfig.spec.json",
99 | "karmaConfig": "src/karma.conf.js",
100 | "scripts": [],
101 | "styles": [
102 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
103 | "src/styles.scss"
104 | ],
105 | "assets": [
106 | "src/assets",
107 | "src/favicon.ico",
108 | "src/favicon.png",
109 | "src/favicon.icns",
110 | "src/favicon.256x256.png",
111 | "src/favicon.512x512.png",
112 | "src/manifest.webmanifest"
113 | ]
114 | }
115 | },
116 | "lint": {
117 | "builder": "@angular-devkit/build-angular:tslint",
118 | "options": {
119 | "tsConfig": [
120 | "src/tsconfig.app.json",
121 | "src/tsconfig.spec.json"
122 | ],
123 | "exclude": [
124 | "**/node_modules/**"
125 | ]
126 | }
127 | }
128 | }
129 | },
130 | "angular-electron-e2e": {
131 | "root": "e2e",
132 | "projectType": "application",
133 | "architect": {
134 | "lint": {
135 | "builder": "@angular-devkit/build-angular:tslint",
136 | "options": {
137 | "tsConfig": [
138 | "e2e/tsconfig.e2e.json"
139 | ],
140 | "exclude": [
141 | "**/node_modules/**"
142 | ]
143 | }
144 | }
145 | }
146 | }
147 | },
148 | "defaultProject": "angular-electron",
149 | "schematics": {
150 | "@schematics/angular:component": {
151 | "prefix": "app",
152 | "styleext": "scss"
153 | },
154 | "@schematics/angular:directive": {
155 | "prefix": "app"
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/app/navbar/navbar.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Inject, Renderer2, ViewChild } from "@angular/core";
2 | import { DOCUMENT } from "@angular/common";
3 | import { MatDialog } from "@angular/material";
4 | import { AddEmojiComponent } from "../add-emoji/add-emoji.component";
5 | import { ShortcutInput, AllowIn } from "ng-keyboard-shortcuts";
6 | import { Observable } from "rxjs";
7 | import { IndexedDB } from "ng-indexed-db";
8 | import { OptionsDialogComponent } from "../options-dialog/options-dialog.component";
9 | import { SaveDialogComponent } from "../save-dialog/save-dialog.component";
10 | import { LoadDialogComponent } from "../load-dialog/load-dialog.component";
11 | import { OPTION, TOOLBAR } from "../constants/app-constants";
12 | import { CONTRIB_GUIDELINES_SELECT, CONTRIB_LIST, LICENCE_SELECT } from "../constants/form-constants";
13 | import { MarkdownService } from "../core/services/markdown.service";
14 |
15 | @Component({
16 | selector: "app-navbar",
17 | templateUrl: "./navbar.component.html",
18 | styleUrls: ["./navbar.component.css"]
19 | })
20 | export class NavbarComponent implements OnInit {
21 | public isDarkMode = true;
22 | public options = OPTION;
23 | public toolbar = TOOLBAR;
24 | public fileName;
25 | shortcuts: ShortcutInput[] = [];
26 | $list: Observable;
27 |
28 | public CONTRIB_GUIDELINES_SELECT = CONTRIB_GUIDELINES_SELECT;
29 | public CONTRIB_LIST = CONTRIB_LIST;
30 | public LICENCE_SELECT = LICENCE_SELECT;
31 |
32 | constructor(
33 | @Inject(DOCUMENT) private document: Document,
34 | private renderer: Renderer2,
35 | private dialog: MatDialog,
36 | private markDown: MarkdownService,
37 | private indexedDbService: IndexedDB
38 | ) {
39 | this.$list = this.indexedDbService.list("markdown_table");
40 | }
41 |
42 | ngOnInit() {
43 | const theme = localStorage.getItem("theme") || "dark-mode";
44 | this.switchTheme(theme);
45 | this.loadedMarkupFromLocalStorage();
46 | }
47 |
48 | loadedMarkupFromLocalStorage() {
49 | this.markDown.markdownFromLocalStorage.subscribe(markdown => {
50 | if (markdown) {
51 | this.fileName = markdown.title;
52 | }
53 | })
54 | }
55 | copyMarkup() {
56 | this.markDown.copyMarkdown.next(true);
57 | }
58 |
59 | downloadMarkup() {
60 | this.markDown.downloadMarkdown.next(true);
61 | }
62 |
63 | newFile() {
64 | this.fileName = null;
65 | this.markDown.newMarkdown.next(true);
66 | }
67 |
68 | saveMarkup() {
69 | const dialogRef = this.dialog.open(SaveDialogComponent, {
70 | data: {
71 | text: null
72 | }
73 | });
74 |
75 | dialogRef.afterClosed().subscribe(result => {
76 | if (result && result.hasOwnProperty("data")) {
77 | this.fileName = result.data;
78 | }
79 | });
80 |
81 | }
82 |
83 | loadMarkup() {
84 | const dialogRef = this.dialog.open(LoadDialogComponent, {
85 | data: {
86 | text: null
87 | }
88 | });
89 | dialogRef.afterClosed().subscribe(result => {
90 | if (result && result.hasOwnProperty("data")) {
91 | this.fileName = result.data.title;
92 | }
93 | });
94 | }
95 |
96 | ngAfterViewInit(): void {
97 | this.shortcuts.push(
98 | {
99 | key: ["cmd + shift + b", "ctrl + shift + b"],
100 | allowIn: [AllowIn.Textarea],
101 | command: e => this.markDown.optionChanged.next(OPTION.BOLD),
102 | preventDefault: true
103 | },
104 | {
105 | key: ["cmd + shift + i", "ctrl + shift + i"],
106 | allowIn: [AllowIn.Textarea],
107 | command: e => this.markDown.optionChanged.next(OPTION.ITALIC),
108 | preventDefault: true
109 | },
110 | {
111 | key: ["cmd + shift + H", "ctrl + shift + H"],
112 | allowIn: [AllowIn.Textarea],
113 | command: e => this.markDown.optionChanged.next(OPTION.SIZE),
114 | preventDefault: true
115 | },
116 | {
117 | key: ["cmd + shift + L", "ctrl + shift + L"],
118 | allowIn: [AllowIn.Textarea],
119 | command: e => this.markDown.optionChanged.next(OPTION.LIST),
120 | preventDefault: true
121 | },
122 | {
123 | key: ["cmd + shift + C", "ctrl + shift + C"],
124 | allowIn: [AllowIn.Textarea],
125 | command: e => this.markDown.optionChanged.next(OPTION.CHECK_BOX),
126 | preventDefault: true
127 | },
128 | {
129 | key: ["cmd + shift + Q", "ctrl + shift + Q"],
130 | allowIn: [AllowIn.Textarea],
131 | command: e => this.markDown.optionChanged.next(OPTION.BLOCK_QUOTE),
132 | preventDefault: true
133 | },
134 | {
135 | key: ["cmd + shift + D", "ctrl + shift + D"],
136 | allowIn: [AllowIn.Textarea],
137 | command: e => this.markDown.optionChanged.next(OPTION.CODE),
138 | preventDefault: true
139 | },
140 | {
141 | key: ["cmd + shift + T", "ctrl + shift + T"],
142 | allowIn: [AllowIn.Textarea],
143 | command: e => this.markDown.optionChanged.next(OPTION.TABLE),
144 | preventDefault: true
145 | },
146 | {
147 | key: ["cmd + shift + K", "ctrl + shift + K"],
148 | allowIn: [AllowIn.Textarea],
149 | command: e => this.markDown.optionChanged.next(OPTION.LINK),
150 | preventDefault: true
151 | },
152 | {
153 | key: ["cmd + shift + G", "ctrl + shift + G"],
154 | allowIn: [AllowIn.Textarea],
155 | command: e => this.markDown.optionChanged.next(OPTION.IMAGE),
156 | preventDefault: true
157 | }
158 | );
159 | }
160 |
161 | openEmojiDialog() {
162 | const dialogRef = this.dialog.open(AddEmojiComponent, {
163 | data: {
164 | text: null
165 | },
166 | panelClass: "emoji-add"
167 | });
168 |
169 | dialogRef.afterClosed().subscribe(result => {
170 | if (result && result.hasOwnProperty("success")) {
171 | this.markDown.emojiAdded.next(result.data);
172 | }
173 | });
174 | }
175 |
176 | //Hack to prevent default angular sorting
177 | sortNull() { }
178 |
179 | formatText(option) {
180 | console.log(option);
181 |
182 | if (option == OPTION.IMAGE || option == OPTION.LINK) {
183 | const dialogRef = this.dialog.open(OptionsDialogComponent, {
184 | data: {
185 | type: option
186 | }
187 | });
188 |
189 | dialogRef.afterClosed().subscribe(result => {
190 | console.log(result);
191 | if (result && result.hasOwnProperty("success")) {
192 | this.markDown.metaAdded.next(result.data);
193 | }
194 | });
195 | } else if (Object.values(OPTION).indexOf(option) == -1) {
196 | const dialogRef = this.dialog.open(OptionsDialogComponent, {
197 | data: {
198 | type: option
199 | }
200 | });
201 |
202 | dialogRef.afterClosed().subscribe(result => {
203 | if (result && result.hasOwnProperty("success")) {
204 | console.log("Success");
205 | console.log(result);
206 | this.markDown.metaAdded.next(result.data);
207 | }
208 | });
209 | } else {
210 | console.log("should not");
211 | this.markDown.optionChanged.next(option);
212 | }
213 | }
214 |
215 | toggleMode() {
216 | let mode;
217 | if (document.body.classList.contains("dark-mode")) mode = "light-mode";
218 | else mode = "dark-mode";
219 |
220 | this.switchTheme(mode);
221 | localStorage.setItem("theme", mode);
222 | }
223 |
224 | switchTheme(mode) {
225 | let oldMode;
226 | if (mode === "light-mode") {
227 | oldMode = "dark-mode";
228 | this.isDarkMode = false;
229 | }
230 | if (mode === "dark-mode") {
231 | this.isDarkMode = true;
232 | oldMode = "light-mode";
233 | }
234 |
235 | this.renderer.removeClass(this.document.body, oldMode);
236 | this.renderer.addClass(this.document.body, mode);
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/src/app/markup/markup.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ViewChild, ElementRef, HostListener } from "@angular/core";
2 | import { FormControl } from "@angular/forms";
3 | import { MatDialog, MatSnackBar } from "@angular/material";
4 | import { HttpClient } from "@angular/common/http";
5 | import { IndexedDB } from "ng-indexed-db";
6 | import { interval } from "rxjs";
7 | import { OPTION, SAMPLE, TOOLBAR } from "../constants/app-constants";
8 | import { MarkdownService } from "../core/services/markdown.service";
9 |
10 | @Component({
11 | selector: "app-markup",
12 | templateUrl: "./markup.component.html",
13 | styleUrls: ["./markup.component.css"]
14 | })
15 | export class MarkupComponent implements OnInit {
16 | options = OPTION;
17 | markupCtrl = new FormControl();
18 | @ViewChild("markupPad", { static: false }) markupPad: ElementRef;
19 | @ViewChild('scrollRaw', { static: false }) scrollRaw: ElementRef;
20 | @ViewChild('scrollMarkup', { static: false }) scrollMarkup: ElementRef;
21 | markdownMode: Boolean = true;
22 | action = {
23 | text: 50,
24 | markup: 50
25 | };
26 | markdownData;
27 | currentMarkdown = null;
28 |
29 |
30 |
31 | constructor(
32 | private snackBar: MatSnackBar,
33 | private dialog: MatDialog,
34 | private markDownService: MarkdownService,
35 | private indexedDbService: IndexedDB
36 | ) { }
37 |
38 | //Hack to change markdown on empty/dynamic insertion
39 | triggerMarkdownChange() {
40 | this.markdownData = `
41 | `
42 | }
43 |
44 |
45 | textAreaEmpty() {
46 | if (this.markdownData.length == 0) {
47 | this.triggerMarkdownChange();
48 | }
49 | }
50 |
51 | @HostListener('window:beforeunload', ['$event'])
52 | beforeunloadHandler(event) {
53 | localStorage.setItem("saving", "true");
54 | localStorage.setItem("md", JSON.stringify(this.currentMarkdown))
55 | localStorage.setItem("recentItem", this.currentMarkdown.id);
56 | }
57 |
58 |
59 | saveFile() {
60 | interval(200).subscribe(_ => {
61 | if (this.currentMarkdown) {
62 | localStorage.setItem("recentItem", this.currentMarkdown.id);
63 | this.indexedDbService
64 | .update("markdown_store", {
65 | title: this.currentMarkdown.title,
66 | id: this.currentMarkdown.id,
67 | data: this.markdownData
68 | })
69 | .subscribe(response => {
70 | }, error => { });
71 |
72 | } else {
73 | localStorage.setItem("tempMarkdown", this.markdownData);
74 | }
75 | });
76 | }
77 |
78 | loadRecent() {
79 | const id = localStorage.getItem("recentItem");
80 | if (id) {
81 | this.currentMarkdown = JSON.parse(localStorage.getItem("md"));
82 | this.markdownData = this.currentMarkdown.data;
83 | this.markDownService.markdownFromLocalStorage.next(this.currentMarkdown);
84 | } else {
85 | this.markdownData = localStorage.getItem("tempMarkdown") || SAMPLE;
86 | }
87 | }
88 |
89 | ngOnInit() {
90 | this.listenToToolbarEvents();
91 | this.emojiAdded();
92 | this.clipboardListener();
93 | this.downloadListener();
94 | this.saveListener();
95 | this.metaListener();
96 | this.loadMarkdownListener();
97 | this.newMarkdownListener();
98 | this.saveFile();
99 | this.loadRecent();
100 |
101 | }
102 |
103 | newMarkdownListener() {
104 | this.markDownService.newMarkdown.subscribe(newFile => {
105 | if (newFile) {
106 | localStorage.removeItem("recentItem");
107 | this.currentMarkdown = null;
108 | this.triggerMarkdownChange();
109 |
110 | }
111 | });
112 | }
113 |
114 | loadMarkdownListener() {
115 | this.markDownService.loadMarkdown.subscribe(markdown => {
116 | if (markdown) {
117 |
118 | this.markdownData = markdown.data;
119 | this.currentMarkdown = markdown;
120 |
121 | }
122 | });
123 | }
124 |
125 | metaListener() {
126 | this.markDownService.metaAdded.subscribe(val => {
127 | if (val && val.hasOwnProperty("type") && val.type != "text") {
128 | const imageDiv =
129 | TOOLBAR[val.type].startTag.replace(
130 | "enter description here",
131 | val.description
132 | ) +
133 | val.link +
134 | TOOLBAR[val.type].endTag;
135 | this.insertAtCaret(imageDiv);
136 | } else if (val && val.hasOwnProperty("type") && val.type == "text") {
137 | this.insertAtCaret((val as any).descr);
138 | }
139 | });
140 | }
141 |
142 | saveMarkup(fileName) {
143 | this.indexedDbService
144 | .create("markdown_store", { title: fileName, data: this.markdownData })
145 | .subscribe(
146 | response => {
147 | this.snackBar.open("Markdown Saved !! 💾", " ", {
148 | duration: 1000
149 | });
150 | },
151 | error => {
152 | this.snackBar.open("Error Occured while saving markdown !! 🛑", " ", {
153 | duration: 1000
154 | });
155 | }
156 | );
157 | }
158 |
159 | copyMarkDown() {
160 | let selBox = document.createElement("textarea");
161 | selBox.style.position = "fixed";
162 | selBox.style.left = "0";
163 | selBox.style.top = "0";
164 | selBox.style.opacity = "0";
165 | selBox.value = this.markdownData;
166 | document.body.appendChild(selBox);
167 | selBox.focus();
168 | selBox.select();
169 | document.execCommand("copy");
170 | document.body.removeChild(selBox);
171 |
172 | this.snackBar.open("Markdown Copied to Clipboard 📋", " ", {
173 | duration: 1000
174 | });
175 | }
176 |
177 | download(filename, text) {
178 | let element = document.createElement("a");
179 | element.setAttribute(
180 | "href",
181 | "data:text/plain;charset=utf-8," + encodeURIComponent(text)
182 | );
183 | element.setAttribute("download", filename);
184 | element.style.display = "none";
185 | document.body.appendChild(element);
186 | element.click();
187 | document.body.removeChild(element);
188 | }
189 |
190 | replaceSelectedText(startTag, endTag) {
191 | let sel;
192 | let range;
193 | if (window.getSelection) {
194 | sel = window.getSelection();
195 | if (sel && sel.anchorNode.parentElement.id != "splitMain") {
196 | return;
197 | }
198 | if (sel.rangeCount) {
199 | range = sel.getRangeAt(0);
200 | const selectedContent = sel.toString();
201 | range.deleteContents();
202 | let replaceDiv = startTag + selectedContent;
203 | if (endTag) {
204 | replaceDiv = replaceDiv + endTag;
205 | }
206 | document.execCommand("insertText", false, replaceDiv);
207 | }
208 | } else if (
209 | (document as any).selection &&
210 | (document as any).selection.createRange
211 | ) {
212 | range = (document as any).selection.createRange();
213 | range.text = startTag;
214 | }
215 | }
216 |
217 | emojiAdded() {
218 | this.markDownService.emojiAdded.subscribe(emoji => {
219 | if (emoji) {
220 | this.insertAtCaret(emoji);
221 | }
222 | });
223 | }
224 |
225 | listenToToolbarEvents() {
226 | this.markDownService.optionChanged.subscribe(value => {
227 | if (value) {
228 | this.replaceSelectedText(
229 | TOOLBAR[value].startTag,
230 | TOOLBAR[value].endTag
231 | );
232 | }
233 | });
234 | }
235 |
236 | clipboardListener() {
237 | this.markDownService.copyMarkdown.subscribe(value => {
238 | if (value) {
239 | this.copyMarkDown();
240 | }
241 | });
242 | }
243 |
244 | downloadListener() {
245 | this.markDownService.downloadMarkdown.subscribe(value => {
246 | if (value) {
247 | this.download("README.md", this.markdownData);
248 | }
249 | });
250 | }
251 |
252 | saveListener() {
253 | this.markDownService.saveMarkdown.subscribe(value => {
254 | if (value) {
255 | this.saveMarkup(this.markDownService.saveMarkdown.getValue());
256 | }
257 | });
258 | }
259 |
260 | insertAtCaret(text) {
261 | if (text) {
262 | const txtarea = document.getElementById("mArea");
263 | if (!txtarea) {
264 | return;
265 | }
266 |
267 | let scrollPos = txtarea.scrollTop;
268 | let strPos = 0;
269 | let br =
270 | (txtarea as any).selectionStart ||
271 | (txtarea as any).selectionStart == "0"
272 | ? "ff"
273 | : (document as any).selection
274 | ? "ie"
275 | : false;
276 | if (br == "ie") {
277 | txtarea.focus();
278 | let range = (document as any).selection.createRange();
279 | range.moveStart("character", -(txtarea as any).value.length);
280 | strPos = range.text.length;
281 | } else if (br == "ff") {
282 | strPos = (txtarea as any).selectionStart;
283 | }
284 |
285 | let front = (txtarea as any).value.substring(0, strPos);
286 | let back = (txtarea as any).value.substring(
287 | strPos,
288 | (txtarea as any).value.length
289 | );
290 | this.markdownData = front + text + back;
291 | strPos = strPos + text.length;
292 | if (br == "ie") {
293 | txtarea.focus();
294 | let ieRange = (document as any).selection.createRange();
295 | ieRange.moveStart("character", -(txtarea as any).value.length);
296 | ieRange.moveStart("character", strPos);
297 | ieRange.moveEnd("character", 0);
298 | ieRange.select();
299 | } else if (br == "ff") {
300 | (txtarea as any).selectionStart = strPos;
301 | (txtarea as any).selectionEnd = strPos;
302 | txtarea.focus();
303 | }
304 |
305 | txtarea.scrollTop = scrollPos;
306 |
307 | }
308 |
309 |
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/src/app/navbar/navbar.component.css:
--------------------------------------------------------------------------------
1 | .header {
2 | display: flex;
3 | width: 100%;
4 | justify-content: space-between;
5 | align-items: center;
6 | }
7 |
8 | .logo {
9 | display: flex;
10 | }
11 |
12 | .logo span {
13 | visibility: hidden;
14 | }
15 |
16 | .logo:hover span {
17 | visibility: visible;
18 | }
19 |
20 | .menu {
21 | display: flex;
22 | align-items: center;
23 | flex: 1;
24 | }
25 |
26 | .end {
27 | justify-content: flex-end;
28 | }
29 |
30 | .github-star {
31 | margin-top: -4px;
32 | }
33 |
34 | .menu a {
35 | padding-left: 20px;
36 | display: flex;
37 | align-items: center;
38 | }
39 |
40 | .menu button {
41 | padding-left: 20px;
42 | display: flex;
43 | align-items: center;
44 | }
45 |
46 |
47 | nav {
48 | box-shadow: 0 4px 12px 0 var(--shadow) !important;
49 | }
50 |
51 | .navbar img {
52 | padding-right: 10px;
53 | height: 40px;
54 | }
55 |
56 | .navbar {
57 | position: relative;
58 | display: -ms-flexbox;
59 | display: flex;
60 | -ms-flex-wrap: wrap;
61 | flex-wrap: wrap;
62 | -ms-flex-align: center;
63 | align-items: center;
64 | -ms-flex-pack: justify;
65 | justify-content: space-between;
66 | padding: 0.5rem 1rem;
67 | }
68 |
69 | .navbar>.container,
70 | .navbar>.container-fluid {
71 | display: -ms-flexbox;
72 | display: flex;
73 | -ms-flex-wrap: wrap;
74 | flex-wrap: wrap;
75 | -ms-flex-align: center;
76 | align-items: center;
77 | -ms-flex-pack: justify;
78 | justify-content: space-between;
79 | }
80 |
81 | .navbar-brand {
82 | display: inline-block;
83 | padding-top: 0.3125rem;
84 | padding-bottom: 0.3125rem;
85 | margin-right: 1rem;
86 | font-size: 1.25rem;
87 | line-height: inherit;
88 | white-space: nowrap;
89 | }
90 |
91 | .navbar-brand:hover,
92 | .navbar-brand:focus {
93 | text-decoration: none;
94 | }
95 |
96 | .navbar-nav {
97 | display: -ms-flexbox;
98 | display: flex;
99 | -ms-flex-direction: column;
100 | flex-direction: column;
101 | padding-left: 0;
102 | margin-bottom: 0;
103 | list-style: none;
104 | }
105 |
106 | .navbar-nav .nav-link {
107 | padding-right: 0;
108 | padding-left: 0;
109 | }
110 |
111 | .navbar-nav .dropdown-menu {
112 | position: static;
113 | float: none;
114 | }
115 |
116 | .navbar-text {
117 | display: inline-block;
118 | padding-top: 0.5rem;
119 | padding-bottom: 0.5rem;
120 | }
121 |
122 | .navbar-collapse {
123 | -ms-flex-preferred-size: 100%;
124 | flex-basis: 100%;
125 | -ms-flex-positive: 1;
126 | flex-grow: 1;
127 | -ms-flex-align: center;
128 | align-items: center;
129 | }
130 |
131 | .navbar-toggler {
132 | padding: 0.25rem 0.75rem;
133 | font-size: 1.25rem;
134 | line-height: 1;
135 | background-color: transparent;
136 | border: 1px solid transparent;
137 | border-radius: 0.25rem;
138 | }
139 |
140 | .navbar-toggler:hover,
141 | .navbar-toggler:focus {
142 | text-decoration: none;
143 | }
144 |
145 | .navbar-toggler-icon {
146 | display: inline-block;
147 | width: 1.5em;
148 | height: 1.5em;
149 | vertical-align: middle;
150 | content: '';
151 | background: no-repeat center center;
152 | background-size: 100% 100%;
153 | }
154 |
155 | @media (max-width: 575.98px) {
156 |
157 | .navbar-expand-sm>.container,
158 | .navbar-expand-sm>.container-fluid {
159 | padding-right: 0;
160 | padding-left: 0;
161 | }
162 | }
163 |
164 | @media (min-width: 576px) {
165 | .navbar-expand-sm {
166 | -ms-flex-flow: row nowrap;
167 | flex-flow: row nowrap;
168 | -ms-flex-pack: start;
169 | justify-content: flex-start;
170 | }
171 |
172 | .navbar-expand-sm .navbar-nav {
173 | -ms-flex-direction: row;
174 | flex-direction: row;
175 | }
176 |
177 | .navbar-expand-sm .navbar-nav .dropdown-menu {
178 | position: absolute;
179 | }
180 |
181 | .navbar-expand-sm .navbar-nav .nav-link {
182 | padding-right: 0.5rem;
183 | padding-left: 0.5rem;
184 | }
185 |
186 | .navbar-expand-sm>.container,
187 | .navbar-expand-sm>.container-fluid {
188 | -ms-flex-wrap: nowrap;
189 | flex-wrap: nowrap;
190 | }
191 |
192 | .navbar-expand-sm .navbar-collapse {
193 | display: -ms-flexbox !important;
194 | display: flex !important;
195 | -ms-flex-preferred-size: auto;
196 | flex-basis: auto;
197 | }
198 |
199 | .navbar-expand-sm .navbar-toggler {
200 | display: none;
201 | }
202 | }
203 |
204 | @media (max-width: 767.98px) {
205 |
206 | .navbar-expand-md>.container,
207 | .navbar-expand-md>.container-fluid {
208 | padding-right: 0;
209 | padding-left: 0;
210 | }
211 | }
212 |
213 | @media (min-width: 768px) {
214 | .navbar-expand-md {
215 | -ms-flex-flow: row nowrap;
216 | flex-flow: row nowrap;
217 | -ms-flex-pack: start;
218 | justify-content: flex-start;
219 | }
220 |
221 | .navbar-expand-md .navbar-nav {
222 | -ms-flex-direction: row;
223 | flex-direction: row;
224 | }
225 |
226 | .navbar-expand-md .navbar-nav .dropdown-menu {
227 | position: absolute;
228 | }
229 |
230 | .navbar-expand-md .navbar-nav .nav-link {
231 | padding-right: 0.5rem;
232 | padding-left: 0.5rem;
233 | }
234 |
235 | .navbar-expand-md>.container,
236 | .navbar-expand-md>.container-fluid {
237 | -ms-flex-wrap: nowrap;
238 | flex-wrap: nowrap;
239 | }
240 |
241 | .navbar-expand-md .navbar-collapse {
242 | display: -ms-flexbox !important;
243 | display: flex !important;
244 | -ms-flex-preferred-size: auto;
245 | flex-basis: auto;
246 | }
247 |
248 | .navbar-expand-md .navbar-toggler {
249 | display: none;
250 | }
251 | }
252 |
253 | @media (max-width: 991.98px) {
254 |
255 | .navbar-expand-lg>.container,
256 | .navbar-expand-lg>.container-fluid {
257 | padding-right: 0;
258 | padding-left: 0;
259 | }
260 | }
261 |
262 | @media (min-width: 992px) {
263 | .navbar-expand-lg {
264 | -ms-flex-flow: row nowrap;
265 | flex-flow: row nowrap;
266 | -ms-flex-pack: start;
267 | justify-content: flex-start;
268 | }
269 |
270 | .navbar-expand-lg .navbar-nav {
271 | -ms-flex-direction: row;
272 | flex-direction: row;
273 | }
274 |
275 | .navbar-expand-lg .navbar-nav .dropdown-menu {
276 | position: absolute;
277 | }
278 |
279 | .navbar-expand-lg .navbar-nav .nav-link {
280 | padding-right: 0.5rem;
281 | padding-left: 0.5rem;
282 | }
283 |
284 | .navbar-expand-lg>.container,
285 | .navbar-expand-lg>.container-fluid {
286 | -ms-flex-wrap: nowrap;
287 | flex-wrap: nowrap;
288 | }
289 |
290 | .navbar-expand-lg .navbar-collapse {
291 | display: -ms-flexbox !important;
292 | display: flex !important;
293 | -ms-flex-preferred-size: auto;
294 | flex-basis: auto;
295 | }
296 |
297 | .navbar-expand-lg .navbar-toggler {
298 | display: none;
299 | }
300 | }
301 |
302 | @media (max-width: 1199.98px) {
303 |
304 | .navbar-expand-xl>.container,
305 | .navbar-expand-xl>.container-fluid {
306 | padding-right: 0;
307 | padding-left: 0;
308 | }
309 | }
310 |
311 | @media (min-width: 1200px) {
312 | .navbar-expand-xl {
313 | -ms-flex-flow: row nowrap;
314 | flex-flow: row nowrap;
315 | -ms-flex-pack: start;
316 | justify-content: flex-start;
317 | }
318 |
319 | .navbar-expand-xl .navbar-nav {
320 | -ms-flex-direction: row;
321 | flex-direction: row;
322 | }
323 |
324 | .navbar-expand-xl .navbar-nav .dropdown-menu {
325 | position: absolute;
326 | }
327 |
328 | .navbar-expand-xl .navbar-nav .nav-link {
329 | padding-right: 0.5rem;
330 | padding-left: 0.5rem;
331 | }
332 |
333 | .navbar-expand-xl>.container,
334 | .navbar-expand-xl>.container-fluid {
335 | -ms-flex-wrap: nowrap;
336 | flex-wrap: nowrap;
337 | }
338 |
339 | .navbar-expand-xl .navbar-collapse {
340 | display: -ms-flexbox !important;
341 | display: flex !important;
342 | -ms-flex-preferred-size: auto;
343 | flex-basis: auto;
344 | }
345 |
346 | .navbar-expand-xl .navbar-toggler {
347 | display: none;
348 | }
349 | }
350 |
351 | .navbar-expand {
352 | -ms-flex-flow: row nowrap;
353 | flex-flow: row nowrap;
354 | -ms-flex-pack: start;
355 | justify-content: flex-start;
356 | }
357 |
358 | .navbar-expand>.container,
359 | .navbar-expand>.container-fluid {
360 | padding-right: 0;
361 | padding-left: 0;
362 | }
363 |
364 | .navbar-expand .navbar-nav {
365 | -ms-flex-direction: row;
366 | flex-direction: row;
367 | }
368 |
369 | .navbar-expand .navbar-nav .dropdown-menu {
370 | position: absolute;
371 | }
372 |
373 | .navbar-expand .navbar-nav .nav-link {
374 | padding-right: 0.5rem;
375 | padding-left: 0.5rem;
376 | }
377 |
378 | .navbar-expand>.container,
379 | .navbar-expand>.container-fluid {
380 | -ms-flex-wrap: nowrap;
381 | flex-wrap: nowrap;
382 | }
383 |
384 | .navbar-expand .navbar-collapse {
385 | display: -ms-flexbox !important;
386 | display: flex !important;
387 | -ms-flex-preferred-size: auto;
388 | flex-basis: auto;
389 | }
390 |
391 | .navbar-expand .navbar-toggler {
392 | display: none;
393 | }
394 |
395 | .navbar-light .navbar-brand {
396 | color: var(--text);
397 | }
398 |
399 | .navbar-light .navbar-brand:hover,
400 | .navbar-light .navbar-brand:focus {
401 | color: var(--text);
402 | }
403 |
404 | .navbar-light .navbar-nav .nav-link {
405 | color: var(--text);
406 | }
407 |
408 | .navbar-light .navbar-nav .nav-link:hover,
409 | .navbar-light .navbar-nav .nav-link:focus {
410 | color: var(--text);
411 | }
412 |
413 | .navbar-light .navbar-nav .nav-link.disabled {
414 | color: var(--text);
415 | }
416 |
417 | .navbar-light .navbar-nav .show>.nav-link,
418 | .navbar-light .navbar-nav .active>.nav-link,
419 | .navbar-light .navbar-nav .nav-link.show,
420 | .navbar-light .navbar-nav .nav-link.active {
421 | color: var(--text);
422 | }
423 |
424 | .navbar-light .navbar-toggler {
425 | color: rgba(0, 0, 0, 0.5);
426 | border-color: rgba(0, 0, 0, 0.1);
427 | }
428 |
429 | .navbar-light .navbar-toggler-icon {
430 | background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
431 | }
432 |
433 | .navbar-light .navbar-text {
434 | color: rgba(0, 0, 0, 0.5);
435 | }
436 |
437 | .navbar-light .navbar-text a {
438 | color: rgba(0, 0, 0, 0.9);
439 | }
440 |
441 | .navbar-light .navbar-text a:hover,
442 | .navbar-light .navbar-text a:focus {
443 | color: rgba(0, 0, 0, 0.9);
444 | }
445 |
446 | .navbar-dark .navbar-brand {
447 | color: #fff;
448 | }
449 |
450 | .navbar-dark .navbar-brand:hover,
451 | .navbar-dark .navbar-brand:focus {
452 | color: #fff;
453 | }
454 |
455 | .navbar-dark .navbar-nav .nav-link {
456 | color: rgba(255, 255, 255, 0.5);
457 | }
458 |
459 | .navbar-dark .navbar-nav .nav-link:hover,
460 | .navbar-dark .navbar-nav .nav-link:focus {
461 | color: rgba(255, 255, 255, 0.75);
462 | }
463 |
464 | .navbar-dark .navbar-nav .nav-link.disabled {
465 | color: rgba(255, 255, 255, 0.25);
466 | }
467 |
468 | .navbar-dark .navbar-nav .show>.nav-link,
469 | .navbar-dark .navbar-nav .active>.nav-link,
470 | .navbar-dark .navbar-nav .nav-link.show,
471 | .navbar-dark .navbar-nav .nav-link.active {
472 | color: #fff;
473 | }
474 |
475 | .navbar-dark .navbar-toggler {
476 | color: rgba(255, 255, 255, 0.5);
477 | border-color: rgba(255, 255, 255, 0.1);
478 | }
479 |
480 | .navbar-dark .navbar-toggler-icon {
481 | background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
482 | }
483 |
484 | .navbar-dark .navbar-text {
485 | color: rgba(255, 255, 255, 0.5);
486 | }
487 |
488 | .navbar-dark .navbar-text a {
489 | color: #fff;
490 | }
491 |
492 | .navbar-dark .navbar-text a:hover,
493 | .navbar-dark .navbar-text a:focus {
494 | color: #fff;
495 | }
496 |
497 |
498 | .emoji-add {
499 | width: 550px;
500 | padding: 0px !important;
501 |
502 | }
503 |
504 | mat-dialog-container {
505 | padding: 0px !important;
506 |
507 | }
508 |
509 | .warning {
510 | padding-top: 3px;
511 | color: #ff9301;
512 | }
513 |
514 | .file-icon {
515 | margin-top: 4px
516 | }
517 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 6.1.0 (2019-08-15)
2 |
3 | * [DELETE]: vscode settings removed ([becc9b4](https://github.com/maximegris/angular-electron/commit/becc9b4))
4 | * [UPDATE]: Typescript version fixes ([a284c23](https://github.com/maximegris/angular-electron/commit/a284c23))
5 | * Angular src restructured as modular as per angular official guidelines, .travis.yml support added fo ([5983703](https://github.com/maximegris/angular-electron/commit/5983703))
6 | * Corejs path updates ([88056d6](https://github.com/maximegris/angular-electron/commit/88056d6))
7 | * Electron 6.0.0 ([1aef350](https://github.com/maximegris/angular-electron/commit/1aef350))
8 | * fix/ Version Electron in README ([acf0d4f](https://github.com/maximegris/angular-electron/commit/acf0d4f))
9 | * misc/ upgrade Angular 8.2 / Spectron 7 ([6e2211f](https://github.com/maximegris/angular-electron/commit/6e2211f))
10 | * Update dependencies ([bd51f28](https://github.com/maximegris/angular-electron/commit/bd51f28))
11 |
12 |
13 |
14 | ## 6.0.1 (2019-05-31)
15 |
16 | * [Bumped Version] 6.0.1 ([4f9cef3](https://github.com/maximegris/angular-electron/commit/4f9cef3))
17 | * ref/ strict version build-angular & zonejs ([5a24b56](https://github.com/maximegris/angular-electron/commit/5a24b56))
18 | * ref/ strict version codelyzer ([6ede0c8](https://github.com/maximegris/angular-electron/commit/6ede0c8))
19 |
20 |
21 |
22 | ## 6.0.0 (2019-05-31)
23 |
24 | * [Bumped Version] 6.0.0 ([fb719ab](https://github.com/maximegris/angular-electron/commit/fb719ab))
25 | * Add CI for macOS. ([3ba02e3](https://github.com/maximegris/angular-electron/commit/3ba02e3))
26 | * bump angular version ([7a564a0](https://github.com/maximegris/angular-electron/commit/7a564a0))
27 | * bump tslint & codelyzer versions, update tslint rules & alphabetize ([8425cdc](https://github.com/maximegris/angular-electron/commit/8425cdc))
28 | * Fix Travis CI link ([10aaa4c](https://github.com/maximegris/angular-electron/commit/10aaa4c))
29 | * Not gitignore src/karma.conf.js ([7599320](https://github.com/maximegris/angular-electron/commit/7599320))
30 | * ref/ upgrade Angular 8 and Electron 5 ([92334cf](https://github.com/maximegris/angular-electron/commit/92334cf))
31 | * Remove Node.js v12. ([ccbf6cd](https://github.com/maximegris/angular-electron/commit/ccbf6cd))
32 | * Run CI for Node.js version 10 ([2538965](https://github.com/maximegris/angular-electron/commit/2538965))
33 | * update versions and prepare for electron 5 ([07a5786](https://github.com/maximegris/angular-electron/commit/07a5786))
34 | * Use service xvfb. ([4db31e3](https://github.com/maximegris/angular-electron/commit/4db31e3))
35 | * version bump ([bb1d6bb](https://github.com/maximegris/angular-electron/commit/bb1d6bb))
36 | * feat(CI): update Ubuntu and Node versions in Travis ([e0ff557](https://github.com/maximegris/angular-electron/commit/e0ff557))
37 | * fix: type conflicts ([a2971bf](https://github.com/maximegris/angular-electron/commit/a2971bf))
38 | * fix(e2e): add mocha types ([20e1e89](https://github.com/maximegris/angular-electron/commit/20e1e89))
39 | * fix(e2e): without devTools ([2581983](https://github.com/maximegris/angular-electron/commit/2581983)), closes [/github.com/electron/spectron/issues/174#issuecomment-319242097](https://github.com//github.com/electron/spectron/issues/174/issues/issuecomment-319242097)
40 | * chore: Spectron for e2e tests ([901438a](https://github.com/maximegris/angular-electron/commit/901438a))
41 |
42 |
43 |
44 | ## 5.1.0 (2018-11-30)
45 |
46 | * [Bumped Version] 5.1.0 ([b790e12](https://github.com/maximegris/angular-electron/commit/b790e12))
47 | * fix/ typo Angular 7 ([fde371f](https://github.com/maximegris/angular-electron/commit/fde371f))
48 | * fix/ typo README ([723233c](https://github.com/maximegris/angular-electron/commit/723233c))
49 | * fix/ typo script npm electron:windows ([45bab44](https://github.com/maximegris/angular-electron/commit/45bab44))
50 | * ref/ remve npx - fix vulnerabilities ([41aeb57](https://github.com/maximegris/angular-electron/commit/41aeb57))
51 | * update README ([f146d5d](https://github.com/maximegris/angular-electron/commit/f146d5d))
52 |
53 |
54 |
55 | ## 5.0.0 (2018-11-11)
56 |
57 | * Fix typos in README file ([0440ee9](https://github.com/maximegris/angular-electron/commit/0440ee9))
58 | * ref/ Generate changelog ([a89b3ce](https://github.com/maximegris/angular-electron/commit/a89b3ce))
59 | * ref/ Upgrade to Angular 7 ([315a79b](https://github.com/maximegris/angular-electron/commit/315a79b))
60 | * Update electron-builder.json files rule ([82c7bcf](https://github.com/maximegris/angular-electron/commit/82c7bcf))
61 | * Update Version Electron 2 to 3 #hacktoberfest ([f083328](https://github.com/maximegris/angular-electron/commit/f083328))
62 |
63 |
64 |
65 | ## 4.2.2 (2018-08-22)
66 |
67 | * fix/ build serve & electron with single tsc command ([9106c8f](https://github.com/maximegris/angular-electron/commit/9106c8f))
68 | * fix/ typo README ([a9448aa](https://github.com/maximegris/angular-electron/commit/a9448aa))
69 |
70 |
71 |
72 | ## 4.2.1 (2018-08-22)
73 |
74 | * fix/ jslib in main process error ([ef33f5e](https://github.com/maximegris/angular-electron/commit/ef33f5e))
75 |
76 |
77 |
78 | ## 4.2.0 (2018-08-19)
79 |
80 | * [Bumped Version] V4.2.0 ([0da3856](https://github.com/maximegris/angular-electron/commit/0da3856))
81 | * fix/ electron builder output directories #200 ([f4535e5](https://github.com/maximegris/angular-electron/commit/f4535e5)), closes [#200](https://github.com/maximegris/angular-electron/issues/200)
82 | * Make sure tsconfig be used. ([961c8b1](https://github.com/maximegris/angular-electron/commit/961c8b1))
83 | * ref/ remove some directories of tsconfig.app.json ([1adad4a](https://github.com/maximegris/angular-electron/commit/1adad4a))
84 | * Upgrade Angular (6.1.2) deps ([d8818c1](https://github.com/maximegris/angular-electron/commit/d8818c1))
85 |
86 |
87 |
88 | ## 4.1.0 (2018-06-27)
89 |
90 | * Allow Angular Using Electron Modules ([ec705ee](https://github.com/maximegris/angular-electron/commit/ec705ee))
91 | * fix/ version angular (revert 6.0.6 -> 6.0.5) ([63a41b8](https://github.com/maximegris/angular-electron/commit/63a41b8))
92 | * fix/ version ts-node ([0d8341a](https://github.com/maximegris/angular-electron/commit/0d8341a))
93 | * ref/ postinstall web & electron ([50657d0](https://github.com/maximegris/angular-electron/commit/50657d0))
94 | * update README ([1d48e32](https://github.com/maximegris/angular-electron/commit/1d48e32))
95 | * feat(zone): add zone-patch-electron to patch Electron native APIs in polyfills ([01842e2](https://github.com/maximegris/angular-electron/commit/01842e2))
96 |
97 |
98 |
99 | ## 4.0.0 (2018-05-25)
100 |
101 | * misc/ remove unused packages ([a7e33b6](https://github.com/maximegris/angular-electron/commit/a7e33b6))
102 | * misc/ update Changelog ([b758122](https://github.com/maximegris/angular-electron/commit/b758122))
103 | * ref/ upgrade angular to 6.0.3 ([e7fac6e](https://github.com/maximegris/angular-electron/commit/e7fac6e))
104 |
105 |
106 |
107 | ## 3.4.1 (2018-05-25)
108 |
109 | * misc/ update changelog ([70b359f](https://github.com/maximegris/angular-electron/commit/70b359f))
110 | * version 3.4.1 ([308ea9c](https://github.com/maximegris/angular-electron/commit/308ea9c))
111 |
112 |
113 |
114 | ## 3.4.0 (2018-05-25)
115 |
116 | * misc/ update changelog ([7d5eeb3](https://github.com/maximegris/angular-electron/commit/7d5eeb3))
117 | * Modify electron builder configuration to remove source code and tests ([0cf6899](https://github.com/maximegris/angular-electron/commit/0cf6899))
118 | * ref/ remove contributors ([6dc97a1](https://github.com/maximegris/angular-electron/commit/6dc97a1))
119 | * The file is unused ([05c9e39](https://github.com/maximegris/angular-electron/commit/05c9e39))
120 | * Translation issue ([35354b1](https://github.com/maximegris/angular-electron/commit/35354b1))
121 | * version 3.4.0 ([06d6b0f](https://github.com/maximegris/angular-electron/commit/06d6b0f))
122 | * refactor: update electron, electron-builder to latest (2.0.2, 20.14.7) ([f19e6ee](https://github.com/maximegris/angular-electron/commit/f19e6ee))
123 | * refactor: upgrade to NodeJS 8, Angular 6, CLI 6, Electron 2.0, RxJS 6.1 ([e37efdb](https://github.com/maximegris/angular-electron/commit/e37efdb))
124 | * refactor(hooks): replace hooks to ng-cli fileReplacements logic ([c940037](https://github.com/maximegris/angular-electron/commit/c940037))
125 | * fix(test): create polyfills-test.ts for karma test & setup Travis CI ([7fbc68c](https://github.com/maximegris/angular-electron/commit/7fbc68c))
126 | * fix(travis): set progress to false (speed up npm) ([be48531](https://github.com/maximegris/angular-electron/commit/be48531))
127 |
128 |
129 |
130 | ## 3.3.0 (2018-04-15)
131 |
132 | * add Changelog file ([71083f1](https://github.com/maximegris/angular-electron/commit/71083f1))
133 | * fix/ typo README.md (production variables) ([a8c2b63](https://github.com/maximegris/angular-electron/commit/a8c2b63))
134 | * version 3.3.0 ([a88bda6](https://github.com/maximegris/angular-electron/commit/a88bda6))
135 | * version 3.3.0 changelog ([ddfbbf9](https://github.com/maximegris/angular-electron/commit/ddfbbf9))
136 |
137 |
138 |
139 | ## 3.2.0 (2018-04-15)
140 |
141 | * fix e2e tests based on PR #161 and terminate the npm process after test execution ([fccf348](https://github.com/maximegris/angular-electron/commit/fccf348)), closes [#161](https://github.com/maximegris/angular-electron/issues/161)
142 | * fix/ app e2e spec ([8046b2a](https://github.com/maximegris/angular-electron/commit/8046b2a))
143 | * Including electron to eliminate Electron not found err sg ([d78203f](https://github.com/maximegris/angular-electron/commit/d78203f))
144 | * provide webFrame access ([6bd044e](https://github.com/maximegris/angular-electron/commit/6bd044e))
145 | * ref/ add node/electron module import as exemple : fs and remote ([e3ad12d](https://github.com/maximegris/angular-electron/commit/e3ad12d))
146 | * remove copyfiles ([9af5138](https://github.com/maximegris/angular-electron/commit/9af5138))
147 | * update dependencies ([89963ab](https://github.com/maximegris/angular-electron/commit/89963ab))
148 | * version 3.2.0 ([8dc69fa](https://github.com/maximegris/angular-electron/commit/8dc69fa))
149 |
150 |
151 |
152 | ## 3.1.0 (2018-03-15)
153 |
154 | * Added option -o to script npm run ng:serve so that it really open the browser ([72aff8d](https://github.com/maximegris/angular-electron/commit/72aff8d))
155 | * Fix to change environment ([448d68b](https://github.com/maximegris/angular-electron/commit/448d68b))
156 | * version 3.1.0 ([f7c71e7](https://github.com/maximegris/angular-electron/commit/f7c71e7))
157 |
158 |
159 |
160 | ## 3.0.1 (2018-03-07)
161 |
162 | * fix/ icon app ([22699ef](https://github.com/maximegris/angular-electron/commit/22699ef))
163 | * version 3.0.1 ([5258ff1](https://github.com/maximegris/angular-electron/commit/5258ff1))
164 |
165 |
166 |
167 | ## 3.0.0 (2018-02-25)
168 |
169 | * fix/ TranslateModule test ([7863aa9](https://github.com/maximegris/angular-electron/commit/7863aa9))
170 | * Ng not ejected anymore ([67ab31c](https://github.com/maximegris/angular-electron/commit/67ab31c))
171 | * pin all dependency versions ([0558d6a](https://github.com/maximegris/angular-electron/commit/0558d6a))
172 | * update dependencies and fix unit tests ([4d3ca6e](https://github.com/maximegris/angular-electron/commit/4d3ca6e))
173 |
174 |
175 |
176 | ## 2.7.1 (2018-02-15)
177 |
178 | * ref/ dernière version cli ([3df8158](https://github.com/maximegris/angular-electron/commit/3df8158))
179 | * version 2.7.1 ([1ae6f7a](https://github.com/maximegris/angular-electron/commit/1ae6f7a))
180 |
181 |
182 |
183 | ## 2.7.0 (2018-02-15)
184 |
185 | * Correction of a word. ([d6655c7](https://github.com/maximegris/angular-electron/commit/d6655c7))
186 | * feat/ add webview directive ([e1b5600](https://github.com/maximegris/angular-electron/commit/e1b5600))
187 | * migrate Angular to 5.2.0 ([b8cf343](https://github.com/maximegris/angular-electron/commit/b8cf343))
188 | * ref/ Remove sponsor ([2a28239](https://github.com/maximegris/angular-electron/commit/2a28239))
189 | * ref/ update angular & dep ([e3b1fab](https://github.com/maximegris/angular-electron/commit/e3b1fab))
190 | * ref/ upgrade electron (security issue) ([f6a0c4e](https://github.com/maximegris/angular-electron/commit/f6a0c4e))
191 | * version bump + logo resize ([3545d16](https://github.com/maximegris/angular-electron/commit/3545d16))
192 | * fix: fixes maximegris/angular-electron#118 ([6d21e69](https://github.com/maximegris/angular-electron/commit/6d21e69)), closes [maximegris/angular-electron#118](https://github.com/maximegris/angular-electron/issues/118)
193 | * fix: fixes maximegris/angular-electron#98 ([136344b](https://github.com/maximegris/angular-electron/commit/136344b)), closes [maximegris/angular-electron#98](https://github.com/maximegris/angular-electron/issues/98)
194 |
195 |
196 |
197 | ## 2.4.1 (2017-12-14)
198 |
199 | * fix/ Manage icons for linux binary generation ([ccd0601](https://github.com/maximegris/angular-electron/commit/ccd0601))
200 | * version 2.4.1 ([5fcfca0](https://github.com/maximegris/angular-electron/commit/5fcfca0))
201 |
202 |
203 |
204 | ## 2.4.0 (2017-12-08)
205 |
206 | * version 2.4.0 ([0437b33](https://github.com/maximegris/angular-electron/commit/0437b33))
207 |
208 |
209 |
210 | ## 2.3.0 (2017-12-04)
211 |
212 | * add ngx translate ([facda37](https://github.com/maximegris/angular-electron/commit/facda37))
213 | * Use HttpClientModule ([5704e2e](https://github.com/maximegris/angular-electron/commit/5704e2e))
214 |
215 |
216 |
217 | ## 2.2.0 (2017-11-28)
218 |
219 | * Brought back scripts defined in webpack.config.js ([441da3d](https://github.com/maximegris/angular-electron/commit/441da3d))
220 | * migrate to Angular 5.0.3 ([f4bc5b2](https://github.com/maximegris/angular-electron/commit/f4bc5b2))
221 | * Update LICENSE badge ([fa783aa](https://github.com/maximegris/angular-electron/commit/fa783aa))
222 | * Update to electron-builder ([0e94b52](https://github.com/maximegris/angular-electron/commit/0e94b52))
223 |
224 |
225 |
226 | ## 2.1.1 (2017-11-19)
227 |
228 | * Move codesponsor ([064be4c](https://github.com/maximegris/angular-electron/commit/064be4c))
229 |
230 |
231 |
232 | ## 2.1.0 (2017-11-19)
233 |
234 | * Add codesponsor ([87e695d](https://github.com/maximegris/angular-electron/commit/87e695d))
235 | * Add script for winportable ([2be2dae](https://github.com/maximegris/angular-electron/commit/2be2dae))
236 | * Add support for building a Windows self-contained executable ([7cfa790](https://github.com/maximegris/angular-electron/commit/7cfa790))
237 | * fix/ electron-packager need favicon >= 256x256 on Windows ([d2c253f](https://github.com/maximegris/angular-electron/commit/d2c253f))
238 | * fix/ refact webpack config (inspired by ng eject Angular 5) ([d1c30ac](https://github.com/maximegris/angular-electron/commit/d1c30ac))
239 | * fix/ replace aotPlugin in no prod mode ([a0caf1e](https://github.com/maximegris/angular-electron/commit/a0caf1e))
240 | * fix/ Replace AotPlugin to AngularCompilerPlugin ([bef106e](https://github.com/maximegris/angular-electron/commit/bef106e))
241 | * fix/ Update README Angular 5 ([93c6949](https://github.com/maximegris/angular-electron/commit/93c6949))
242 | * fix/ webpack template path ([518b66b](https://github.com/maximegris/angular-electron/commit/518b66b))
243 | * Mgrate to Angular 5.0.2 ([bd7bed6](https://github.com/maximegris/angular-electron/commit/bd7bed6))
244 | * Update package.json ([b16cf73](https://github.com/maximegris/angular-electron/commit/b16cf73))
245 | * Version 2.1.0 ([fccef2f](https://github.com/maximegris/angular-electron/commit/fccef2f))
246 |
247 |
248 |
249 | ## 2.0.0 (2017-11-13)
250 |
251 | * Add buffer to externals ([7e797f0](https://github.com/maximegris/angular-electron/commit/7e797f0))
252 | * Edit a typo on README ([956a2bc](https://github.com/maximegris/angular-electron/commit/956a2bc))
253 | * Fix #55 removed bootstraps.css which for example purpose before. ([41445eb](https://github.com/maximegris/angular-electron/commit/41445eb)), closes [#55](https://github.com/maximegris/angular-electron/issues/55)
254 | * License MIT ([73494b7](https://github.com/maximegris/angular-electron/commit/73494b7))
255 | * Migrate to Angular 5 ([3a3ffe1](https://github.com/maximegris/angular-electron/commit/3a3ffe1))
256 |
257 |
258 |
259 | ## 1.9.0 (2017-09-22)
260 |
261 | * feat/ launch electron & webpack in // (npm run start) ([8c37cc4](https://github.com/maximegris/angular-electron/commit/8c37cc4))
262 | * ref/ Exclude node_modules (tslint) ([412a0a5](https://github.com/maximegris/angular-electron/commit/412a0a5))
263 |
264 |
265 |
266 | ## 1.8.1 (2017-09-22)
267 |
268 | * Fix #55 , and also added functionality for scripts global building ([012a894](https://github.com/maximegris/angular-electron/commit/012a894)), closes [#55](https://github.com/maximegris/angular-electron/issues/55)
269 | * ref/ add package-lock in gitignore ([4edd98d](https://github.com/maximegris/angular-electron/commit/4edd98d))
270 | * remove package-lock ([8e98627](https://github.com/maximegris/angular-electron/commit/8e98627))
271 | * upgrade angular version 4.4.3 ([10d0f87](https://github.com/maximegris/angular-electron/commit/10d0f87))
272 | * version 1.8.1 ([70879d1](https://github.com/maximegris/angular-electron/commit/70879d1))
273 |
274 |
275 |
276 | ## 1.8.0 (2017-09-09)
277 |
278 | * upgrade lib version ([2ac2aa0](https://github.com/maximegris/angular-electron/commit/2ac2aa0))
279 |
280 |
281 |
282 | ## 1.7.0 (2017-08-18)
283 |
284 | * ref/ Update Angular (4.3.5) / Electron (1.7.2) / Electron Packager (8.7.2) / Typescript (2.5.0) ([f97cd81](https://github.com/maximegris/angular-electron/commit/f97cd81))
285 |
286 |
287 |
288 | ## 1.6.1 (2017-07-27)
289 |
290 | * fix/ angular-cli error in prod compilation with aot ([c26a5ae](https://github.com/maximegris/angular-electron/commit/c26a5ae))
291 | * version 1.6.1 ([899babd](https://github.com/maximegris/angular-electron/commit/899babd))
292 |
293 |
294 |
295 | ## 1.6.0 (2017-07-16)
296 |
297 | * ajout package-lock npm v5 ([09c0840](https://github.com/maximegris/angular-electron/commit/09c0840))
298 | * Change background img ([7e58717](https://github.com/maximegris/angular-electron/commit/7e58717))
299 | * Fix npm run build:prod ([c23bade](https://github.com/maximegris/angular-electron/commit/c23bade))
300 | * fix/ Bindings not updating automatically #44 ([2a90191](https://github.com/maximegris/angular-electron/commit/2a90191)), closes [#44](https://github.com/maximegris/angular-electron/issues/44)
301 | * fix/ e2e test with jasmine2 ([9c51f32](https://github.com/maximegris/angular-electron/commit/9c51f32))
302 | * fix/ typescript issues ([bb0a6ab](https://github.com/maximegris/angular-electron/commit/bb0a6ab))
303 | * increment version deps ([bde452c](https://github.com/maximegris/angular-electron/commit/bde452c))
304 | * Revert last pull request - break production compilation ([ccc9064](https://github.com/maximegris/angular-electron/commit/ccc9064))
305 | * upgrade angular version to 4.3.0 ([ab16959](https://github.com/maximegris/angular-electron/commit/ab16959))
306 |
307 |
308 |
309 | ## 1.5.0 (2017-06-10)
310 |
311 | * fix/ karma Unit test ([ea13d6d](https://github.com/maximegris/angular-electron/commit/ea13d6d))
312 | * fix/ remove yarn because of error with module dep in prod builds ([8a49a45](https://github.com/maximegris/angular-electron/commit/8a49a45))
313 | * update yarn lock ([18c0e62](https://github.com/maximegris/angular-electron/commit/18c0e62))
314 |
315 |
316 |
317 | ## 1.4.4 (2017-06-08)
318 |
319 | * fix/ Fix npm run lint ([db7972a](https://github.com/maximegris/angular-electron/commit/db7972a))
320 | * ref/ electron ./dist more generic ([7e71add](https://github.com/maximegris/angular-electron/commit/7e71add))
321 | * Replace const icon to let icon ([dadf65f](https://github.com/maximegris/angular-electron/commit/dadf65f))
322 |
323 |
324 |
325 | ## 1.4.3 (2017-06-06)
326 |
327 | * fix/ favicon path during packaging ([aa2b012](https://github.com/maximegris/angular-electron/commit/aa2b012))
328 | * remove build node 8 till node-sass failed ([34f201d](https://github.com/maximegris/angular-electron/commit/34f201d))
329 | * v1.4.3 ([4961fb0](https://github.com/maximegris/angular-electron/commit/4961fb0))
330 |
331 |
332 |
333 | ## 1.4.2 (2017-05-31)
334 |
335 | * Change dep versions ([62d08d3](https://github.com/maximegris/angular-electron/commit/62d08d3))
336 | * install npm dep when building ([56948d0](https://github.com/maximegris/angular-electron/commit/56948d0))
337 | * Minor update ([5f282b7](https://github.com/maximegris/angular-electron/commit/5f282b7))
338 | * No hot reload in browser ([7892f0d](https://github.com/maximegris/angular-electron/commit/7892f0d))
339 | * update Electron v1.6.10 ([f2f2080](https://github.com/maximegris/angular-electron/commit/f2f2080))
340 | * upgrade ng/electron dependencies ([78b0f27](https://github.com/maximegris/angular-electron/commit/78b0f27))
341 | * chore(package): bump dependencies ([b62c7b6](https://github.com/maximegris/angular-electron/commit/b62c7b6))
342 |
343 |
344 |
345 | ## 1.4.0 (2017-05-23)
346 |
347 | * 1.4.0 ([23213c4](https://github.com/maximegris/angular-electron/commit/23213c4))
348 | * Change style home page ([93dcc52](https://github.com/maximegris/angular-electron/commit/93dcc52))
349 | * Fixed compiler warnings ([fca6b15](https://github.com/maximegris/angular-electron/commit/fca6b15))
350 | * ref/ electron main from js to ts ([835d32b](https://github.com/maximegris/angular-electron/commit/835d32b))
351 | * Remove caret & tilde ([dd98155](https://github.com/maximegris/angular-electron/commit/dd98155))
352 |
353 |
354 |
355 | ## 1.3.5 (2017-05-18)
356 |
357 | * Add new tags ([cd07a86](https://github.com/maximegris/angular-electron/commit/cd07a86))
358 | * v 1.3.5 ([d528a71](https://github.com/maximegris/angular-electron/commit/d528a71))
359 |
360 |
361 |
362 | ## 1.3.4 (2017-05-12)
363 |
364 | * feat/ add nodejs native lib in webpack config ([27d9bc6](https://github.com/maximegris/angular-electron/commit/27d9bc6))
365 | * Fix issue #15 ([d77cbf1](https://github.com/maximegris/angular-electron/commit/d77cbf1)), closes [#15](https://github.com/maximegris/angular-electron/issues/15)
366 | * Ref/ Electron packager in external file ([17b04e8](https://github.com/maximegris/angular-electron/commit/17b04e8))
367 | * version 1.3.4 ([374af16](https://github.com/maximegris/angular-electron/commit/374af16))
368 |
369 |
370 |
371 | ## 1.3.3 (2017-05-10)
372 |
373 | * Chapters order ([a772b9c](https://github.com/maximegris/angular-electron/commit/a772b9c))
374 | * Chapters order ([06547e5](https://github.com/maximegris/angular-electron/commit/06547e5))
375 | * Delete spec file of electron.service ([083498e](https://github.com/maximegris/angular-electron/commit/083498e))
376 | * Fix issue #15 ([e7cd6e6](https://github.com/maximegris/angular-electron/commit/e7cd6e6)), closes [#15](https://github.com/maximegris/angular-electron/issues/15)
377 | * Move Browser mode chapter ([8818750](https://github.com/maximegris/angular-electron/commit/8818750))
378 | * Version 1.3.3 ([f4db75b](https://github.com/maximegris/angular-electron/commit/f4db75b))
379 |
380 |
381 |
382 | ## 1.3.2 (2017-05-05)
383 |
384 | * Add comments of how conditional import works ([e6c1b3b](https://github.com/maximegris/angular-electron/commit/e6c1b3b))
385 | * Conditional import of Electron/NodeJS libs - The app can be launch in browser mode ([c434f8a](https://github.com/maximegris/angular-electron/commit/c434f8a))
386 | * Fix indentation ([6a9836a](https://github.com/maximegris/angular-electron/commit/6a9836a))
387 | * Fix prepree2e script ([b2af4fd](https://github.com/maximegris/angular-electron/commit/b2af4fd))
388 | * Set e2e tests ([d223974](https://github.com/maximegris/angular-electron/commit/d223974))
389 | * Suround electron browser by try/catch ([88be472](https://github.com/maximegris/angular-electron/commit/88be472))
390 | * Update @types/node ([9d43304](https://github.com/maximegris/angular-electron/commit/9d43304))
391 | * Update readme with e2e info ([01bbf13](https://github.com/maximegris/angular-electron/commit/01bbf13))
392 | * update version ([0849a0a](https://github.com/maximegris/angular-electron/commit/0849a0a))
393 |
394 |
395 |
396 | ## 1.3.1 (2017-05-05)
397 |
398 | * Add routing module ([7334ce8](https://github.com/maximegris/angular-electron/commit/7334ce8))
399 | * Fixed hardcoded path in glob copy, blocking assets after eject ([815d519](https://github.com/maximegris/angular-electron/commit/815d519))
400 | * update comments in dev/prod env files ([7cf6a51](https://github.com/maximegris/angular-electron/commit/7cf6a51))
401 | * Version 1.3.1 ([f18ac77](https://github.com/maximegris/angular-electron/commit/f18ac77))
402 |
403 |
404 |
405 | ## 1.3.0 (2017-05-01)
406 |
407 | * Fix webpack prod/dev env ([8549da1](https://github.com/maximegris/angular-electron/commit/8549da1))
408 |
409 |
410 |
411 | ## 1.2.1 (2017-04-30)
412 |
413 | * allowJs ([4efd188](https://github.com/maximegris/angular-electron/commit/4efd188))
414 | * Example url background in scss ([3705a35](https://github.com/maximegris/angular-electron/commit/3705a35))
415 | * Fix electron build (extract-zip workaround) ([a7ee90e](https://github.com/maximegris/angular-electron/commit/a7ee90e))
416 | * Fix webpack config url in css ([cea4be5](https://github.com/maximegris/angular-electron/commit/cea4be5))
417 | * html loader ([c55558a](https://github.com/maximegris/angular-electron/commit/c55558a))
418 | * update version 1.2.1 ([78e8da7](https://github.com/maximegris/angular-electron/commit/78e8da7))
419 |
420 |
421 |
422 | ## 1.2.0 (2017-04-19)
423 |
424 | * Set one example of css class in app component ([a15775f](https://github.com/maximegris/angular-electron/commit/a15775f))
425 | * Update npm dependencies ([0a93ebe](https://github.com/maximegris/angular-electron/commit/0a93ebe))
426 |
427 |
428 |
429 | ## 1.1.2 (2017-04-18)
430 |
431 | * Fix typo & fix script electron:mac ([bd06859](https://github.com/maximegris/angular-electron/commit/bd06859))
432 | * Set theme jekyll-theme-architect ([644d857](https://github.com/maximegris/angular-electron/commit/644d857))
433 | * update README ([97fa63d](https://github.com/maximegris/angular-electron/commit/97fa63d))
434 | * update README ([23fc0a9](https://github.com/maximegris/angular-electron/commit/23fc0a9))
435 | * update README ([a8dcf6a](https://github.com/maximegris/angular-electron/commit/a8dcf6a))
436 | * v1.1.2 ([e29e467](https://github.com/maximegris/angular-electron/commit/e29e467))
437 |
438 |
439 |
440 | ## 1.1.1 (2017-04-12)
441 |
442 | * Fix webpack.config file path (travisci) ([a172df9](https://github.com/maximegris/angular-electron/commit/a172df9))
443 | * live reload on disk ([7bb2f8b](https://github.com/maximegris/angular-electron/commit/7bb2f8b))
444 | * Remove unused dependency (webpack-dev-server) ([e9150f4](https://github.com/maximegris/angular-electron/commit/e9150f4))
445 |
446 |
447 |
448 | ## 1.1.0 (2017-04-12)
449 |
450 | * add depdencies CI & Licence ([6ceb0f2](https://github.com/maximegris/angular-electron/commit/6ceb0f2))
451 | * Override webpack configuration ([60d6116](https://github.com/maximegris/angular-electron/commit/60d6116))
452 |
453 |
454 |
455 | ## 1.0.3 (2017-04-07)
456 |
457 | * Add TravisCI ([e5640fd](https://github.com/maximegris/angular-electron/commit/e5640fd))
458 | * v1.0.3 ([9866d53](https://github.com/maximegris/angular-electron/commit/9866d53))
459 |
460 |
461 |
462 | ## 1.0.2 (2017-04-07)
463 |
464 | * Add TravisCI ([ef4b80e](https://github.com/maximegris/angular-electron/commit/ef4b80e))
465 | * Fix typo ([f964c3f](https://github.com/maximegris/angular-electron/commit/f964c3f))
466 | * Fix typo ([e42bb5e](https://github.com/maximegris/angular-electron/commit/e42bb5e))
467 | * Update README ([3bb45b3](https://github.com/maximegris/angular-electron/commit/3bb45b3))
468 | * Update README with angular-cli doc ([5a57578](https://github.com/maximegris/angular-electron/commit/5a57578))
469 | * v1.0.2 ([1bd8e0e](https://github.com/maximegris/angular-electron/commit/1bd8e0e))
470 |
471 |
472 |
473 | ## 1.0.1 (2017-04-03)
474 |
475 | * feat/ Add electron-packager scripts ([57891dc](https://github.com/maximegris/angular-electron/commit/57891dc))
476 | * ref/ update README ([7fddc20](https://github.com/maximegris/angular-electron/commit/7fddc20))
477 | * update README ([9a983c1](https://github.com/maximegris/angular-electron/commit/9a983c1))
478 | * v1.0.0 ([7a21eb9](https://github.com/maximegris/angular-electron/commit/7a21eb9))
479 | * v1.0.1 ([68275a3](https://github.com/maximegris/angular-electron/commit/68275a3))
480 | * chore: initial commit from @angular/cli ([616a69e](https://github.com/maximegris/angular-electron/commit/616a69e))
481 |
482 |
483 |
484 |
--------------------------------------------------------------------------------