fontastic 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +29 -0
  3. package/README.md +6 -0
  4. package/app/Application.js +4 -4
  5. package/app/Application.ts +13 -13
  6. package/app/config/database.js +1 -1
  7. package/app/config/database.ts +1 -1
  8. package/app/config/mimes.js +23 -23
  9. package/app/config/mimes.ts +35 -29
  10. package/app/core/ConfigManager.js +27 -0
  11. package/app/core/ConfigManager.ts +28 -0
  12. package/app/core/FontFinder.js +66 -15
  13. package/app/core/FontFinder.ts +81 -22
  14. package/app/core/FontManager.js +20 -18
  15. package/app/core/FontManager.ts +21 -19
  16. package/app/core/FontObject.js +44 -24
  17. package/app/core/FontObject.ts +47 -27
  18. package/app/core/MessageHandler.js +70 -19
  19. package/app/core/MessageHandler.ts +82 -21
  20. package/app/core/SystemManager.js +5 -1
  21. package/app/core/SystemManager.ts +5 -1
  22. package/app/database/entity/Collection.schema.js +20 -18
  23. package/app/database/entity/Collection.schema.ts +22 -21
  24. package/app/database/repository/Collection.repository.js +17 -18
  25. package/app/database/repository/Collection.repository.ts +27 -18
  26. package/app/database/repository/Store.repository.js +13 -18
  27. package/app/database/repository/Store.repository.ts +13 -21
  28. package/app/enums/ChannelType.js +18 -0
  29. package/app/enums/ChannelType.ts +24 -0
  30. package/app/main.js +79 -2
  31. package/app/main.ts +100 -3
  32. package/app/package.json +1 -1
  33. package/app/types/NativeThemeState.js +3 -0
  34. package/app/types/NativeThemeState.ts +4 -0
  35. package/app/types/ScanProgress.js +3 -0
  36. package/app/types/ScanProgress.ts +6 -0
  37. package/app/types/SystemPreferencesState.js +3 -0
  38. package/app/types/SystemPreferencesState.ts +4 -0
  39. package/app/types/index.js +3 -0
  40. package/app/types/index.ts +3 -0
  41. package/package.json +1 -1
  42. package/src/app/core/services/database/database.service.ts +6 -0
  43. package/src/app/core/services/message/message.service.ts +33 -1
  44. package/src/app/core/services/presentation/presentation.service.ts +93 -1
  45. package/src/app/layout/footer/footer.component.html +13 -2
  46. package/src/app/layout/footer/footer.component.ts +18 -2
  47. package/src/app/layout/navigation/navigation.component.html +11 -9
  48. package/src/app/layout/navigation/navigation.component.ts +35 -0
  49. package/src/app/settings/ai-keys/ai-keys.component.ts +13 -18
  50. package/src/app/settings/danger-zone/danger-zone.component.html +8 -0
  51. package/src/app/settings/danger-zone/danger-zone.component.ts +12 -0
  52. package/src/app/settings/news-api/news-api.component.ts +6 -8
  53. package/src/app/settings/theme/theme.component.html +15 -2
  54. package/src/app/settings/theme/theme.component.ts +4 -0
  55. package/src/app/shared/components/datagrid/datagrid.component.html +8 -17
  56. package/src/app/shared/components/datagrid/datagrid.component.ts +6 -10
  57. package/src/app/shared/components/glyphs/glyphs.component.html +5 -15
  58. package/src/app/shared/components/glyphs/glyphs.component.ts +3 -0
  59. package/src/app/shared/components/preview/preview.component.html +1 -1
  60. package/src/app/shared/components/preview/preview.component.ts +3 -8
  61. package/src/app/shared/components/prompt-dialog/prompt-dialog.component.html +2 -2
  62. package/src/app/shared/components/prompt-dialog/prompt-dialog.component.ts +2 -1
  63. package/src/app/shared/components/rule-builder/rule-builder.component.html +18 -6
  64. package/src/app/shared/components/rule-builder/rule-builder.component.ts +34 -2
  65. package/src/app/shared/components/search/search.component.html +9 -36
  66. package/src/app/shared/components/search/search.component.ts +2 -1
  67. package/src/app/shared/components/waterfall/waterfall.component.html +1 -3
  68. package/src/app/shared/components/waterfall/waterfall.component.ts +2 -1
  69. package/src/app/shared/directives/disabled-opacity/disabled-opacity.directive.ts +18 -0
  70. package/src/app/shared/directives/hover-highlight/hover-highlight.directive.ts +38 -0
  71. package/src/app/shared/directives/index.ts +5 -0
  72. package/src/app/shared/directives/modal-backdrop/modal-backdrop.directive.ts +18 -0
  73. package/src/app/shared/directives/scroll-reset/scroll-reset.directive.ts +15 -0
  74. package/src/app/shared/directives/stop-propagation/stop-propagation.directive.ts +12 -0
  75. package/src/index.html +1 -1
