Wiring of markdown-preview

This commit is contained in:
itdominator 2025-07-04 21:59:09 -05:00
parent e64a18b18b
commit 857f0ded57
18 changed files with 195 additions and 24 deletions

View File

@ -47,6 +47,7 @@
"src/assets/css/ace-overrides.css"
],
"scripts":[
"src/libs/showdown.min.js"
],
"optimization": true
},
@ -110,7 +111,6 @@
"src/styles.css"
],
"scripts":[
]
}
}

View File

@ -2,6 +2,7 @@
<info-bar></info-bar>
<tabs></tabs>
<editors></editors>
<markdown-preview></markdown-preview>
<search-replace></search-replace>
<files-modal></files-modal>

View File

@ -4,6 +4,7 @@ import { InfoBarComponent } from './editor/info-bar/info-bar.component';
import { TabsComponent } from './editor/tabs/tabs.component';
import { EditorsComponent } from './editor/editors.component';
import { SearchReplaceComponent } from "./editor/search-replace/search-replace.component";
import { MarkdownPreviewComponent } from "./editor/markdown-preview/markdown-preview.component";
import { FilesModalComponent } from "./common/components/modals/files/files-modal.component";
@ -15,6 +16,7 @@ import { FilesModalComponent } from "./common/components/modals/files/files-moda
TabsComponent,
EditorsComponent,
SearchReplaceComponent,
MarkdownPreviewComponent,
FilesModalComponent
],
templateUrl: './app.component.html',

View File

@ -28,10 +28,14 @@ export const Keybindings: Array<{}> = [
name: "showLSPModal",
bindKey: {win: "ctrl-shift-l", mac: "ctrl-shift-l"},
readOnly: false
}, {
name: "markdownPreviewPopup",
bindKey: {win: "ctrl-shift-m", mac: "ctrl-shift-m"},
readOnly: false
}, {
name: "searchPopup",
bindKey: {win: "ctrl-f", mac: "ctrl-f"},
readOnly: true
readOnly: false
}, {
name: "replacePopup",
bindKey: {win: "ctrl-r", mac: "ctrl-r"},

View File

@ -0,0 +1,27 @@
import { Injectable } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs';
import { ServiceMessage } from '../../../types/service-message.type';
@Injectable({
providedIn: 'root'
})
export class MarkdownPreviewService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1);
constructor() {
}
public sendMessage(data: ServiceMessage): void {
this.messageSubject.next(data);
}
public getMessage$(): Observable<ServiceMessage> {
return this.messageSubject.asObservable();
}
}

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from '@angular/core';
import { Injectable } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs';
import { ServiceMessage } from '../../../types/service-message.type';

View File

