Skip to content

Commit 93b0f70

Browse files
authored
Merge pull request #12 from Lissy93/fix/vercel2
fix: Update Angular Analog, ignore mermaid in server
2 parents b045fce + b32b45c commit 93b0f70

File tree

10 files changed

+1223
-3083
lines changed

10 files changed

+1223
-3083
lines changed

package-lock.json

+1,012-3,003
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "domain-locker",
3-
"version": "0.0.4",
3+
"version": "0.0.5",
44
"type": "module",
55
"engines": {
66
"node": ">=22.13.0"
@@ -15,18 +15,18 @@
1515
"build": "ng build",
1616
"build:vercel": "NITRO_PRESET=vercel ng build --configuration production",
1717
"build:deno": "NITRO_PRESET=deno ng build --configuration production",
18-
"build:netlify": "NITRO_PRESET=netlify NODE_OPTIONS=--max-old-space-size=8096 ng build --configuration production",
18+
"build:netlify": "NITRO_PRESET=netlify NODE_OPTIONS=--max-old-space-size=4096 ng build --configuration production",
1919
"watch": "ng build --watch --configuration development",
2020
"test": "ng test",
2121
"lint": "ng lint"
2222
},
2323
"private": true,
2424
"dependencies": {
25-
"@analogjs/content": "^1.14.1-beta.5",
26-
"@analogjs/platform": "^1.14.1-beta.5",
27-
"@analogjs/router": "^1.14.1-beta.5",
28-
"@analogjs/vite-plugin-angular": "^1.14.1-beta.5",
29-
"@analogjs/vitest-angular": "^1.14.1-beta.5",
25+
"@analogjs/content": "^1.14.1-beta.6",
26+
"@analogjs/platform": "^1.14.1-beta.6",
27+
"@analogjs/router": "^1.14.1-beta.6",
28+
"@analogjs/vite-plugin-angular": "^1.14.1-beta.6",
29+
"@analogjs/vitest-angular": "^1.14.1-beta.6",
3030
"@angular/animations": "^19.2.2",
3131
"@angular/cdk": "^18.2.9",
3232
"@angular/common": "^19.2.2",

src/app/app.config.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
22
ApplicationConfig, importProvidersFrom,
3-
APP_INITIALIZER, PLATFORM_ID,
3+
APP_INITIALIZER, PLATFORM_ID,
44
APP_ID} from '@angular/core';
55
// Importing providers
66
import { HTTP_INTERCEPTORS, provideHttpClient, withFetch, withInterceptorsFromDi } from '@angular/common/http';
@@ -34,7 +34,7 @@ import { EnvLoaderService } from './utils/env.loader';
3434
export const appConfig: ApplicationConfig = {
3535
providers: [
3636
{ provide: APP_ID, useValue: 'domain-locker' },
37-
37+
3838
// Transfer relevant env vars on self-hosted version
3939
{
4040
provide: APP_INITIALIZER,
@@ -46,12 +46,12 @@ export const appConfig: ApplicationConfig = {
4646
provideHttpClient(withFetch()),
4747
provideClientHydration(),
4848
provideContent(
49-
withMarkdownRenderer({ loadMermaid: () => import('mermaid') }),
49+
withMarkdownRenderer(),
5050
withShikiHighlighter(),
5151
),
5252
provideAnimations(),
5353
provideFileRouter(
54-
withInMemoryScrolling({ anchorScrolling: 'enabled' }),
54+
withInMemoryScrolling({scrollPositionRestoration: 'enabled'}),
5555
withEnabledBlockingInitialNavigation(),
5656
),
5757
// HTTP Interceptors
@@ -60,7 +60,7 @@ export const appConfig: ApplicationConfig = {
6060
withInterceptorsFromDi()
6161
),
6262
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
63-
63+
6464
// PrimeNG Services
6565
ConfirmationService,
6666
MessageService,

src/app/components/about-things/doc-viewer.component.ts

+147-33
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { Component, Input, HostListener, ViewEncapsulation } from '@angular/core';
1+
import { Component, Input, HostListener, ViewEncapsulation, PLATFORM_ID, Inject } from '@angular/core';
22
import { ContentFile } from '@analogjs/content';
3-
import { Observable } from 'rxjs';
3+
import { filter, Observable, Subscription } from 'rxjs';
44
import { MarkdownComponent } from '@analogjs/content';
55
import { PrimeNgModule } from '~/app/prime-ng.module';
66
import { MetaTagsService } from '~/app/services/meta-tags.service';
7-
import { CommonModule, NgIf } from '@angular/common';
7+
import { CommonModule, isPlatformBrowser, NgIf } from '@angular/common';
8+
import { NavigationEnd, Router } from '@angular/router';
9+
import { ErrorHandlerService } from '~/app/services/error-handler.service';
810

911
export interface DocAttributes {
1012
title: string;
@@ -88,48 +90,160 @@ export class DocsViewerComponent {
8890

8991
doc: ContentFile<DocAttributes | Record<string, never>> | null = null;
9092

91-
navTop = 'unset'; // default top offset
93+
navTop = 'unset';
94+
private docSub?: Subscription;
95+
private routerSub?: Subscription;
96+
private docLoaded = false;
97+
private hasRenderedMermaid = false;
9298

93-
constructor(private metaTagsService: MetaTagsService) {}
99+
constructor(
100+
private router: Router,
101+
private errorHandler: ErrorHandlerService,
102+
private metaTagsService: MetaTagsService,
103+
@Inject(PLATFORM_ID) private platformId: Object
104+
) {}
94105

95106
ngOnInit() {
96-
this.doc$.subscribe(doc => {
97-
// Set current doc when it resolves
98-
this.doc = doc;
99-
// If doc has attributes, then get them for meta and JSON-LD content
100-
if (doc?.attributes) {
101-
const { title, description, coverImage, author, publishedDate, modifiedDate, slug } = doc.attributes;
102-
103-
// Set meta tags
104-
this.metaTagsService.setCustomMeta(title, description, undefined, coverImage || this.getFallbackImage(title));
105-
106-
// Set JSON-LD structured data
107-
this.metaTagsService.addStructuredData('article', {
108-
title: title,
109-
description: description,
110-
coverImage: coverImage || this.getFallbackImage(title),
111-
author: author || 'Domain Locker Team',
112-
publishedDate: publishedDate || new Date().toISOString(),
113-
modifiedDate: modifiedDate || publishedDate || new Date().toISOString(),
114-
slug: slug,
115-
category: this.categoryName,
116-
});
107+
this.docSub = this.doc$.subscribe({
108+
next: (doc) => {
109+
// Set current doc when it resolves
110+
this.doc = doc;
111+
// If doc has attributes, then get them for meta and JSON-LD content
112+
if (doc?.attributes) {
113+
const { title, description, coverImage, author, publishedDate, modifiedDate, slug } = doc.attributes;
114+
115+
// Set meta tags
116+
this.metaTagsService.setCustomMeta(title, description, undefined, coverImage || this.getFallbackImage(title));
117+
118+
// Set JSON-LD structured data
119+
this.metaTagsService.addStructuredData('article', {
120+
title: title,
121+
description: description,
122+
coverImage: coverImage || this.getFallbackImage(title),
123+
author: author || 'Domain Locker Team',
124+
publishedDate: publishedDate || new Date().toISOString(),
125+
modifiedDate: modifiedDate || publishedDate || new Date().toISOString(),
126+
slug: slug,
127+
category: this.categoryName,
128+
});
129+
}
130+
if (isPlatformBrowser(this.platformId)) {
131+
setTimeout(() => {
132+
this.loadAndRenderMermaid();
133+
}, 50);
134+
}
135+
this.docLoaded = true;
136+
},
137+
error: (err) => {
138+
this.errorHandler.handleError({ error: err, message: 'Doc subscription error', location: 'doc-viewer' });
117139
}
118140
});
141+
142+
this.routerSub = this.router.events
143+
.pipe(filter((e) => e instanceof NavigationEnd))
144+
.subscribe({
145+
next: () => {
146+
if (isPlatformBrowser(this.platformId)) {
147+
setTimeout(() => this.loadAndRenderMermaid(), 50);
148+
}
149+
},
150+
error: (err) => {
151+
this.errorHandler.handleError({ error: err, message: 'Router events error', location: 'doc-viewer' });
152+
}
153+
});
154+
}
155+
156+
ngOnDestroy(): void {
157+
if (this.docSub) {
158+
try {
159+
this.docSub.unsubscribe();
160+
} catch (err) {
161+
this.errorHandler.handleError({ error: err, message: 'Doc subscription cleanup error', location: 'doc-viewer' });
162+
}
163+
}
164+
if (this.routerSub) {
165+
try {
166+
this.routerSub.unsubscribe();
167+
} catch (err) {
168+
this.errorHandler.handleError({ error: err, message: 'Router subscription cleanup error', location: 'doc-viewer' });
169+
}
170+
}
171+
}
172+
173+
ngAfterViewChecked(): void {
174+
// If running client-side, and doc is loaded but no mermaid rendered yet, then init
175+
if (!isPlatformBrowser(this.platformId)) return;
176+
if (this.docLoaded && !this.hasRenderedMermaid) {
177+
this.loadAndRenderMermaid();
178+
this.hasRenderedMermaid = true;
179+
}
119180
}
120181

121182
/** Called on window scroll. If user scrolled > 7rem => fix nav top at 7rem. Otherwise 0. */
122183
@HostListener('window:scroll')
123184
onWindowScroll() {
124-
const scrollY = window.scrollY;
125-
const sevenRemInPx = 112; // approx 7rem if root font-size = 16px
126-
this.navTop = scrollY > sevenRemInPx ? '1rem' : '9rem';
185+
try {
186+
const scrollY = window.scrollY;
187+
const sevenRemInPx = 112; // approx 7rem if root font-size = 16px
188+
this.navTop = scrollY > sevenRemInPx ? '1rem' : '9rem';
189+
} catch (err) {
190+
this.errorHandler.handleError({ error: err, message: 'Scroll handler error', location: 'doc-viewer' });
191+
}
127192
}
128193

129194
getFallbackImage(title: string) {
130-
const encodedTitle = encodeURIComponent(title);
131-
return `https://dynamic-og-image-generator.vercel.app/api/generate?title=${encodedTitle}`
132-
+ ' &author=Domain+Locker&websiteUrl=domain-locker.com&avatar=https%3A%2F%2Fdomain-locker'
133-
+ '.com%2Ficons%2Fandroid-chrome-maskable-192x192.png&theme=dracula';
195+
try {
196+
const encodedTitle = encodeURIComponent(title);
197+
return `https://dynamic-og-image-generator.vercel.app/api/generate?title=${encodedTitle}`
198+
+ ' &author=Domain+Locker&websiteUrl=domain-locker.com&avatar=https%3A%2F%2Fdomain-locker'
199+
+ '.com%2Ficons%2Fandroid-chrome-maskable-192x192.png&theme=dracula';
200+
} catch (err) {
201+
this.errorHandler.handleError({ error: err, message: 'Fallback image error', location: 'doc-viewer' });
202+
return 'https://domain-locker.com/og.png';
203+
}
204+
}
205+
206+
/**
207+
* 1) Checks for any <pre class="mermaid"> blocks
208+
* 2) If found, dynamically load mermaid from a CDN
209+
* 3) Then call mermaid.initialize + mermaid.run
210+
*/
211+
private loadAndRenderMermaid() {
212+
try {
213+
const mermaidBlocks = document.querySelectorAll('pre.mermaid');
214+
if (!mermaidBlocks?.length) {
215+
return;
216+
}
217+
const existingScript = document.getElementById('mermaidScript') as HTMLScriptElement | null;
218+
if (existingScript) {
219+
this.runMermaid();
220+
} else {
221+
const script = document.createElement('script');
222+
script.id = 'mermaidScript';
223+
script.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
224+
script.async = true;
225+
script.onload = () => {
226+
this.runMermaid();
227+
};
228+
document.head.appendChild(script);
229+
}
230+
} catch (err) {
231+
this.errorHandler.handleError({ error: err, message: 'loadAndRenderMermaid error', location: 'doc-viewer' });
232+
}
233+
}
234+
235+
private runMermaid() {
236+
try {
237+
const mermaid = (window as any).mermaid;
238+
if (!mermaid) return;
239+
try {
240+
mermaid.initialize({ startOnLoad: false });
241+
mermaid.run({ querySelector: 'pre.mermaid' });
242+
} catch (err) {
243+
this.errorHandler.handleError({ error: err, message: 'Mermaid render failed', location: 'doc-viewer' });
244+
}
245+
} catch (err) {
246+
this.errorHandler.handleError({ error: err, message: 'runMermaid error', location: 'doc-viewer' });
247+
}
134248
}
135249
}

src/app/components/domain-things/domain-collection/domain-collection.component.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
1+
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import Fuse from 'fuse.js';
44
import { DomainCardComponent } from '~/app/components/domain-things/domain-card/domain-card.component';
@@ -59,6 +59,10 @@ export class DomainCollectionComponent implements OnInit {
5959

6060
visibleColumns: any[] = [];
6161

62+
constructor(
63+
private cdr: ChangeDetectorRef,
64+
) {}
65+
6266
ngOnInit() {
6367
this.filteredDomains = this.domains;
6468
this.sortDomains();
@@ -136,7 +140,8 @@ export class DomainCollectionComponent implements OnInit {
136140
reloadDomains(event: any) {
137141
setTimeout(() => {
138142
this.$triggerReload.emit(event);
139-
}, 1500);
143+
this.cdr.detectChanges();
144+
}, 1000);
140145
}
141146

142147
initializeFuse() {

src/app/components/domain-things/domain-filters/domain-filters.component.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
<p-multiSelect
1515
id="field-filters"
1616
[options]="fieldOptions"
17-
[(ngModel)]="selectedFields"
17+
[(ngModel)]="selectedFieldsList"
1818
(onChange)="onSelectionChange()"
1919
optionLabel="label"
2020
selectedItemsLabel="{0} fields shown"
2121
[style]="{minWidth: '200px'}"
2222
placeholder="Choose visible fields"
23-
></p-multiSelect>
23+
/>
2424
</div>
2525
<!-- Sort Select Dropdown -->
2626
<div class="flex flex-col gap-1" *ngIf="selectedLayout">

src/app/components/domain-things/domain-filters/domain-filters.component.ts

+5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export class FieldVisibilityFilterComponent implements OnInit {
5050
@Output() $triggerReload = new EventEmitter();
5151

5252
selectedFields: FieldOption[] = [];
53+
selectedFieldsList: string[] = [];
5354
sortOrder: FieldOption = this.sortOptions[0];
5455
selectedLayout: boolean = true;
5556
quickAddDialogOpen: boolean = false;
@@ -95,13 +96,17 @@ export class FieldVisibilityFilterComponent implements OnInit {
9596
this.selectedFields = this.fieldOptions.filter(option =>
9697
this.defaultSelectedFields.includes(option.value)
9798
);
99+
this.selectedFieldsList = this.selectedFields.map(field => field.value);
98100
this.onSelectionChange();
99101
}
100102

101103
onSelectionChange() {
102104
if (this.selectedFields.length === 0) {
103105
this.initializeSelectedFields();
104106
}
107+
this.selectedFields = this.fieldOptions.filter(option =>
108+
this.selectedFieldsList.includes(option.value)
109+
);
105110
this.visibilityChange.emit(this.selectedFields);
106111
}
107112

src/app/services/db-query-services/sb/db-history.service.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ export class HistoryQueries {
6363
.from('domain_updates')
6464
.select('id', { count: 'exact' });
6565

66-
// if (domainName) {
67-
// query = this.supabase
68-
// .from('domain_updates')
69-
// .select('id, domains!inner(domain_name)', { count: 'exact' })
70-
// .eq('domains.domain_name', domainName);
71-
// }
66+
if (domainName) {
67+
query = this.supabase
68+
.from<any, any>('domain_updates')
69+
.select('id, domains!inner(domain_name)', { count: 'exact' })
70+
.eq('domains.domain_name', domainName);
71+
}
7272

7373
return from(query.then(({ count, error }: { count: number | null; error: any }) => {
7474
if (error) throw error;

src/app/services/error-handler.service.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,7 @@ export class ErrorHandlerService {
141141
if (!error && !message) return; // Not much I can do without an error or message!
142142

143143
// Log to console in development mode
144-
if (isDevMode()) {
145-
this.printToConsole(message, location, error);
146-
}
144+
this.printToConsole(message, location, error);
147145

148146
// Show error toast if showError is true
149147
if (showToast && message && error) {

0 commit comments

Comments
 (0)