@@ -26,9 +26,7 @@
26
26
  <button
27
27
  class="px-2 py-0.5 rounded cursor-pointer text-[10px] transition-colors"
28
28
  [style.color]="'var(--text-secondary)'"
29
- [style.background-color]="'transparent'"
30
- (mouseenter)="$any($event.currentTarget).style.backgroundColor = 'var(--hover-bg)'"
31
- (mouseleave)="$any($event.currentTarget).style.backgroundColor = 'transparent'"
29
+ [appHoverHighlight]="false"
32
30
  (click)="onReset()"
33
31
  >
34
32
  Clear
@@ -64,15 +62,7 @@
64
62
  <label
65
63
  class="flex items-center gap-2 px-2 py-1 rounded cursor-pointer text-[11px] transition-colors"
66
64
  [style.color]="'var(--text-secondary)'"
67
- [style.background-color]="isSearchFieldSelected(sf.value) ? 'var(--selected-bg)' : 'transparent'"
68
- (mouseenter)="
69
- $any($event.currentTarget).style.backgroundColor = isSearchFieldSelected(sf.value)
70
- ? 'var(--selected-bg)'
71
- : 'var(--hover-bg)'
72
- "
73
- (mouseleave)="
74
- $any($event.currentTarget).style.backgroundColor = isSearchFieldSelected(sf.value) ? 'var(--selected-bg)' : 'transparent'
75
- "
65
+ [appHoverHighlight]="isSearchFieldSelected(sf.value)"
76
66
  >
77
67
  <input
78
68
  type="checkbox"
@@ -96,15 +86,7 @@
96
86
  <label
97
87
  class="flex items-center gap-2 px-2 py-1 rounded cursor-pointer text-[11px] transition-colors"
98
88
  [style.color]="'var(--text-secondary)'"
99
- [style.background-color]="isFileTypeSelected(ft.value) ? 'var(--selected-bg)' : 'transparent'"
100
- (mouseenter)="
101
- $any($event.currentTarget).style.backgroundColor = isFileTypeSelected(ft.value)
102
- ? 'var(--selected-bg)'
103
- : 'var(--hover-bg)'
104
- "
105
- (mouseleave)="
106
- $any($event.currentTarget).style.backgroundColor = isFileTypeSelected(ft.value) ? 'var(--selected-bg)' : 'transparent'
107
- "
89
+ [appHoverHighlight]="isFileTypeSelected(ft.value)"
108
90
  >
109
91
  <input
110
92
  type="checkbox"
@@ -125,9 +107,7 @@
125
107
  <label
126
108
  class="flex items-center gap-2 px-2 py-1 rounded cursor-pointer text-[11px] transition-colors"
127
109
  [style.color]="'var(--text-secondary)'"
128
- [style.background-color]="favoritesOnly ? 'var(--selected-bg)' : 'transparent'"
129
- (mouseenter)="$any($event.currentTarget).style.backgroundColor = favoritesOnly ? 'var(--selected-bg)' : 'var(--hover-bg)'"
130
- (mouseleave)="$any($event.currentTarget).style.backgroundColor = favoritesOnly ? 'var(--selected-bg)' : 'transparent'"
110
+ [appHoverHighlight]="favoritesOnly"
131
111
  >
132
112
  <input type="checkbox" class="accent-current" [(ngModel)]="favoritesOnly" />
133
113
  Favorites Only
@@ -135,9 +115,7 @@
135
115
  <label
136
116
  class="flex items-center gap-2 px-2 py-1 rounded cursor-pointer text-[11px] transition-colors"
137
117
  [style.color]="'var(--text-secondary)'"
138
- [style.background-color]="systemOnly ? 'var(--selected-bg)' : 'transparent'"
139
- (mouseenter)="$any($event.currentTarget).style.backgroundColor = systemOnly ? 'var(--selected-bg)' : 'var(--hover-bg)'"
140
- (mouseleave)="$any($event.currentTarget).style.backgroundColor = systemOnly ? 'var(--selected-bg)' : 'transparent'"
118
+ [appHoverHighlight]="systemOnly"
141
119
  >
142
120
  <input type="checkbox" class="accent-current" [(ngModel)]="systemOnly" />
143
121
  System Fonts Only
@@ -145,9 +123,7 @@
145
123
  <label
146
124
  class="flex items-center gap-2 px-2 py-1 rounded cursor-pointer text-[11px] transition-colors"
