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.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +29 -0
- package/README.md +6 -0
- package/app/Application.js +4 -4
- package/app/Application.ts +13 -13
- package/app/config/database.js +1 -1
- package/app/config/database.ts +1 -1
- package/app/config/mimes.js +23 -23
- package/app/config/mimes.ts +35 -29
- package/app/core/ConfigManager.js +27 -0
- package/app/core/ConfigManager.ts +28 -0
- package/app/core/FontFinder.js +66 -15
- package/app/core/FontFinder.ts +81 -22
- package/app/core/FontManager.js +20 -18
- package/app/core/FontManager.ts +21 -19
- package/app/core/FontObject.js +44 -24
- package/app/core/FontObject.ts +47 -27
- package/app/core/MessageHandler.js +70 -19
- package/app/core/MessageHandler.ts +82 -21
- package/app/core/SystemManager.js +5 -1
- package/app/core/SystemManager.ts +5 -1
- package/app/database/entity/Collection.schema.js +20 -18
- package/app/database/entity/Collection.schema.ts +22 -21
- package/app/database/repository/Collection.repository.js +17 -18
- package/app/database/repository/Collection.repository.ts +27 -18
- package/app/database/repository/Store.repository.js +13 -18
- package/app/database/repository/Store.repository.ts +13 -21
- package/app/enums/ChannelType.js +18 -0
- package/app/enums/ChannelType.ts +24 -0
- package/app/main.js +79 -2
- package/app/main.ts +100 -3
- package/app/package.json +1 -1
- package/app/types/NativeThemeState.js +3 -0
- package/app/types/NativeThemeState.ts +4 -0
- package/app/types/ScanProgress.js +3 -0
- package/app/types/ScanProgress.ts +6 -0
- package/app/types/SystemPreferencesState.js +3 -0
- package/app/types/SystemPreferencesState.ts +4 -0
- package/app/types/index.js +3 -0
- package/app/types/index.ts +3 -0
- package/package.json +1 -1
- package/src/app/core/services/database/database.service.ts +6 -0
- package/src/app/core/services/message/message.service.ts +33 -1
- package/src/app/core/services/presentation/presentation.service.ts +93 -1
- package/src/app/layout/footer/footer.component.html +13 -2
- package/src/app/layout/footer/footer.component.ts +18 -2
- package/src/app/layout/navigation/navigation.component.html +11 -9
- package/src/app/layout/navigation/navigation.component.ts +35 -0
- package/src/app/settings/ai-keys/ai-keys.component.ts +13 -18
- package/src/app/settings/danger-zone/danger-zone.component.html +8 -0
- package/src/app/settings/danger-zone/danger-zone.component.ts +12 -0
- package/src/app/settings/news-api/news-api.component.ts +6 -8
- package/src/app/settings/theme/theme.component.html +15 -2
- package/src/app/settings/theme/theme.component.ts +4 -0
- package/src/app/shared/components/datagrid/datagrid.component.html +8 -17
- package/src/app/shared/components/datagrid/datagrid.component.ts +6 -10
- package/src/app/shared/components/glyphs/glyphs.component.html +5 -15
- package/src/app/shared/components/glyphs/glyphs.component.ts +3 -0
- package/src/app/shared/components/preview/preview.component.html +1 -1
- package/src/app/shared/components/preview/preview.component.ts +3 -8
- package/src/app/shared/components/prompt-dialog/prompt-dialog.component.html +2 -2
- package/src/app/shared/components/prompt-dialog/prompt-dialog.component.ts +2 -1
- package/src/app/shared/components/rule-builder/rule-builder.component.html +18 -6
- package/src/app/shared/components/rule-builder/rule-builder.component.ts +34 -2
- package/src/app/shared/components/search/search.component.html +9 -36
- package/src/app/shared/components/search/search.component.ts +2 -1
- package/src/app/shared/components/waterfall/waterfall.component.html +1 -3
- package/src/app/shared/components/waterfall/waterfall.component.ts +2 -1
- package/src/app/shared/directives/disabled-opacity/disabled-opacity.directive.ts +18 -0
- package/src/app/shared/directives/hover-highlight/hover-highlight.directive.ts +38 -0
- package/src/app/shared/directives/index.ts +5 -0
- package/src/app/shared/directives/modal-backdrop/modal-backdrop.directive.ts +18 -0
- package/src/app/shared/directives/scroll-reset/scroll-reset.directive.ts +15 -0
- package/src/app/shared/directives/stop-propagation/stop-propagation.directive.ts +12 -0
- package/src/index.html +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.3.1](https://github.com/tomshaw/fontastic/compare/fontastic-v1.3.0...fontastic-v1.3.1) (2026-03-17)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### 🛠️ Fixes
|
|
7
|
+
|
|
8
|
+
* update base href in index.html for correct path resolution ([2c30779](https://github.com/tomshaw/fontastic/commit/2c307790d64947fbbbf0f3ec057c4994e28ddf13))
|
|
9
|
+
|
|
10
|
+
## [1.3.0](https://github.com/tomshaw/fontastic/compare/fontastic-v1.2.0...fontastic-v1.3.0) (2026-03-15)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### 🎉 Features
|
|
14
|
+
|
|
15
|
+
* add smart collection preview functionality and enhance rule builder component ([436b162](https://github.com/tomshaw/fontastic/commit/436b16242ddae4eaaff3279d749b70dc79c9098c))
|
|
16
|
+
* enhance application with secure storage and scan progress features ([7a2576b](https://github.com/tomshaw/fontastic/commit/7a2576b5484ce3d8ce1c06171f60608b43c3e42b))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### 🛠️ Fixes
|
|
20
|
+
|
|
21
|
+
* add height class to footer layout for improved display ([10da788](https://github.com/tomshaw/fontastic/commit/10da78803ba3ccec3cf9f76646a896a013a8935f))
|
|
22
|
+
* adjust collection order handling in createParent and createChild methods ([3c9a0c4](https://github.com/tomshaw/fontastic/commit/3c9a0c4e2062a3c8a36b32f71193b01ddc57073e))
|
|
23
|
+
* refactor footer component layout and enhance version handling ([dc0475e](https://github.com/tomshaw/fontastic/commit/dc0475ed4c67a326a7710894d9145731a7909b5d))
|
|
24
|
+
* update collection order handling in createParent and createChild methods ([ce855d0](https://github.com/tomshaw/fontastic/commit/ce855d0664ab391fd15fed0bbffd16568a019699))
|
|
25
|
+
* update footer layout to use grid for improved structure and responsiveness ([b2a4dac](https://github.com/tomshaw/fontastic/commit/b2a4dac434f9ea732180f32477854905c0fa80fb))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### 🏗️ Refactor
|
|
29
|
+
|
|
30
|
+
* standardize string quotes and improve code readability ([f352ddb](https://github.com/tomshaw/fontastic/commit/f352ddb248e6c7dc1e566917ebf57ebabdde3531))
|
|
31
|
+
|
|
3
32
|
## [1.2.0](https://github.com/tomshaw/fontastic/compare/fontastic-v1.1.0...fontastic-v1.2.0) (2026-03-14)
|
|
4
33
|
|
|
5
34
|
|
package/README.md
CHANGED
|
@@ -23,6 +23,12 @@ Fontastic is an Electron-based font management and cataloging application built
|
|
|
23
23
|
- Glyph inspector — browse and examine individual characters and Unicode points
|
|
24
24
|
- Waterfall preview — compare text rendering across multiple sizes at a glance
|
|
25
25
|
- Font table viewer — read raw OpenType and TrueType metadata tables
|
|
26
|
+
- System theme sync — automatically matches OS light/dark mode via `nativeTheme`
|
|
27
|
+
- Encrypted storage — API keys secured at rest using OS keychain via `safeStorage`
|
|
28
|
+
- Scan progress — real-time font import progress streamed over `MessageChannelMain`
|
|
29
|
+
- Power aware — pauses activity on system sleep and resumes on wake via `powerMonitor`
|
|
30
|
+
- Session hardening — CSP headers, permission deny-list, and cache management via `session`
|
|
31
|
+
- Accessibility — respects OS reduced motion and accent color via `systemPreferences`
|
|
26
32
|
- Cross-platform — builds for Windows, macOS, and Linux
|
|
27
33
|
|
|
28
34
|
## Getting Started
|
package/app/Application.js
CHANGED
|
@@ -26,12 +26,12 @@ class Application {
|
|
|
26
26
|
const systemManager = new SystemManager_1.default(this.machineId, this.isProduction);
|
|
27
27
|
const configManager = new ConfigManager_1.default(systemManager);
|
|
28
28
|
configManager.initialize();
|
|
29
|
+
const menuBuilder = new MenuBuilder_1.default(this.mainWindow, this.isProduction);
|
|
29
30
|
const connectionManager = new ConnectionManager_1.default(configManager);
|
|
30
|
-
|
|
31
|
+
// Initialize menu and database connection in parallel — menu doesn't depend on DB
|
|
32
|
+
yield Promise.all([connectionManager.initialize(), Promise.resolve(menuBuilder.initialize())]);
|
|
31
33
|
const fontManager = new FontManager_1.default(systemManager, configManager, connectionManager);
|
|
32
|
-
const
|
|
33
|
-
menuBuilder.initialize();
|
|
34
|
-
const messageHandler = new MessageHandler_1.default(systemManager, configManager, connectionManager, fontManager);
|
|
34
|
+
const messageHandler = new MessageHandler_1.default(systemManager, configManager, connectionManager, fontManager, this.mainWindow);
|
|
35
35
|
messageHandler.initialize();
|
|
36
36
|
});
|
|
37
37
|
}
|
package/app/Application.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import SystemManager from
|
|
2
|
-
import ConfigManager from
|
|
3
|
-
import ConnectionManager from
|
|
4
|
-
import FontManager from
|
|
5
|
-
import MessageHandler from
|
|
6
|
-
import MenuBuilder from
|
|
7
|
-
import { BrowserWindow } from
|
|
1
|
+
import SystemManager from './core/SystemManager';
|
|
2
|
+
import ConfigManager from './core/ConfigManager';
|
|
3
|
+
import ConnectionManager from './core/ConnectionManager';
|
|
4
|
+
import FontManager from './core/FontManager';
|
|
5
|
+
import MessageHandler from './core/MessageHandler';
|
|
6
|
+
import MenuBuilder from './core/menu/MenuBuilder';
|
|
7
|
+
import { BrowserWindow } from 'electron';
|
|
8
8
|
|
|
9
9
|
export default class Application {
|
|
10
|
-
|
|
11
10
|
machineId: string;
|
|
12
11
|
isProduction: boolean;
|
|
13
12
|
mainWindow: BrowserWindow;
|
|
@@ -24,15 +23,16 @@ export default class Application {
|
|
|
24
23
|
const configManager = new ConfigManager(systemManager);
|
|
25
24
|
configManager.initialize();
|
|
26
25
|
|
|
26
|
+
const menuBuilder = new MenuBuilder(this.mainWindow, this.isProduction);
|
|
27
|
+
|
|
27
28
|
const connectionManager = new ConnectionManager(configManager);
|
|
28
|
-
await connectionManager.initialize()
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
// Initialize menu and database connection in parallel — menu doesn't depend on DB
|
|
31
|
+
await Promise.all([connectionManager.initialize(), Promise.resolve(menuBuilder.initialize())]);
|
|
31
32
|
|
|
32
|
-
const
|
|
33
|
-
menuBuilder.initialize();
|
|
33
|
+
const fontManager = new FontManager(systemManager, configManager, connectionManager);
|
|
34
34
|
|
|
35
|
-
const messageHandler = new MessageHandler(systemManager, configManager, connectionManager, fontManager);
|
|
35
|
+
const messageHandler = new MessageHandler(systemManager, configManager, connectionManager, fontManager, this.mainWindow);
|
|
36
36
|
messageHandler.initialize();
|
|
37
37
|
}
|
|
38
38
|
}
|
package/app/config/database.js
CHANGED
package/app/config/database.ts
CHANGED
package/app/config/mimes.js
CHANGED
|
@@ -3,41 +3,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.installable = exports.mimeTypes = exports.fontMimeTypes = void 0;
|
|
4
4
|
exports.fontMimeTypes = [
|
|
5
5
|
{
|
|
6
|
-
type:
|
|
7
|
-
name:
|
|
6
|
+
type: 'font/ttf',
|
|
7
|
+
name: 'TrueType',
|
|
8
8
|
description: "TrueType is an outline font standard developed by Apple in the late 1980s as a competitor to Adobe's Type 1 fonts used in PostScript. It has become the most common format for fonts on the classic Mac OS, macOS, and Microsoft Windows operating systems.",
|
|
9
|
-
installable: true
|
|
9
|
+
installable: true,
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
|
-
type:
|
|
13
|
-
name:
|
|
12
|
+
type: 'font/otf',
|
|
13
|
+
name: 'OpenType',
|
|
14
14
|
description: "OpenType is a format for scalable computer fonts. It was built on its predecessor TrueType, retaining TrueType's basic structure and adding many intricate data structures for prescribing typographic behavior. OpenType is a registered trademark of Microsoft Corporation.",
|
|
15
|
-
installable: true
|
|
15
|
+
installable: true,
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
|
-
type:
|
|
19
|
-
name:
|
|
20
|
-
description:
|
|
21
|
-
installable: false
|
|
18
|
+
type: 'font/woff',
|
|
19
|
+
name: 'Web Open Font Format',
|
|
20
|
+
description: 'The Web Open Font Format is a font format for use in web pages. WOFF files are OpenType or TrueType fonts, with format-specific compression applied and additional XML metadata added.',
|
|
21
|
+
installable: false,
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
|
-
type:
|
|
25
|
-
name:
|
|
26
|
-
description:
|
|
27
|
-
installable: false
|
|
24
|
+
type: 'font/woff2',
|
|
25
|
+
name: 'Web Open Font Format 2',
|
|
26
|
+
description: 'A WOFF2 file is a web font file created in the WOFF (Web Open Font Format) 2.0 format, an open format used to deliver webpage fonts on the fly. It is saved as a compressed container that supports TrueType (. TTF) and OpenType (. OTF) fonts. WOFF2 files also support font licensing metadata.',
|
|
27
|
+
installable: false,
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
|
-
type:
|
|
31
|
-
name:
|
|
32
|
-
description:
|
|
33
|
-
installable: false
|
|
30
|
+
type: 'font/ttc',
|
|
31
|
+
name: 'TrueType Collection',
|
|
32
|
+
description: 'TrueType Collection (TTC) is an extension of TrueType format that allows combining multiple fonts into a single file, creating substantial space savings for a collection of fonts with many glyphs in common.',
|
|
33
|
+
installable: false,
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
|
-
type:
|
|
37
|
-
name:
|
|
38
|
-
description:
|
|
39
|
-
installable: false
|
|
40
|
-
}
|
|
36
|
+
type: 'font/dfont',
|
|
37
|
+
name: 'Datafork TrueType',
|
|
38
|
+
description: 'Datafork TrueType is a font wrapper used on Apple Macintosh computers running Mac OS X. It is a TrueType suitcase with the resource map in the data fork, rather than the resource fork as had been the case in Mac OS 9.',
|
|
39
|
+
installable: false,
|
|
40
|
+
},
|
|
41
41
|
];
|
|
42
42
|
exports.mimeTypes = exports.fontMimeTypes.map((item) => item.type);
|
|
43
43
|
exports.installable = exports.fontMimeTypes.reduce((prev, curr) => {
|
package/app/config/mimes.ts
CHANGED
|
@@ -1,54 +1,60 @@
|
|
|
1
1
|
interface Mime {
|
|
2
2
|
type: string;
|
|
3
3
|
name: string;
|
|
4
|
-
description: string
|
|
5
|
-
installable: boolean
|
|
4
|
+
description: string;
|
|
5
|
+
installable: boolean;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export const fontMimeTypes: Mime[] = [
|
|
9
9
|
{
|
|
10
|
-
type:
|
|
11
|
-
name:
|
|
12
|
-
description:
|
|
13
|
-
|
|
10
|
+
type: 'font/ttf',
|
|
11
|
+
name: 'TrueType',
|
|
12
|
+
description:
|
|
13
|
+
"TrueType is an outline font standard developed by Apple in the late 1980s as a competitor to Adobe's Type 1 fonts used in PostScript. It has become the most common format for fonts on the classic Mac OS, macOS, and Microsoft Windows operating systems.",
|
|
14
|
+
installable: true,
|
|
14
15
|
},
|
|
15
16
|
{
|
|
16
|
-
type:
|
|
17
|
-
name:
|
|
18
|
-
description:
|
|
19
|
-
|
|
17
|
+
type: 'font/otf',
|
|
18
|
+
name: 'OpenType',
|
|
19
|
+
description:
|
|
20
|
+
"OpenType is a format for scalable computer fonts. It was built on its predecessor TrueType, retaining TrueType's basic structure and adding many intricate data structures for prescribing typographic behavior. OpenType is a registered trademark of Microsoft Corporation.",
|
|
21
|
+
installable: true,
|
|
20
22
|
},
|
|
21
23
|
{
|
|
22
|
-
type:
|
|
23
|
-
name:
|
|
24
|
-
description:
|
|
25
|
-
|
|
24
|
+
type: 'font/woff',
|
|
25
|
+
name: 'Web Open Font Format',
|
|
26
|
+
description:
|
|
27
|
+
'The Web Open Font Format is a font format for use in web pages. WOFF files are OpenType or TrueType fonts, with format-specific compression applied and additional XML metadata added.',
|
|
28
|
+
installable: false,
|
|
26
29
|
},
|
|
27
30
|
{
|
|
28
|
-
type:
|
|
29
|
-
name:
|
|
30
|
-
description:
|
|
31
|
-
|
|
31
|
+
type: 'font/woff2',
|
|
32
|
+
name: 'Web Open Font Format 2',
|
|
33
|
+
description:
|
|
34
|
+
'A WOFF2 file is a web font file created in the WOFF (Web Open Font Format) 2.0 format, an open format used to deliver webpage fonts on the fly. It is saved as a compressed container that supports TrueType (. TTF) and OpenType (. OTF) fonts. WOFF2 files also support font licensing metadata.',
|
|
35
|
+
installable: false,
|
|
32
36
|
},
|
|
33
37
|
{
|
|
34
|
-
type:
|
|
35
|
-
name:
|
|
36
|
-
description:
|
|
37
|
-
|
|
38
|
+
type: 'font/ttc',
|
|
39
|
+
name: 'TrueType Collection',
|
|
40
|
+
description:
|
|
41
|
+
'TrueType Collection (TTC) is an extension of TrueType format that allows combining multiple fonts into a single file, creating substantial space savings for a collection of fonts with many glyphs in common.',
|
|
42
|
+
installable: false,
|
|
38
43
|
},
|
|
39
44
|
{
|
|
40
|
-
type:
|
|
41
|
-
name:
|
|
42
|
-
description:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
type: 'font/dfont',
|
|
46
|
+
name: 'Datafork TrueType',
|
|
47
|
+
description:
|
|
48
|
+
'Datafork TrueType is a font wrapper used on Apple Macintosh computers running Mac OS X. It is a TrueType suitcase with the resource map in the data fork, rather than the resource fork as had been the case in Mac OS 9.',
|
|
49
|
+
installable: false,
|
|
50
|
+
},
|
|
45
51
|
];
|
|
46
52
|
|
|
47
53
|
export const mimeTypes: string[] = fontMimeTypes.map((item) => item.type);
|
|
48
54
|
|
|
49
|
-
export const installable:
|
|
55
|
+
export const installable: string[] = fontMimeTypes.reduce((prev: string[], curr) => {
|
|
50
56
|
if (curr.installable) {
|
|
51
57
|
prev.push(curr.type);
|
|
52
58
|
}
|
|
53
59
|
return prev;
|
|
54
|
-
}, []);
|
|
60
|
+
}, []);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const electron_1 = require("electron");
|
|
3
4
|
const database_1 = require("../config/database");
|
|
4
5
|
const StorageType_1 = require("../enums/StorageType");
|
|
5
6
|
const Store = require('electron-store');
|
|
@@ -26,6 +27,32 @@ class ConfigManager {
|
|
|
26
27
|
clear() {
|
|
27
28
|
return store.clear();
|
|
28
29
|
}
|
|
30
|
+
// --- Safe Storage (OS keychain encryption) ---
|
|
31
|
+
setSecure(key, value) {
|
|
32
|
+
if (electron_1.safeStorage.isEncryptionAvailable()) {
|
|
33
|
+
const encrypted = electron_1.safeStorage.encryptString(value);
|
|
34
|
+
store.set(key, encrypted.toString('base64'));
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
store.set(key, value);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
getSecure(key) {
|
|
41
|
+
const raw = store.get(key);
|
|
42
|
+
if (!raw)
|
|
43
|
+
return null;
|
|
44
|
+
if (electron_1.safeStorage.isEncryptionAvailable() && typeof raw === 'string') {
|
|
45
|
+
try {
|
|
46
|
+
const buffer = Buffer.from(raw, 'base64');
|
|
47
|
+
return electron_1.safeStorage.decryptString(buffer);
|
|
48
|
+
}
|
|
49
|
+
catch (_a) {
|
|
50
|
+
// Fallback: value may have been stored before encryption was enabled
|
|
51
|
+
return raw;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return typeof raw === 'string' ? raw : null;
|
|
55
|
+
}
|
|
29
56
|
toArray() {
|
|
30
57
|
return store.store;
|
|
31
58
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { safeStorage } from 'electron';
|
|
1
2
|
import SystemManager from './SystemManager';
|
|
2
3
|
import { database } from '../config/database';
|
|
3
4
|
import { StorageType } from '../enums/StorageType';
|
|
@@ -35,6 +36,33 @@ export default class ConfigManager {
|
|
|
35
36
|
return store.clear();
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
// --- Safe Storage (OS keychain encryption) ---
|
|
40
|
+
|
|
41
|
+
setSecure(key: string, value: string): void {
|
|
42
|
+
if (safeStorage.isEncryptionAvailable()) {
|
|
43
|
+
const encrypted = safeStorage.encryptString(value);
|
|
44
|
+
store.set(key, encrypted.toString('base64'));
|
|
45
|
+
} else {
|
|
46
|
+
store.set(key, value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
getSecure(key: string): string | null {
|
|
51
|
+
const raw = store.get(key);
|
|
52
|
+
if (!raw) return null;
|
|
53
|
+
|
|
54
|
+
if (safeStorage.isEncryptionAvailable() && typeof raw === 'string') {
|
|
55
|
+
try {
|
|
56
|
+
const buffer = Buffer.from(raw, 'base64');
|
|
57
|
+
return safeStorage.decryptString(buffer);
|
|
58
|
+
} catch {
|
|
59
|
+
// Fallback: value may have been stored before encryption was enabled
|
|
60
|
+
return raw;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return typeof raw === 'string' ? raw : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
38
66
|
toArray(): any {
|
|
39
67
|
return store.store;
|
|
40
68
|
}
|
package/app/core/FontFinder.js
CHANGED
|
@@ -13,58 +13,109 @@ const FontObject_1 = require("./FontObject");
|
|
|
13
13
|
const fs = require("fs/promises");
|
|
14
14
|
const path = require("path");
|
|
15
15
|
const mimes_1 = require("../config/mimes");
|
|
16
|
-
const prettyBytes = require(
|
|
17
|
-
const mime = require(
|
|
16
|
+
const prettyBytes = require('pretty-bytes');
|
|
17
|
+
const mime = require('mime');
|
|
18
|
+
const SCAN_CONCURRENCY = 10;
|
|
18
19
|
class FontFinder {
|
|
19
|
-
constructor(connectionManager) {
|
|
20
|
+
constructor(connectionManager, onProgress) {
|
|
20
21
|
this.errors = [];
|
|
21
22
|
this.counter = 0;
|
|
23
|
+
this.onProgress = null;
|
|
22
24
|
this.connectionManager = connectionManager;
|
|
25
|
+
this.onProgress = onProgress !== null && onProgress !== void 0 ? onProgress : null;
|
|
23
26
|
}
|
|
24
|
-
|
|
27
|
+
getFontMimeType(filePath) {
|
|
25
28
|
const fileType = mime.getType(filePath);
|
|
26
|
-
return fileType && mimes_1.mimeTypes.includes(fileType);
|
|
29
|
+
return fileType && mimes_1.mimeTypes.includes(fileType) ? fileType : null;
|
|
27
30
|
}
|
|
28
31
|
scanFiles(files, options) {
|
|
29
32
|
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
+
this.errors = [];
|
|
34
|
+
this.counter = 0;
|
|
35
|
+
const fontFiles = [];
|
|
30
36
|
for (const fp of files) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
const fileType = this.getFontMimeType(fp);
|
|
38
|
+
if (fileType) {
|
|
39
|
+
fontFiles.push({ fp, fileType });
|
|
34
40
|
}
|
|
35
41
|
}
|
|
42
|
+
yield this.processInBatches(fontFiles, options);
|
|
36
43
|
});
|
|
37
44
|
}
|
|
38
45
|
scanFolder(dir, options) {
|
|
39
46
|
return __awaiter(this, void 0, void 0, function* () {
|
|
47
|
+
this.errors = [];
|
|
48
|
+
this.counter = 0;
|
|
49
|
+
const fontFiles = yield this.collectFontFiles(dir);
|
|
50
|
+
yield this.processInBatches(fontFiles, options);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
collectFontFiles(dir) {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
const results = [];
|
|
40
56
|
const entries = yield fs.readdir(dir, { withFileTypes: true });
|
|
57
|
+
const subdirPromises = [];
|
|
41
58
|
for (const entry of entries) {
|
|
42
59
|
const fp = path.join(dir, entry.name);
|
|
43
60
|
if (entry.isDirectory()) {
|
|
44
|
-
|
|
61
|
+
subdirPromises.push(this.collectFontFiles(fp));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const fileType = this.getFontMimeType(fp);
|
|
65
|
+
if (fileType) {
|
|
66
|
+
results.push({ fp, fileType });
|
|
67
|
+
}
|
|
45
68
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
69
|
+
}
|
|
70
|
+
if (subdirPromises.length > 0) {
|
|
71
|
+
const subdirResults = yield Promise.all(subdirPromises);
|
|
72
|
+
for (const subResults of subdirResults) {
|
|
73
|
+
results.push(...subResults);
|
|
49
74
|
}
|
|
50
75
|
}
|
|
76
|
+
return results;
|
|
51
77
|
});
|
|
52
78
|
}
|
|
53
|
-
|
|
79
|
+
processInBatches(fontFiles, options) {
|
|
80
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
const total = fontFiles.length;
|
|
82
|
+
for (let i = 0; i < fontFiles.length; i += SCAN_CONCURRENCY) {
|
|
83
|
+
const batch = fontFiles.slice(i, i + SCAN_CONCURRENCY);
|
|
84
|
+
yield Promise.all(batch.map(({ fp, fileType }) => this.processFont(fp, fileType, options)));
|
|
85
|
+
if (this.onProgress) {
|
|
86
|
+
const lastFile = batch[batch.length - 1];
|
|
87
|
+
this.onProgress({
|
|
88
|
+
processed: Math.min(i + SCAN_CONCURRENCY, total),
|
|
89
|
+
total,
|
|
90
|
+
currentFile: path.basename(lastFile.fp),
|
|
91
|
+
errors: this.errors.length,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
processFont(fp, fileType, options) {
|
|
54
98
|
return __awaiter(this, void 0, void 0, function* () {
|
|
55
99
|
const font = new FontObject_1.default(fp);
|
|
56
100
|
if (font.hasError()) {
|
|
57
101
|
this.errors.push(font.getError());
|
|
58
102
|
return;
|
|
59
103
|
}
|
|
60
|
-
|
|
104
|
+
let stat;
|
|
105
|
+
try {
|
|
106
|
+
stat = yield fs.stat(fp);
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
this.errors.push({ file: fp, message: err.message });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
61
112
|
const data = Object.assign(Object.assign({ file_path: fp, file_name: path.basename(fp), file_size: stat.size, file_size_pretty: prettyBytes(stat.size), file_type: fileType, installable: mimes_1.installable.includes(fileType) }, options), font.getNamesTable());
|
|
62
113
|
try {
|
|
63
114
|
yield this.connectionManager.getStoreRepository().create(data);
|
|
64
115
|
this.counter++;
|
|
65
116
|
}
|
|
66
117
|
catch (err) {
|
|
67
|
-
this.errors.push(err.message);
|
|
118
|
+
this.errors.push({ file: fp, message: err.message });
|
|
68
119
|
}
|
|
69
120
|
});
|
|
70
121
|
}
|
package/app/core/FontFinder.ts
CHANGED
|
@@ -1,50 +1,103 @@
|
|
|
1
|
-
import ConnectionManager from
|
|
2
|
-
import FontObject from
|
|
3
|
-
import * as fs from
|
|
4
|
-
import * as path from
|
|
5
|
-
import { mimeTypes, installable } from
|
|
1
|
+
import ConnectionManager from './ConnectionManager';
|
|
2
|
+
import FontObject from './FontObject';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { mimeTypes, installable } from '../config/mimes';
|
|
6
|
+
import type { ScanProgress } from '../types';
|
|
6
7
|
|
|
7
|
-
const prettyBytes = require(
|
|
8
|
-
const mime = require(
|
|
8
|
+
const prettyBytes = require('pretty-bytes');
|
|
9
|
+
const mime = require('mime');
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
const SCAN_CONCURRENCY = 10;
|
|
12
|
+
|
|
13
|
+
export type ProgressCallback = (progress: ScanProgress) => void;
|
|
11
14
|
|
|
15
|
+
export default class FontFinder {
|
|
12
16
|
connectionManager: ConnectionManager;
|
|
13
17
|
errors: any[] = [];
|
|
14
18
|
counter: number = 0;
|
|
19
|
+
private onProgress: ProgressCallback | null = null;
|
|
15
20
|
|
|
16
|
-
constructor(connectionManager: ConnectionManager) {
|
|
21
|
+
constructor(connectionManager: ConnectionManager, onProgress?: ProgressCallback) {
|
|
17
22
|
this.connectionManager = connectionManager;
|
|
23
|
+
this.onProgress = onProgress ?? null;
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
private
|
|
26
|
+
private getFontMimeType(filePath: string): string | null {
|
|
21
27
|
const fileType = mime.getType(filePath);
|
|
22
|
-
return fileType && mimeTypes.includes(fileType);
|
|
28
|
+
return fileType && mimeTypes.includes(fileType) ? fileType : null;
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
async scanFiles(files: string[], options: any) {
|
|
32
|
+
this.errors = [];
|
|
33
|
+
this.counter = 0;
|
|
34
|
+
|
|
35
|
+
const fontFiles: { fp: string; fileType: string }[] = [];
|
|
26
36
|
for (const fp of files) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
const fileType = this.getFontMimeType(fp);
|
|
38
|
+
if (fileType) {
|
|
39
|
+
fontFiles.push({ fp, fileType });
|
|
30
40
|
}
|
|
31
41
|
}
|
|
42
|
+
|
|
43
|
+
await this.processInBatches(fontFiles, options);
|
|
32
44
|
}
|
|
33
45
|
|
|
34
46
|
async scanFolder(dir: string, options: any) {
|
|
47
|
+
this.errors = [];
|
|
48
|
+
this.counter = 0;
|
|
49
|
+
|
|
50
|
+
const fontFiles = await this.collectFontFiles(dir);
|
|
51
|
+
await this.processInBatches(fontFiles, options);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private async collectFontFiles(dir: string): Promise<{ fp: string; fileType: string }[]> {
|
|
55
|
+
const results: { fp: string; fileType: string }[] = [];
|
|
35
56
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
57
|
+
|
|
58
|
+
const subdirPromises: Promise<{ fp: string; fileType: string }[]>[] = [];
|
|
59
|
+
|
|
36
60
|
for (const entry of entries) {
|
|
37
61
|
const fp = path.join(dir, entry.name);
|
|
38
62
|
if (entry.isDirectory()) {
|
|
39
|
-
|
|
40
|
-
} else
|
|
41
|
-
const
|
|
42
|
-
|
|
63
|
+
subdirPromises.push(this.collectFontFiles(fp));
|
|
64
|
+
} else {
|
|
65
|
+
const fileType = this.getFontMimeType(fp);
|
|
66
|
+
if (fileType) {
|
|
67
|
+
results.push({ fp, fileType });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (subdirPromises.length > 0) {
|
|
73
|
+
const subdirResults = await Promise.all(subdirPromises);
|
|
74
|
+
for (const subResults of subdirResults) {
|
|
75
|
+
results.push(...subResults);
|
|
43
76
|
}
|
|
44
77
|
}
|
|
78
|
+
|
|
79
|
+
return results;
|
|
45
80
|
}
|
|
46
81
|
|
|
47
|
-
private async
|
|
82
|
+
private async processInBatches(fontFiles: { fp: string; fileType: string }[], options: any) {
|
|
83
|
+
const total = fontFiles.length;
|
|
84
|
+
for (let i = 0; i < fontFiles.length; i += SCAN_CONCURRENCY) {
|
|
85
|
+
const batch = fontFiles.slice(i, i + SCAN_CONCURRENCY);
|
|
86
|
+
await Promise.all(batch.map(({ fp, fileType }) => this.processFont(fp, fileType, options)));
|
|
87
|
+
|
|
88
|
+
if (this.onProgress) {
|
|
89
|
+
const lastFile = batch[batch.length - 1];
|
|
90
|
+
this.onProgress({
|
|
91
|
+
processed: Math.min(i + SCAN_CONCURRENCY, total),
|
|
92
|
+
total,
|
|
93
|
+
currentFile: path.basename(lastFile.fp),
|
|
94
|
+
errors: this.errors.length,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private async processFont(fp: string, fileType: string, options: any) {
|
|
48
101
|
const font = new FontObject(fp);
|
|
49
102
|
|
|
50
103
|
if (font.hasError()) {
|
|
@@ -52,7 +105,13 @@ export default class FontFinder {
|
|
|
52
105
|
return;
|
|
53
106
|
}
|
|
54
107
|
|
|
55
|
-
|
|
108
|
+
let stat;
|
|
109
|
+
try {
|
|
110
|
+
stat = await fs.stat(fp);
|
|
111
|
+
} catch (err: any) {
|
|
112
|
+
this.errors.push({ file: fp, message: err.message });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
56
115
|
|
|
57
116
|
const data = {
|
|
58
117
|
file_path: fp,
|
|
@@ -68,8 +127,8 @@ export default class FontFinder {
|
|
|
68
127
|
try {
|
|
69
128
|
await this.connectionManager.getStoreRepository().create(data);
|
|
70
129
|
this.counter++;
|
|
71
|
-
} catch (err) {
|
|
72
|
-
this.errors.push(err.message);
|
|
130
|
+
} catch (err: any) {
|
|
131
|
+
this.errors.push({ file: fp, message: err.message });
|
|
73
132
|
}
|
|
74
133
|
}
|
|
75
134
|
}
|