@ -7,6 +7,7 @@ import { TabsService } from '../../common/services/editor/tabs/tabs.service';
import { EditorsService } from '../../common/services/editor/editors.service';
import { FilesService } from '../../common/services/files.service';
import { SearchReplaceService } from '../../common/services/editor/search-replace/search-replace.service';
import { MarkdownPreviewService } from '../../common/services/editor/markdown-preview/markdown-preview.service';
import { EditorSettings } from "../../common/configs/editor.config";
import { NewtonFile } from '../../common/types/file.type';
@ -23,12 +24,13 @@ export class CodeViewBase {
public leftSiblingUUID!: string;
public rightSiblingUUID!: string;
protected infoBarService: InfoBarService = inject(InfoBarService);
protected filesModalService: FilesModalService = inject(FilesModalService);
protected tabsService: TabsService = inject(TabsService);
protected editorsService: EditorsService = inject(EditorsService);
protected filesService: FilesService = inject(FilesService);
protected searchReplaceService: SearchReplaceService = inject(SearchReplaceService);
protected infoBarService: InfoBarService = inject(InfoBarService);
protected filesModalService: FilesModalService = inject(FilesModalService);
protected tabsService: TabsService = inject(TabsService);
protected editorsService: EditorsService = inject(EditorsService);
protected filesService: FilesService = inject(FilesService);
protected searchReplaceService: SearchReplaceService = inject(SearchReplaceService);
protected markdownPreviewService: MarkdownPreviewService = inject(MarkdownPreviewService);
@ViewChild('editor') editorElm!: ElementRef;
@Input() editorSettings!: typeof EditorSettings;
@ -95,6 +97,12 @@ export class CodeViewBase {
this.editor.showKeyboardShortcuts();
}
public markdownPreviewPopup() {
let message = new ServiceMessage();
message.action = "toggle-markdown-preview";
this.markdownPreviewService.sendMessage(message);
}
public searchPopup() {
let message = new ServiceMessage();
message.action = "toggle-search-replace";

View File

@ -117,6 +117,11 @@ export class CodeViewComponent extends CodeViewBase {
this.editorsService.sendMessage(message);
this.searchReplaceService.sendMessage(message);
message = new ServiceMessage();
message.action = "set-active-editor";
message.rawData = this;
this.markdownPreviewService.sendMessage(message);
this.updateInfoBar();
});

View File

@ -0,0 +1,6 @@
<div class="row">
<div class="col">
<div [innerHtml]="bodyHtml || defaultHtml">
</div>
</div>
</div>

View File

@ -0,0 +1,96 @@
import { Component, HostBinding, inject } from '@angular/core';
import { Subject, takeUntil } from 'rxjs';
import { MarkdownPreviewService } from '../../common/services/editor/markdown-preview/markdown-preview.service';
import { ServiceMessage } from '../../common/types/service-message.type';
@Component({
selector: 'markdown-preview',
standalone: true,
imports: [
],
templateUrl: './markdown-preview.component.html',
styleUrl: './markdown-preview.component.css',
host: {
'class': 'container-fluid markdown-preview'
}
})
export class MarkdownPreviewComponent {
private unsubscribe: Subject<void> = new Subject();
private markdownPreviewService: MarkdownPreviewService = inject(MarkdownPreviewService);
@HostBinding("class.hidden") isHidden: boolean = true;
converter: any = new showdown.Converter();
defaultHtml: string = "<h1>NOT a Markdown file...</h1>"
bodyHtml: string = "";
private editorComponent!: any;
constructor() {
}
private ngAfterViewInit(): void {
this.loadSubscribers();
}
private ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
private loadSubscribers() {
this.markdownPreviewService.getMessage$().pipe(
takeUntil(this.unsubscribe)
).subscribe((message: ServiceMessage) => {
if (message.action === "toggle-markdown-preview") {
this.toggleMarkdownPreview(message);
} else if (message.action === "set-active-editor") {
this.setActiveEditor(message);
}
});
}
private toggleMarkdownPreview(message: ServiceMessage) {
this.isHidden = !this.isHidden;
setTimeout(() => {
this.updatePreview();
}, 200);
}
private setActiveEditor(message: ServiceMessage) {
if (this.editorComponent == message.rawData) return;
this.editorComponent = message.rawData;
if (this.isHidden) return;
this.updatePreview();
}
public updatePreview() {
let fileMode = this.editorComponent.editor.session.getMode()["$id"];
let isMdFile = (fileMode.includes("markdown"));
this.bodyHtml = "";
if (!isMdFile) return;
let mdStr = this.editorComponent.editor.session.getValue();
let pathParts = this.editorComponent.activeFile.path.split("/");
let basePath = "file://" + pathParts.slice(0, -1).join("/");
this.bodyHtml = this.converter.makeHtml(
mdStr.replaceAll("](images", `](${basePath}/images`)
.replaceAll("](imgs", `](${basePath}/imgs`)
.replaceAll("](pictures", `](${basePath}/pictures`)
.replaceAll("](pics", `](${basePath}/pics`)
.replaceAll("](screenshots", `](${basePath}/screenshots`)
);
}
}

View File

@ -91,7 +91,7 @@ export class SearchReplaceComponent {
}
private setActiveEditor(message: ServiceMessage) {
if (!this.isHidden && this.editor == message.rawData) return;
if (this.editor == message.rawData) return;
this.editor = message.rawData;

View File

@ -20,22 +20,34 @@ body {
/* CLASSES */
.markdown-preview {
top: 2em;
bottom: 2em;
right: 2em;
z-index: 900;
position: fixed;
display: inline-block;
width: 50vw;
overflow: auto;
background-color: rgba(64, 64, 64, 0.84);
}
.search-replace {
bottom: 2em;
left: 2em;
right: 2em;
z-index: 800;
display: inline-block;
position: fixed;
background-color: rgba(64, 64, 64, 0.64);
}
.info-bar {
font-size: 0.8em;
color: rgba(255, 255, 255, 0.84);
text-align: center;
}
.search-replace {
bottom: 2em;
z-index: 999;
display: inline-block;
position: fixed;
width: 100%;
background-color: rgba(64, 64, 64, 0.24);
}
.tabs {
display: flex;
overflow: auto;

3
src/libs/showdown.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
src/typings.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare var showdown: any;

View File

@ -13,4 +13,4 @@
"src/polyfills.ts",
"src/**/*.d.ts"
]
}
}

View File

@ -16,8 +16,11 @@
"declaration": false,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true
}
"forceConsistentCasingInFileNames": true,
},
"includes": [
"src/typings.d.ts"
]
}
@ -53,7 +56,10 @@
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
},
"includes": [
"src/typings.d.ts"
]
}
*/