Skip to content

Commit b628051

Browse files
Merge pull request #123 from CenterForOpenScience/feat/222-wiki-page-compare
feat(wiki): add autocomplete, refactoring
2 parents c2ea8c4 + 2bdea3a commit b628051

24 files changed

+269
-52
lines changed

angular.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@
4949
},
5050
"scripts": [
5151
"node_modules/ngx-markdown-editor/assets/highlight.js/highlight.min.js",
52-
"node_modules/ngx-markdown-editor/assets/marked.min.js"
52+
"node_modules/ngx-markdown-editor/assets/marked.min.js",
53+
"src/assets/js/ace/snippetsMarkdown.js"
5354
]
5455
},
5556
"configurations": {

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,14 @@
4343
"ace-builds": "^1.42.0",
4444
"chart.js": "^4.4.9",
4545
"diff": "^8.0.2",
46+
"markdown-it": "^14.1.0",
47+
"markdown-it-video": "^0.6.3",
4648
"ngx-markdown-editor": "^5.3.4",
4749
"primeflex": "^4.0.0",
4850
"primeicons": "^7.0.0",
4951
"primeng": "^19.0.9",
5052
"rxjs": "~7.8.0",
5153
"tslib": "^2.3.0",
52-
"turndown": "^7.2.0",
5354
"zone.js": "~0.15.0"
5455
},
5556
"devDependencies": {
@@ -60,7 +61,7 @@
6061
"@commitlint/config-conventional": "^19.7.1",
6162
"@types/jasmine": "~5.1.0",
6263
"@types/jest": "^29.5.14",
63-
"@types/turndown": "^5.0.5",
64+
"@types/markdown-it": "^14.1.2",
6465
"angular-eslint": "19.1.0",
6566
"angularx-qrcode": "^19.0.0",
6667
"eslint": "^9.20.0",

src/@types/markdown-it-video.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
declare module 'markdown-it-video' {
2+
import { PluginSimple } from 'markdown-it';
3+
4+
const videoPlugin: PluginSimple;
5+
export default videoPlugin;
6+
}
7+
8+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
9+
declare const ace: any;

src/app/app.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { withNgxsReduxDevtoolsPlugin } from '@ngxs/devtools-plugin';
22
import { provideStore } from '@ngxs/store';
33

4+
import { provideMarkdown } from 'ngx-markdown';
45
import { TranslateModule } from '@ngx-translate/core';
56

67
import { ConfirmationService, MessageService } from 'primeng/api';
@@ -42,5 +43,6 @@ export const appConfig: ApplicationConfig = {
4243
ConfirmationService,
4344
MessageService,
4445
{ provide: ErrorHandler, useClass: GlobalErrorHandler },
46+
provideMarkdown(),
4547
],
4648
};