147
125
  [style.color]="'var(--text-secondary)'"
148
- [style.background-color]="installedOnly ? 'var(--selected-bg)' : 'transparent'"
149
- (mouseenter)="$any($event.currentTarget).style.backgroundColor = installedOnly ? 'var(--selected-bg)' : 'var(--hover-bg)'"
150
- (mouseleave)="$any($event.currentTarget).style.backgroundColor = installedOnly ? 'var(--selected-bg)' : 'transparent'"
126
+ [appHoverHighlight]="installedOnly"
151
127
  >
152
128
  <input type="checkbox" class="accent-current" [(ngModel)]="installedOnly" />
153
129
  Installed Only
@@ -292,11 +268,10 @@
292
268
  <div class="flex gap-2 pt-5">
293
269
  <button
294
270
  class="flex-1 px-3 py-1.5 rounded text-[11px] font-medium cursor-pointer transition-colors"
295
- [style.background-color]="'var(--selected-bg)'"
296
271
  [style.color]="'var(--text-primary)'"
297
272
  [style.border]="'1px solid var(--border-default)'"
298
- (mouseenter)="$any($event.currentTarget).style.backgroundColor = 'var(--hover-bg)'"
299
- (mouseleave)="$any($event.currentTarget).style.backgroundColor = 'var(--selected-bg)'"
273
+ [appHoverHighlight]="false"
274
+ normalBg="var(--selected-bg)"
300
275
  (click)="onSearch()"
301
276
  >
302
277
  <span class="flex items-center justify-center gap-1">
@@ -316,9 +291,7 @@
316
291
  class="px-3 py-1.5 rounded text-[11px] cursor-pointer transition-colors"
317
292
  [style.color]="'var(--text-muted)'"
318
293
  [style.border]="'1px solid var(--border-subtle)'"
319
- [style.background-color]="'transparent'"
320
- (mouseenter)="$any($event.currentTarget).style.backgroundColor = 'var(--hover-bg)'"
321
- (mouseleave)="$any($event.currentTarget).style.backgroundColor = 'transparent'"
294
+ [appHoverHighlight]="false"
322
295
  (click)="onReset()"
323
296
  >
324
297
  Reset
@@ -1,6 +1,7 @@
1
1
  import { Component, inject, signal } from '@angular/core';
2
2
  import { FormsModule } from '@angular/forms';
3
3
  import { DatabaseService, PresentationService } from '../../../core/services';
4
+ import { HoverHighlightDirective } from '../../directives/hover-highlight/hover-highlight.directive';
4
5
  import type { Collection } from '@main/database/entity/Collection.schema';
5
6
 
