diff --git a/README.md b/README.md
index e7ae1b5..fa1f2f4 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
",
"homepage": "https://www.proangular.com",
@@ -32,6 +32,7 @@
"@angular/platform-browser": ">=12 <15",
"@ngneat/until-destroy": "^9.2.1",
"highlight.js": "^11.6.0",
+ "highlightjs-line-numbers.js": "^2.8.0",
"io-ts": "^2.2.17",
"io-ts-types": "^0.5.16"
},
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index e80236b..cd575d4 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,3 +1,4 @@
+import { NgxGist } from './public/ngx-gist.model';
import { Component } from '@angular/core';
@Component({
@@ -9,26 +10,102 @@ import { Component } from '@angular/core';
Examples of displaying local and GitHub gists and code snippets.
-
+
+
+
+ FETCHED GIST (AUTO CACHED FOR 24 HOURS)
+
+ ngx-gist will fetch the gist once and store it locally for 24 hours. In
+ that timeframe, if the user returns or visits another page where this
+ gist was previously loaded, it will reload the content without having to
+ reach out to GitHub again.
+
-
+
+ FETCHED GIST (FORCED NO CACHE)
+
+ Force no cache. This will force ngx-gist to retrieve the content live
+ from GitHub every time this content loads. This is disabled by default,
+ but could be useful if your gists change frequently.
+
+
+
+ DISPLAYING ONE SPECIFIC FILE
+ Display only one specific file when your gist has many.
-
+
+ DISPLAYING MULTIPLE, SPECIFIC FILES
+ You can also display any number of specific files by name.
-
-
+
+ HIDING LINE NUMBERS
+
+ Line numbers are enabled by default, but you can turn them off like so.
+
+
`,
- styles: [],
+ styles: [
+ `
+ h2 {
+ margin-top: 2rem;
+ }
+ h3 {
+ margin-bottom: 3rem;
+ }
+ `,
+ ],
})
-export class AppComponent {}
+export class AppComponent {
+ public readonly localGistObject = NgxGist.create({
+ // Required
+ files: [
+ {
+ content: getTimeSnippet,
+ filename: 'get-time.ts',
+ },
+ {
+ content: printHelloWorldSnippet,
+ filename: 'print-hello-world.js',
+ },
+ ],
+ // Optional
+ created_at: undefined,
+ languageOverride: undefined,
+ });
+}
+
+const getTimeSnippet = `
+function getTime(): number {
+ return new Date().getTime();
+}
+`.trimStart();
+
+const printHelloWorldSnippet = `
+function printHelloWorld() {
+ console.log('Hello world!');
+}
+`.trimStart();
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 927aac8..2833956 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -5,6 +5,8 @@ import { NgxGistModule } from './public/ngx-gist.module';
import { BodyComponent, FooterComponent, HeaderComponent } from './layout';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { MatToolbarModule } from '@angular/material/toolbar';
+import { MatButtonModule } from '@angular/material/button';
@NgModule({
declarations: [AppComponent, BodyComponent, FooterComponent, HeaderComponent],
@@ -12,6 +14,8 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
BrowserAnimationsModule,
BrowserModule,
HttpClientModule,
+ MatButtonModule,
+ MatToolbarModule,
NgxGistModule,
],
providers: [],
diff --git a/src/app/layout/body.component.ts b/src/app/layout/body.component.ts
index d2890d3..41c5f07 100644
--- a/src/app/layout/body.component.ts
+++ b/src/app/layout/body.component.ts
@@ -12,6 +12,7 @@ import { Component } from '@angular/core';
section {
padding: 1rem;
min-height: 90vh;
+ margin-top: 64px;
}
`,
],
diff --git a/src/app/layout/header.component.ts b/src/app/layout/header.component.ts
index 42f1791..184a661 100644
--- a/src/app/layout/header.component.ts
+++ b/src/app/layout/header.component.ts
@@ -3,17 +3,55 @@ import { Component } from '@angular/core';
@Component({
selector: 'ngx-header',
template: `
-
+
+
+
+
+
+
+
+
`,
styles: [
`
- header {
+ :host,
+ mat-toolbar {
+ position: fixed;
background-color: #1775d1;
- padding: 0.5rem 1rem;
+ padding: 0;
+ z-index: 1000;
+ width: 100%;
+ height: 64px;
+ top: 0;
+ }
+ a.logo,
+ img.git-hub {
+ height: 40px;
+ }
+ .github-link-container {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ width: 100%;
+ min-width: 6.5rem;
+ height: 100%;
+ margin-right: 1rem;
+ position: relative;
+ margin: 0;
+
+ > a {
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+ }
}
`,
],
diff --git a/src/app/public/ngx-gist-content.pipe.ts b/src/app/public/ngx-gist-content.pipe.ts
deleted file mode 100644
index a752dfb..0000000
--- a/src/app/public/ngx-gist-content.pipe.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { NgxGist } from './ngx-gist.model';
-import { Pipe, PipeTransform } from '@angular/core';
-
-@Pipe({ name: 'gistContent' })
-export class GistContentPipe implements PipeTransform {
- public transform(value: NgxGist, key: string): string {
- const file = value.files[key];
- return file.content;
- }
-}
diff --git a/src/app/public/ngx-gist-file-filter.pipe.ts b/src/app/public/ngx-gist-file-filter.pipe.ts
index c246558..a9fe88e 100644
--- a/src/app/public/ngx-gist-file-filter.pipe.ts
+++ b/src/app/public/ngx-gist-file-filter.pipe.ts
@@ -1,22 +1,33 @@
import { NgxGist } from './ngx-gist.model';
import { Pipe, PipeTransform } from '@angular/core';
+import { isNonEmptyString, isStringArray } from './ngx-gist.utilities';
@Pipe({ name: 'gistFileFilter' })
export class GistFileFilterPipe implements PipeTransform {
public transform(
- files: NgxGist['files'] | null,
- displayOnlyFileName?: string | null,
- ): NgxGist['files'] {
+ files: NgxGist['highlightedFiles'] | null,
+ displayOnlyFileNames?: string | readonly string[] | null,
+ ): NgxGist['highlightedFiles'] {
if (!files) {
- return {};
+ return [];
}
- if (!displayOnlyFileName) {
+ if (!displayOnlyFileNames || displayOnlyFileNames === '') {
return files;
}
- return {
- [displayOnlyFileName]: files[displayOnlyFileName],
- };
+ if (isNonEmptyString(displayOnlyFileNames)) {
+ return (
+ files.filter(({ filename }) => displayOnlyFileNames === filename) ?? []
+ );
+ }
+
+ if (isStringArray(displayOnlyFileNames)) {
+ return files.filter(({ filename }) =>
+ displayOnlyFileNames.includes(filename),
+ );
+ }
+
+ return files;
}
}
diff --git a/src/app/public/ngx-gist-line-numbers.service.ts b/src/app/public/ngx-gist-line-numbers.service.ts
new file mode 100644
index 0000000..1c3835f
--- /dev/null
+++ b/src/app/public/ngx-gist-line-numbers.service.ts
@@ -0,0 +1,65 @@
+import { DOCUMENT } from '@angular/common';
+import { Inject, Injectable } from '@angular/core';
+import hljs, { HLJSApi } from 'highlight.js';
+import { filter, map, Observable, firstValueFrom, from } from 'rxjs';
+
+@Injectable({ providedIn: 'root' }) // Must be a singleton
+export class NgxGistLineNumbersService {
+ public constructor(@Inject(DOCUMENT) private readonly document: Document) {}
+ private isLoaded = false;
+
+ public async load(): Promise {
+ if (
+ this.isLoaded ||
+ typeof this.document.defaultView?.hljs?.initLineNumbersOnLoad ===
+ 'function'
+ ) {
+ return;
+ }
+
+ try {
+ if (this.document.defaultView) {
+ // Ensure hljs is available before we load the dependant library
+ // `highlightjs-line-numbers.js` dynamically as a js import.
+ this.document.defaultView.hljs = hljs;
+ } else {
+ throw new Error(
+ `Unable to access default view to apply "highlight.js" package.`,
+ );
+ }
+
+ await firstValueFrom(this.loadHljsLineNumbersLibrary()).then(() => {
+ // The library `highlightjs-line-numbers.js` adds new functions to the
+ // `highlight.js` scope on load, so we should now be able to call it
+ // without failure.
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ this.document.defaultView?.hljs?.initLineNumbersOnLoad!();
+ });
+ } catch (e: unknown) {
+ console.log(e);
+ } finally {
+ this.isLoaded = true;
+ }
+ }
+
+ /* eslint-disable @typescript-eslint/no-explicit-any */
+ private loadHljsLineNumbersLibrary(): Observable {
+ return from(import('highlightjs-line-numbers.js' as any)).pipe(
+ filter((module: any) => !!module && !!module.default),
+ map((module: any) => module.default),
+ );
+ }
+ /* eslint-enable @typescript-eslint/no-explicit-any */
+}
+
+declare global {
+ interface Window {
+ hljs?: HLJSApi & {
+ /* eslint-disable @typescript-eslint/no-explicit-any */
+ initLineNumbersOnLoad?: (options?: any) => void;
+ lineNumbersBlock?: (value: Element, options?: any) => void;
+ lineNumbersValue?: (value: string, options?: any) => string;
+ /* eslint-enable @typescript-eslint/no-explicit-any */
+ };
+ }
+}
diff --git a/src/app/public/ngx-gist-theme.service.ts b/src/app/public/ngx-gist-theme.service.ts
new file mode 100644
index 0000000..3f3b1a3
--- /dev/null
+++ b/src/app/public/ngx-gist-theme.service.ts
@@ -0,0 +1,37 @@
+import { Inject, Injectable } from '@angular/core';
+import { DOCUMENT } from '@angular/common';
+
+@Injectable({ providedIn: 'root' }) // Must be a singleton
+export class NgxGistThemeService {
+ public constructor(@Inject(DOCUMENT) private readonly document: Document) {}
+
+ private importElMaterialTheme: HTMLLinkElement | null = null;
+
+ public setTheme(materialPrebuiltTheme: MaterialPrebuiltTheme): void {
+ const themeId = 'material-theme-import';
+ const currentEl = this.document.getElementById(themeId);
+
+ if (currentEl) {
+ this.document.removeChild(currentEl);
+ }
+
+ if (this.importElMaterialTheme) {
+ this.document.removeChild(this.importElMaterialTheme);
+ }
+
+ this.importElMaterialTheme = this.document.createElement('link');
+ this.importElMaterialTheme.href = `https://unpkg.com/@angular/material@14.1.0/prebuilt-themes/${materialPrebuiltTheme}.css`;
+ this.importElMaterialTheme.media = 'screen,print';
+ this.importElMaterialTheme.rel = 'stylesheet';
+ this.importElMaterialTheme.type = 'text/css';
+ this.importElMaterialTheme.id = themeId;
+
+ this.document.head.appendChild(this.importElMaterialTheme);
+ }
+}
+
+export type MaterialPrebuiltTheme =
+ | 'deeppurple-amber'
+ | 'indigo-pink'
+ | 'pink-bluegrey'
+ | 'purple-green';
diff --git a/src/app/public/ngx-gist.component.ts b/src/app/public/ngx-gist.component.ts
index 0f1e3a8..f7c3f2e 100644
--- a/src/app/public/ngx-gist.component.ts
+++ b/src/app/public/ngx-gist.component.ts
@@ -2,62 +2,87 @@ import { NgxGistService } from './ngx-gist.service';
import { isNonEmptyValue } from './ngx-gist.utilities';
import { NgxGist } from './ngx-gist.model';
import { Component, Inject, Input, OnInit } from '@angular/core';
-import { Language, default as hljs } from 'highlight.js';
-import { filter, firstValueFrom, ReplaySubject } from 'rxjs';
+import { Language } from 'highlight.js';
+import { BehaviorSubject, filter, firstValueFrom, ReplaySubject } from 'rxjs';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { DOCUMENT } from '@angular/common';
+import { NgxGistLineNumbersService } from './ngx-gist-line-numbers.service';
+import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
+import {
+ MaterialPrebuiltTheme,
+ NgxGistThemeService,
+} from './ngx-gist-theme.service';
@UntilDestroy()
@Component({
selector: 'ngx-gist',
template: `
-
-
+
+
+ Error loading code...
+
-
+
+
link Open Gist on GitHub
+
+ Loading Code Snippet...
`,
styleUrls: ['./ngx-gist.component.scss'],
})
export class NgxGistComponent implements OnInit {
public constructor(
- @Inject(DOCUMENT)
- private readonly document: Document,
+ @Inject(DOCUMENT) private readonly document: Document,
+ private readonly domSanitizer: DomSanitizer,
private readonly ngxGistService: NgxGistService,
+ private readonly ngxGistLineNumbersService: NgxGistLineNumbersService,
+ private readonly ngxGistThemeService: NgxGistThemeService,
) {}
- public codeSnippet: string | null = null;
- private htmlLinkElement: HTMLLinkElement | null = null;
+ // TODO: Apply HighlightJs code theme.
+ // @Input() public codeTheme?: unknown;
/**
- * Display in the DOM only the selected filename from the gists files array.
- *
- * TODO: Make this possible for string array input.
+ * Display in the DOM only the selected filename(s) from the gists files array.
*
* Default: `undefined`
+ *
+ * Example: `'my-styles.scss'` or `'super-feature.ts'`
+ *
+ * Tip: Can be either a string or string array. File names much match exactly,
+ * be sure to remove any leading or trailing whitespace in the provided strings.
*/
- @Input() public displayOnlyFileName?: string;
+ @Input() public displayOnlyFileNames?: string | readonly string[];
+ /**
+ * Optionally hide the gist link which opens the gist on GitHub. The gist links
+ * automatically dispaly for remote gists, but can be hidden with this feature.
+ *
+ * Default: `false`
+ */
+ @Input() public hideGistLink = false;
/**
* Provide a static gist model here directly which will be displayed if
* no `gistId` is provided for remote fetching. Also this model will be
@@ -66,53 +91,62 @@ export class NgxGistComponent implements OnInit {
*
* Default: `undefined`
*/
- @Input() public gist?: NgxGist;
- // We want reactive behavior for `gistId` so we can update gists asynchronously
- private readonly gistIdSubject = new ReplaySubject<
- NgxGistComponent['gistId']
- >(1);
- public readonly gistIdChanges = this.gistIdSubject.asObservable();
+ @Input() public set gist(value: NgxGist | undefined) {
+ this.gistSubject.next(value);
+ }
+ private readonly gistSubject = new BehaviorSubject(
+ undefined,
+ );
+ public readonly gistChanges = this.gistSubject.asObservable();
/**
* Provide the GitHub gist id to be fetched and loaded. This can be found in
* URL of the gists you create. For example the id `TH1515th31DT0C0PY` in:
* https://gist.github.com/FakeUserName/TH1515th31DT0C0PY
*
- * Alternatively, provide a value directly in the sibling input `gist`.
+ * Default: `undefined`
+ *
+ * Tip: Alternatively, provide a value directly in the sibling input `gist`.
*/
@Input() public set gistId(value: string) {
this.gistIdSubject.next(value);
}
+ private readonly gistIdSubject = new ReplaySubject<
+ NgxGistComponent['gistId']
+ >(1);
+ public readonly gistIdChanges = this.gistIdSubject.asObservable();
/**
* When defined, override automatic language detection [and styling] and
* treat all gists as this lanuage.
*
- * See supported languages here:
- * https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md
- *
* Default: `undefined`
+ *
+ * Tip: See supported language strings here:
+ * https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md
*/
@Input() public languageName?: Language['name'];
/**
* Define a material core theme to apply. Ideally, you should already have
* your global material theme set at the root of your project so try to
- * avoid using this if possible. Note: These are also loaded from a CDN.
- *
- * See theming Angular Material: https://material.angular.io/guide/theming
+ * avoid using this if possible.
*
- * CDN used: `https://unpkg.com`
+ * Note: These are loaded from the CDN: `https://unpkg.com`
*
* Default: `undefined`
+ *
+ * Tip: See theming Angular Material: https://material.angular.io/guide/theming
+ * if you need help applying a global material theme.
+ */
+ @Input() public materialTheme?: MaterialPrebuiltTheme;
+ /**
+ * Display or hide the line numbers in your gist code snippets.
+ *
+ * Default: `true`
*/
- @Input() public materialTheme:
- | 'deeppurple-amber'
- | 'indigo-pink'
- | 'pink-bluegrey'
- | 'purple-green'
- | undefined = undefined;
+ @Input() public showLineNumbers = true;
/**
* Cache the GitHub gist request in local memory for 24 hours. GitHub has a
* request limit, so this helps in reducing bandwidth. Loads previously
- * fetched gist content from the users machine.
+ * fetched gist content from the users machine on refresh and page re-visits.
*
* Default: `true`
*/
@@ -121,6 +155,10 @@ export class NgxGistComponent implements OnInit {
public async ngOnInit(): Promise {
this.setTheme();
+ if (this.showLineNumbers) {
+ await this.ngxGistLineNumbersService.load();
+ }
+
this.gistIdChanges
.pipe(filter(isNonEmptyValue), untilDestroyed(this))
.subscribe(async (gistId) => {
@@ -137,31 +175,17 @@ export class NgxGistComponent implements OnInit {
});
}
- // TODO: Work on speeding this call up. Or possibly pre-render instead.
- public getHighlightJsContent(value: string): string {
- const userSpecifiedLanguage = this.languageName;
- if (userSpecifiedLanguage) {
- return hljs.highlight(value, { language: userSpecifiedLanguage }).value;
- }
-
- return hljs.highlightAuto(value).value;
- }
-
private async fetchAndSetGist(gistId: string): Promise {
- // Use the initial gist model as a fallback for a failed fetch. This
- // enables us to have a fallback gist snippet should we be offline or
- // the data is unavailable for some reason.
- const initialGist = this.gist ? { ...this.gist } : undefined;
-
// Fetch and hydrate model or fallback to initial gist.
- this.gist =
- (await firstValueFrom(this.ngxGistService.get(gistId))) ?? initialGist;
+ const fetcheGist =
+ (await firstValueFrom(this.ngxGistService.get(gistId))) ?? undefined;
+ this.gist = fetcheGist;
- if (this.useCache && this.gist) {
+ if (this.useCache && fetcheGist) {
// Set value in cache for reuse saving on the amount of HTTP requests.
// Set refresh time to be a hard coded 24 hours. This was once configurable
// but I decided against it for simplicities sake on ease of use.
- this.ngxGistService.setToCache(this.gist, 1440);
+ this.ngxGistService.setToCache(fetcheGist, 1440);
}
}
@@ -169,12 +193,19 @@ export class NgxGistComponent implements OnInit {
if (!this.materialTheme) {
return;
}
+ this.ngxGistThemeService.setTheme(this.materialTheme);
+ }
- this.htmlLinkElement = this.document.createElement('link');
- this.htmlLinkElement.href = `https://unpkg.com/@angular/material@14.1.0/prebuilt-themes/${this.materialTheme}.css`;
- this.htmlLinkElement.media = 'screen,print';
- this.htmlLinkElement.rel = 'stylesheet';
- this.htmlLinkElement.type = 'text/css';
- this.document.head.appendChild(this.htmlLinkElement);
+ public applyLineNumbers(highlightedConent: string): SafeHtml | null {
+ if (
+ this.showLineNumbers &&
+ this.document.defaultView?.hljs &&
+ typeof this.document.defaultView.hljs.lineNumbersValue === 'function'
+ ) {
+ return this.domSanitizer.bypassSecurityTrustHtml(
+ this.document.defaultView.hljs.lineNumbersValue(highlightedConent),
+ );
+ }
+ return highlightedConent;
}
}
diff --git a/src/app/public/ngx-gist.model.ts b/src/app/public/ngx-gist.model.ts
index 40e290f..a6033e9 100644
--- a/src/app/public/ngx-gist.model.ts
+++ b/src/app/public/ngx-gist.model.ts
@@ -5,9 +5,10 @@ import {
isNonEmptyString,
parsedJsonFromStringCodec,
} from './ngx-gist.utilities';
+import hljs from 'highlight.js';
export class NgxGist implements Gist {
- public constructor(args: Gist) {
+ public constructor(args: Gist & Pick) {
this.comments = args.comments;
this.comments_url = args.comments_url;
this.commits_url = args.commits_url;
@@ -28,8 +29,23 @@ export class NgxGist implements Gist {
this.updated_at = new Date(args.updated_at);
this.url = args.url;
this.user = args.user;
+
+ // Additional properties
+ this.languageOverride = args.languageOverride;
+ const highlightedFiles: NgxGist['highlightedFiles'] = [];
+ for (const key in this.files) {
+ if (this.files[key]) {
+ const file = this.files[key];
+ highlightedFiles.push({
+ ...file,
+ highlightedContent: getHighlightedContent(file.content),
+ });
+ }
+ }
+ this.highlightedFiles = highlightedFiles;
}
+ /** Core gist properties */
/* eslint-disable @typescript-eslint/naming-convention */
public readonly comments: number;
public readonly comments_url: string;
@@ -53,23 +69,51 @@ export class NgxGist implements Gist {
public readonly user?: unknown;
/* eslint-enable @typescript-eslint/naming-convention */
+ /** Additional properties */
+ public readonly highlightedFiles: HighlightedFiles;
+ public readonly languageOverride?: string;
+
/**
* Create a local, static gist object. Do not use this for fetched data.
* Used for creating and displaying local code samples.
*
+ * Use `deserialize` sibling function for remote "unknown" responses.
+ *
* @param args Minimally necessary paramaters for displaying local code.
* @returns A 'partial' model in which unnecessary fields are dummny data.
*/
public static create(
- args: Pick,
+ args: { files: Files } & Partial> & // Required // Optional
+ Pick, // Optional
): NgxGist {
+ const files: NgxGist['files'] = args.files.reduce((prev, curr) => {
+ const file: GistFilesContent = {
+ // Passed in values, use these.
+ content: curr.content,
+ filename: curr.filename,
+ // Leave these empty, not needed for a local, static model.
+ language: '',
+ raw_url: '',
+ size: 0,
+ truncated: false,
+ type: '',
+ };
+ return {
+ ...prev,
+ [curr.filename]: file,
+ };
+ }, {});
return new NgxGist({
+ // Properties with settable values. These are the mimimum values needed
+ // for displaying non "remote".
+ created_at: args.created_at ? new Date(args.created_at) : new Date(),
+ description: args.description ?? '',
+ files,
+ languageOverride: args.languageOverride,
+ // Empty properties that aren't needed for displaying non "remote" gists
comments: 0,
comments_url: '',
commits_url: '',
- created_at: new Date(args.created_at),
- description: args.description,
- files: args.files,
forks: [],
forks_url: '',
git_pull_url: '',
@@ -88,7 +132,7 @@ export class NgxGist implements Gist {
}
/**
- * Deserialize and decode fetched/unkown data into the full model.
+ * Deserialize and decode fetched/unknown data into the full model.
*
* @param value Unknown value, but expects a full model either by object or JSON string.
* @returns Either the full model or null if deserialization fails.
@@ -102,15 +146,40 @@ export class NgxGist implements Gist {
? decodeValueElseNull(gistFromJsonStringCodec)(value)
: null);
return decoded
- ? {
+ ? new NgxGist({
...decoded,
created_at: new Date(decoded.created_at),
updated_at: new Date(decoded.updated_at),
- }
+ })
: null;
}
}
+type Files = Array>;
+
+type HighlightedFiles = Array;
+
+interface HighlightedContent {
+ highlightedContent: string;
+}
+
+function getHighlightedContent(
+ baseContent: string,
+ languageOverride?: string,
+): string {
+ let highlighted = baseContent;
+
+ if (languageOverride) {
+ highlighted = hljs.highlight(baseContent, {
+ language: languageOverride,
+ }).value;
+ } else {
+ highlighted = hljs.highlightAuto(baseContent).value;
+ }
+
+ return highlighted;
+}
+
const gitHubUserCodec = io.readonly(
io.type({
avatar_url: io.string,
@@ -155,21 +224,18 @@ const gistHistoryCodec = io.readonly(
'GistHistory',
);
-const gistFilesCodec = io.readonly(
- io.record(
- io.string,
- io.type({
- content: io.string,
- filename: io.string,
- language: io.string,
- raw_url: io.string,
- size: io.number,
- truncated: io.boolean,
- type: io.string,
- }),
- ),
- 'GistFiles',
-);
+const gistFilesContent = io.type({
+ content: io.string,
+ filename: io.string,
+ language: io.string,
+ raw_url: io.string,
+ size: io.number,
+ truncated: io.boolean,
+ type: io.string,
+});
+type GistFilesContent = io.TypeOf;
+
+const gistFilesCodec = io.record(io.string, gistFilesContent);
export const gistCodec = io.readonly(
io.intersection([
@@ -207,7 +273,7 @@ const gistFromJsonStringCodec = parsedJsonFromStringCodec.pipe(
);
/**
- * Official Gist objecio.
+ * Official Gist object
* https://docs.github.com/en/rest/gists
*/
type Gist = io.TypeOf;
diff --git a/src/app/public/ngx-gist.module.ts b/src/app/public/ngx-gist.module.ts
index 5ba1f08..ee63c07 100644
--- a/src/app/public/ngx-gist.module.ts
+++ b/src/app/public/ngx-gist.module.ts
@@ -4,12 +4,13 @@ import { NgxGistComponent } from './ngx-gist.component';
import { NgxGistService } from './ngx-gist.service';
import { MatCardModule } from '@angular/material/card';
import { MatTabsModule } from '@angular/material/tabs';
-import { GistContentPipe } from './ngx-gist-content.pipe';
import { GistFileFilterPipe } from './ngx-gist-file-filter.pipe';
import { MatIconModule } from '@angular/material/icon';
+import { NgxGistLineNumbersService } from './ngx-gist-line-numbers.service';
+import { NgxGistThemeService } from './ngx-gist-theme.service';
@NgModule({
- declarations: [NgxGistComponent, GistContentPipe, GistFileFilterPipe],
+ declarations: [NgxGistComponent, GistFileFilterPipe],
imports: [
// Needs to be imported at root.
// BrowserAnimationsModule,
@@ -21,6 +22,6 @@ import { MatIconModule } from '@angular/material/icon';
MatTabsModule,
],
exports: [NgxGistComponent],
- providers: [NgxGistService],
+ providers: [NgxGistLineNumbersService, NgxGistService, NgxGistThemeService],
})
export class NgxGistModule {}
diff --git a/src/app/public/ngx-gist.service.ts b/src/app/public/ngx-gist.service.ts
index d58722b..5b325c1 100644
--- a/src/app/public/ngx-gist.service.ts
+++ b/src/app/public/ngx-gist.service.ts
@@ -61,11 +61,11 @@ export class NgxGistService {
const gist = storedGist.value;
// All is good, return unexpired gist
- return {
+ return new NgxGist({
...gist,
created_at: new Date(gist.created_at),
updated_at: new Date(gist.updated_at),
- };
+ });
}
public setToCache(gist: NgxGist, expiresInMin?: number): void {
diff --git a/src/app/public/public.ts b/src/app/public/public.ts
index 9616528..3081920 100644
--- a/src/app/public/public.ts
+++ b/src/app/public/public.ts
@@ -1,9 +1,5 @@
/** Public API Exports for Node Package */
-export * from './ngx-gist-content.pipe';
-export * from './ngx-gist-file-filter.pipe';
export * from './ngx-gist.component';
export * from './ngx-gist.model';
export * from './ngx-gist.module';
-export * from './ngx-gist.service';
-export * from './ngx-gist.utilities';
diff --git a/src/assets/images/git-hub.svg b/src/assets/images/git-hub.svg
new file mode 100644
index 0000000..232e834
--- /dev/null
+++ b/src/assets/images/git-hub.svg
@@ -0,0 +1,35 @@
+
+
+
+ GitHub Icon
+
+
+
diff --git a/src/styles.scss b/src/styles.scss
index 3542910..906298e 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -15,4 +15,5 @@
body {
padding: 0;
margin: 0;
+ background-color: #faf9f6;
}