src/app/features/project/overview/components/overview-wiki/overview-wiki.component.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ <h2>{{ 'project.overview.wiki.title' | translate }}</h2>
44
<p-skeleton width="100%" height="5rem" />
55
} @else {
66
@if (wikiContent()) {
7-
<osf-truncated-text [text]="wikiContent()" />
7+
<osf-truncated-text [text]="wikiContent()" [hasContent]="true">
8+
<osf-markdown [markdownText]="wikiContent()"></osf-markdown>
9+
</osf-truncated-text>
810
} @else {
911
<div [innerHtml]="'project.overview.wiki.noWikiMessage' | translate"></div>
1012
}

src/app/features/project/overview/components/overview-wiki/overview-wiki.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import { Skeleton } from 'primeng/skeleton';
77
import { ChangeDetectionStrategy, Component } from '@angular/core';
88

99
import { WikiSelectors } from '@osf/features/project/wiki/store';
10-
import { TruncatedTextComponent } from '@shared/components';
10+
import { MarkdownComponent, TruncatedTextComponent } from '@shared/components';
1111

1212
@Component({
1313
selector: 'osf-project-wiki',
14-
imports: [Skeleton, TranslatePipe, TruncatedTextComponent],
14+
imports: [Skeleton, TranslatePipe, TruncatedTextComponent, MarkdownComponent],
1515
templateUrl: './overview-wiki.component.html',
1616
styleUrl: './overview-wiki.component.scss',
1717
changeDetection: ChangeDetectionStrategy.OnPush,

src/app/features/project/wiki/components/compare-section/compare-section.component.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { FormsModule } from '@angular/forms';
1010
import { WikiVersion } from '../../models';
1111

1212
import * as Diff from 'diff';
13-
import TurndownService from 'turndown';
1413

1514
@Component({
1615
selector: 'osf-compare-section',
@@ -39,10 +38,7 @@ export class CompareSectionComponent {
3938
]);
4039

4140
content = computed(() => {
42-
const changes = Diff.diffWords(
43-
this.convertHtmlToMarkdown(this.versionContent()),
44-
this.convertHtmlToMarkdown(this.previewContent())
45-
);
41+
const changes = Diff.diffWords(this.versionContent(), this.previewContent());
4642
return changes
4743
.map((change) => {
4844
if (change.added) {
@@ -61,18 +57,6 @@ export class CompareSectionComponent {
6157
this.selectVersion.emit(this.selectedVersion);
6258
});
6359
}
64-
convertHtmlToMarkdown(html: string): string {
65-
const turndownService = new TurndownService({
66-
headingStyle: 'atx',
67-
emDelimiter: '*',
68-
strongDelimiter: '**',
69-
hr: '---',
70-
linkStyle: 'inlined',
71-
bulletListMarker: '-',
72-
codeBlockStyle: 'indented',
73-
});
74-
return turndownService.turndown(html);
75-
}
7660
onVersionChange(versionId: string): void {
7761
this.selectedVersion = versionId;
7862
this.selectVersion.emit(versionId);

src/app/features/project/wiki/components/edit-section/edit-section.component.html

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ <h2 class="mr-2">{{ 'project.wiki.edit' | translate }}</h2>
2222
[(ngModel)]="content"
2323
[options]="options"
2424
(onEditorLoaded)="onEditorLoaded($event)"
25-
(onPreviewDomChanged)="onPreviewDomChanged($event)"
25+
(onPreviewDomChanged)="onPreviewDomChanged()"
2626
height="100%"
2727
>
2828
<slot custom-btns>
@@ -32,10 +32,19 @@ <h2 class="mr-2">{{ 'project.wiki.edit' | translate }}</h2>
3232
<button class="btn btn-sm btn-default" title="Redo" (click)="redo()">
3333
<i class="fas fa-rotate-right"></i>
3434
</button>
35-
<div class="btn-group">
36-
<button class="btn btn-sm btn-default" title="Horizontal rule <hr>" (click)="doHorizontalRule()">
37-
<i class="fas fa-grip-lines"></i>
38-
</button>
35+
<button class="btn btn-sm btn-default" title="Horizontal rule <hr>" (click)="doHorizontalRule()">
36+
<i class="fas fa-grip-lines"></i>
37+
</button>
38+
<div class="btn btn-sm btn-default flex align-items-center">
39+
<p-checkbox
40+
inputId="autoComplete"
41+
[value]="autoCompleteEnabled"
42+
size="small"
43+
variant="outlined"
44+
binary
45+
(onChange)="toggleAutocomplete()"
46+
/>
47+
<label for="autoComplete" class="ml-2 mb-0"> Autocomplete </label>
3948
</div>
4049
<button class="btn btn-sm btn-default" title="Wiki syntax help" (click)="openSyntaxHelpDialog()">
4150
<i class="fas fa-question"></i>

src/app/features/project/wiki/components/edit-section/edit-section.component.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,31 @@ import { LMarkdownEditorModule, MdEditorOption } from 'ngx-markdown-editor';
22
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
33

44
import { Button } from 'primeng/button';
5+
import { Checkbox } from 'primeng/checkbox';
56
import { DialogService } from 'primeng/dynamicdialog';
67
import { PanelModule } from 'primeng/panel';
78

8-
import { ChangeDetectionStrategy, Component, inject, input, OnInit, output } from '@angular/core';
9+
import { ChangeDetectionStrategy, Component, effect, inject, input, OnInit, output } from '@angular/core';
910
import { FormsModule } from '@angular/forms';
1011

12+
import 'ace-builds/src-noconflict/ext-language_tools';
13+
1114
import { WikiSyntaxHelpDialogComponent } from '../wiki-syntax-help-dialog/wiki-syntax-help-dialog.component';
1215

1316
@Component({
1417
selector: 'osf-edit-section',
15-
imports: [PanelModule, Button, TranslatePipe, FormsModule, LMarkdownEditorModule],
18+
imports: [PanelModule, Button, TranslatePipe, FormsModule, LMarkdownEditorModule, Checkbox],
1619
templateUrl: './edit-section.component.html',
1720
styleUrl: './edit-section.component.scss',
1821
changeDetection: ChangeDetectionStrategy.OnPush,
1922
providers: [DialogService],
2023
})
2124
export class EditSectionComponent implements OnInit {
2225
readonly currentContent = input.required<string>();
26+
readonly versionContent = input.required<string>();
2327
readonly isSaving = input<boolean>(false);
2428
readonly contentChange = output<string>();
2529
readonly saveContent = output<string>();
26-
private htmlContent = '';
2730
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2831
private editorInstance: any;
2932
content = '';
@@ -41,6 +44,18 @@ export class EditSectionComponent implements OnInit {
4144
hideIcons: [''],
4245
};
4346

47+
autoCompleteEnabled = false;
48+
49+
constructor() {
50+
effect(() => {
51+
const versionContent = this.versionContent();
52+
if (!this.initialContent) {
53+
this.content = versionContent;
54+
this.initialContent = versionContent;
55+
}
56+
});
57+
}
58+
4459
ngOnInit(): void {
4560
this.content = this.currentContent();
4661
this.initialContent = this.currentContent();
@@ -50,15 +65,20 @@ export class EditSectionComponent implements OnInit {
5065
onEditorLoaded(editor: any) {
5166
this.editorInstance = editor;
5267
editor.setShowPrintMargin(false);
68+
const langTools = ace.require('ace/ext/language_tools');
69+
editor.setOptions({
70+
enableBasicAutocompletion: this.autoCompleteEnabled,
71+
enableLiveAutocompletion: this.autoCompleteEnabled,
72+
enableSnippets: [langTools.snippetCompleter],
73+
});
5374
}
5475

55-
onPreviewDomChanged(event: HTMLElement) {
56-
this.htmlContent = event.innerHTML;
57-
this.contentChange.emit(event.innerHTML);
76+
onPreviewDomChanged() {
77+
this.contentChange.emit(this.editorInstance?.getValue());
5878
}
5979

6080
save() {
61-
this.saveContent.emit(this.htmlContent);
81+
this.saveContent.emit(this.editorInstance?.getValue());
6282
}
6383

6484
revert() {
@@ -85,4 +105,14 @@ export class EditSectionComponent implements OnInit {
85105
modal: true,
86106
});
87107
}
108+
109+
toggleAutocomplete() {
110+
if (this.editorInstance) {
111+
this.autoCompleteEnabled = !this.autoCompleteEnabled;
112+
this.editorInstance.setOptions({
113+
enableBasicAutocompletion: this.autoCompleteEnabled,
114+
enableLiveAutocompletion: this.autoCompleteEnabled,
115+
});
116+
}
117+
}
88118
}

src/app/features/project/wiki/components/view-section/view-section.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ <h2 class="mr-2">{{ 'project.wiki.view' | translate }}</h2>
2222
<p-panel class="view-panel" showHeader="false">
2323
<div class="editor-view-mode mt-3">
2424
@if (content()) {
25-
<div class="md-editor-container" [innerHTML]="content() | safeHtml"></div>
25+
<osf-markdown [markdownText]="content()"></osf-markdown>
2626
} @else {
2727
<p class="font-italic">{{ 'project.wiki.version.noContent' | translate }}</p>
2828
}

0 commit comments

Comments
 (0)