6
7
  const FILE_TYPES = [
@@ -45,7 +46,7 @@ const SORT_OPTIONS = [
45
46
  @Component({
46
47
  selector: 'app-search',
47
48
  standalone: true,
48
- imports: [FormsModule],
49
+ imports: [FormsModule, HoverHighlightDirective],
49
50
  templateUrl: './search.component.html',
50
51
  })
51
52
  export class SearchComponent {
@@ -67,9 +67,7 @@
67
67
  class="px-2 py-0.5 rounded cursor-pointer text-[10px] transition-colors"
68
68
  [style.color]="'var(--text-secondary)'"
69
69
  [style.border]="'1px solid var(--border-default)'"
70
- [style.background-color]="'transparent'"
71
- (mouseenter)="$any($event.currentTarget).style.backgroundColor = 'var(--hover-bg)'"
72
- (mouseleave)="$any($event.currentTarget).style.backgroundColor = 'transparent'"
70
+ [appHoverHighlight]="false"
73
71
  (click)="resetScale()"
74
72
  title="Reset scale"
75
73
  >
@@ -1,6 +1,7 @@
1
1
  import { Component, inject, computed, signal } from '@angular/core';
2
2
  import { FormsModule } from '@angular/forms';
3
3
  import { DatabaseService, PresentationService } from '../../../core/services';
4
+ import { HoverHighlightDirective } from '../../directives/hover-highlight/hover-highlight.directive';
4
5
  import { fontTypeScale } from '@main/config/system';
5
6
 
6
7
  interface ScaleItem {
@@ -14,7 +15,7 @@ interface ScaleItem {
14
15
  @Component({
15
16
  selector: 'app-waterfall',
16
17
  standalone: true,
17
- imports: [FormsModule],
18
+ imports: [FormsModule, HoverHighlightDirective],
18
19
  templateUrl: './waterfall.component.html',
19
20
  })
20
21
  export class WaterfallComponent {
@@ -0,0 +1,18 @@
1
+ import { Directive, ElementRef, Input, OnChanges, inject } from '@angular/core';
2
+
3
+ @Directive({
4
+ selector: '[appDisabledOpacity]',
5
+ standalone: true,
6
+ })
7
+ export class DisabledOpacityDirective implements OnChanges {
8
+ @Input('appDisabledOpacity') isDisabled = false;
9
+ @Input() opacityValue = '0.3';
10
+
11
+ private el = inject(ElementRef);
12
+
13
+ ngOnChanges() {
14
+ const el = this.el.nativeElement;
15
+ el.disabled = this.isDisabled;
16
+ el.style.opacity = this.isDisabled ? this.opacityValue : '';
17
+ }
18
+ }
@@ -0,0 +1,38 @@
1
+ import { Directive, ElementRef, HostListener, Input, OnChanges, inject } from '@angular/core';
2
+
3
+ @Directive({
4
+ selector: '[appHoverHighlight]',
5
+ standalone: true,
6
+ })
7
+ export class HoverHighlightDirective implements OnChanges {
8
+ @Input('appHoverHighlight') selected = false;
9
+ @Input() hoverBg = 'var(--hover-bg)';
10
+ @Input() selectedBg = 'var(--selected-bg)';
11
+ @Input() normalBg = 'transparent';
12
+ @Input() selectedColor = '';
13
+ @Input() normalColor = '';
14
+
15
+ private el = inject(ElementRef);
16
+
17
+ ngOnChanges() {
18
+ this.applyStyles();
19
+ }
20
+
21
+ @HostListener('mouseenter')
22
+ onMouseEnter() {
23
+ this.el.nativeElement.style.backgroundColor = this.selected ? this.selectedBg : this.hoverBg;
24
+ }
25
+
26
+ @HostListener('mouseleave')
27
+ onMouseLeave() {
28
+ this.applyStyles();
29
+ }
30
+
31
+ private applyStyles() {
32
+ const el = this.el.nativeElement;
33
+ el.style.backgroundColor = this.selected ? this.selectedBg : this.normalBg;
34
+ if (this.selectedColor || this.normalColor) {
35
+ el.style.color = this.selected ? this.selectedColor : this.normalColor;
36
+ }
37
+ }
38
+ }
@@ -1,2 +1,7 @@
1
1
  export * from './autofocus/autofocus.directive';
2
+ export * from './disabled-opacity/disabled-opacity.directive';
3
+ export * from './hover-highlight/hover-highlight.directive';
4
+ export * from './modal-backdrop/modal-backdrop.directive';
5
+ export * from './scroll-reset/scroll-reset.directive';
6
+ export * from './stop-propagation/stop-propagation.directive';
2
7
  export * from './webview/webview.directive';
@@ -0,0 +1,18 @@
1
+ import { Directive, ElementRef, HostListener, Output, EventEmitter, inject } from '@angular/core';
2
+
3
+ @Directive({
4
+ selector: '[appModalBackdrop]',
5
+ standalone: true,
6
+ })
7
+ export class ModalBackdropDirective {
8
+ @Output() backdropClick = new EventEmitter<void>();
9
+
10
+ private el = inject(ElementRef);
11
+
12
+ @HostListener('click', ['$event'])
13
+ onClick(event: MouseEvent) {
14
+ if (event.target === this.el.nativeElement) {
15
+ this.backdropClick.emit();
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,15 @@
1
+ import { Directive, ElementRef, Input, OnChanges, inject } from '@angular/core';
2
+
3
+ @Directive({
4
+ selector: '[appScrollReset]',
5
+ standalone: true,
6
+ })
7
+ export class ScrollResetDirective implements OnChanges {
8
+ @Input('appScrollReset') trigger: unknown;
9
+
10
+ private el = inject(ElementRef);
11
+
12
+ ngOnChanges() {
13
+ this.el.nativeElement.scrollTo(0, 0);
14
+ }
15
+ }
@@ -0,0 +1,12 @@
1
+ import { Directive, HostListener } from '@angular/core';
2
+
3
+ @Directive({
4
+ selector: '[appStopPropagation]',
5
+ standalone: true,
6
+ })
7
+ export class StopPropagationDirective {
8
+ @HostListener('click', ['$event'])
9
+ onClick(event: Event) {
10
+ event.stopPropagation();
11
+ }
12
+ }
package/src/index.html CHANGED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <title>Fontastic</title>
6
- <base href="/" />
6
+ <base href="./" />
7
7
 
8
8
  <meta name="viewport" content="width=device-width, initial-scale=1" />
9
9
  <link rel="icon" type="image/x-icon" href="assets/icons/favicon.ico" />