@enableai-fonts/core 0.1.3

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.
@@ -0,0 +1,185 @@
1
+ /**
2
+ * @enableai-fonts/core v0.1.3
3
+ * (c) 2024-present Enableai
4
+ * @license Proprietary
5
+ **/
6
+ 'use strict';
7
+
8
+ Object.defineProperty(exports, '__esModule', { value: true });
9
+
10
+ class FontIconRegistry {
11
+ constructor(options) {
12
+ // font mode fields
13
+ this.fontFamily = "";
14
+ this.icons = {};
15
+ this.autoInjectCss = false;
16
+ this.cssInjected = false;
17
+ // svg mode fields
18
+ this.symbolIdPrefix = "icon-";
19
+ this.spriteUrl = "";
20
+ this.svgInjected = false;
21
+ this._initCalled = false;
22
+ this._initPromise = null;
23
+ this._svgInitPromise = null;
24
+ this.options = options;
25
+ this.icons = options.icons || {};
26
+ if (options.mode === "font") {
27
+ this.fontFamily = options.fontFamily;
28
+ this.classPrefix = options.classPrefix;
29
+ this.fontSrc = options.fontSrc;
30
+ this.autoInjectCss = !!options.autoInjectCss;
31
+ } else {
32
+ this.spriteUrl = options.fontSrc.sprite;
33
+ }
34
+ }
35
+ get initCalled() {
36
+ return this._initCalled;
37
+ }
38
+ get keys() {
39
+ return Object.keys(this.icons);
40
+ }
41
+ async init() {
42
+ if (this._initCalled) return true;
43
+ if (this._initPromise) return this._initPromise;
44
+ this._initPromise = (async () => {
45
+ try {
46
+ if (this.options.mode === "svg") {
47
+ const ok = await this.initSvgSprite();
48
+ this._initCalled = ok;
49
+ return ok;
50
+ }
51
+ if (this.fontSrc) {
52
+ const srcParts = [];
53
+ if (this.fontSrc.woff) srcParts.push(`url("${this.fontSrc.woff}") format("woff")`);
54
+ if (this.fontSrc.ttf) srcParts.push(`url("${this.fontSrc.ttf}") format("truetype")`);
55
+ if (this.fontSrc.svg) srcParts.push(`url("${this.fontSrc.svg}") format("svg")`);
56
+ if (srcParts.length === 0) {
57
+ console.warn("[FontIconRegistry] fontSrc provided but empty; falling back to document.fonts.load");
58
+ } else {
59
+ const face = new FontFace(this.fontFamily, srcParts.join(", "));
60
+ const loaded = await face.load();
61
+ document.fonts.add(loaded);
62
+ }
63
+ }
64
+ if (this.autoInjectCss && this.classPrefix && !this.cssInjected) {
65
+ this.injectCss();
66
+ }
67
+ await document.fonts.load(`1em ${this.fontFamily}`);
68
+ await document.fonts.ready;
69
+ this._initCalled = true;
70
+ return true;
71
+ } catch (err) {
72
+ console.error("[FontIconRegistry] init failed:", err);
73
+ this._initPromise = null;
74
+ return false;
75
+ }
76
+ })();
77
+ return this._initPromise;
78
+ }
79
+ /** SVG: 返回 symbol href,如 "#icon-edit" */
80
+ getSymbolHref(name) {
81
+ if (this.options.mode !== "svg") {
82
+ throw new Error("[FontIconRegistry] getSymbolHref only works in svg mode");
83
+ }
84
+ return `#${this.symbolIdPrefix}${String(name)}`;
85
+ }
86
+ /** Font: 返回 HTML 可用的 className,例如 "anticon anticon-edit" */
87
+ getClass(name, extra) {
88
+ if (this.options.mode !== "font") {
89
+ throw new Error("[FontIconRegistry] getClass only works in font mode");
90
+ }
91
+ const base = this.classPrefix ? this.classPrefix : "";
92
+ const item = this.classPrefix ? `${this.classPrefix}-${name}` : "";
93
+ return [base, item, extra].filter(Boolean).join(" ");
94
+ }
95
+ /** Font: 返回图标的 hex 码点(不带 \u) */
96
+ getHex(name) {
97
+ if (this.options.mode !== "font") {
98
+ throw new Error("[FontIconRegistry] getHex only works in font mode");
99
+ }
100
+ const hex = this.icons[name];
101
+ if (!hex) throw new Error(`[FontIconRegistry] icon "${name}" not found`);
102
+ return hex.toLowerCase();
103
+ }
104
+ /** Font: 返回可直接渲染的字符 */
105
+ getChar(name) {
106
+ const hex = this.getHex(name);
107
+ const codePoint = parseInt(hex, 16);
108
+ return String.fromCodePoint(codePoint);
109
+ }
110
+ /** Font: 返回 HTML 实体形式,例如 "" */
111
+ toEntity(name) {
112
+ return `&#x${this.getHex(name)};`;
113
+ }
114
+ async initSvgSprite() {
115
+ if (this.svgInjected) return true;
116
+ if (this._svgInitPromise) return this._svgInitPromise;
117
+ this._svgInitPromise = (async () => {
118
+ try {
119
+ if (!this.spriteUrl) return false;
120
+ const mark = `font-icon-sprite:${this.spriteUrl}`;
121
+ const selector = `[data-font-icon-sprite="${CSS.escape(mark)}"]`;
122
+ if (document.querySelector(selector)) {
123
+ this.svgInjected = true;
124
+ return true;
125
+ }
126
+ const res = await fetch(this.spriteUrl, { credentials: "omit", cache: "force-cache" });
127
+ if (!res.ok) throw new Error(`fetch sprite failed: ${res.status} ${res.statusText}`);
128
+ const svgText = await res.text();
129
+ if (!svgText || !svgText.includes("<svg")) throw new Error("sprite is empty or invalid");
130
+ const container = document.createElement("div");
131
+ container.setAttribute("data-font-icon-sprite", mark);
132
+ container.style.cssText = "position:absolute;width:0;height:0;overflow:hidden;";
133
+ container.innerHTML = svgText;
134
+ const svgEl = container.querySelector("svg");
135
+ if (svgEl) {
136
+ svgEl.setAttribute("aria-hidden", "true");
137
+ svgEl.style.position = "absolute";
138
+ svgEl.style.width = "0";
139
+ svgEl.style.height = "0";
140
+ svgEl.style.overflow = "hidden";
141
+ }
142
+ const mount = document.body || document.documentElement;
143
+ mount.insertBefore(container, mount.firstChild);
144
+ this.svgInjected = true;
145
+ return true;
146
+ } catch (err) {
147
+ console.error("[FontIconRegistry] initSvgSprite failed:", err);
148
+ this._svgInitPromise = null;
149
+ return false;
150
+ }
151
+ })();
152
+ return this._svgInitPromise;
153
+ }
154
+ injectCss() {
155
+ if (!this.classPrefix) return;
156
+ const prefix = this.classPrefix;
157
+ const style = document.createElement("style");
158
+ style.setAttribute("data-font-icon", this.fontFamily);
159
+ const base = `
160
+ .${prefix} {
161
+ font-family: "${this.fontFamily}" !important;
162
+ speak: none;
163
+ font-style: normal;
164
+ font-weight: normal;
165
+ font-variant: normal;
166
+ text-rendering: auto;
167
+ -webkit-font-smoothing: antialiased;
168
+ -moz-osx-font-smoothing: grayscale;
169
+ }
170
+ `.trim();
171
+ const items = Object.entries(this.icons).map(([name, hex]) => {
172
+ return `.${prefix}-${name}::before { content: "\\${hex}"; }`;
173
+ });
174
+ style.textContent = base + "\n" + items.join("\n");
175
+ document.head.appendChild(style);
176
+ this.cssInjected = true;
177
+ }
178
+ }
179
+
180
+ const ENABLEAI_FONT_STACK = `'Roboto', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Source Han Sans SC', system-ui, -apple-system,
181
+ BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji',
182
+ sans-serif`;
183
+
184
+ exports.ENABLEAI_FONT_STACK = ENABLEAI_FONT_STACK;
185
+ exports.FontIconRegistry = FontIconRegistry;
@@ -0,0 +1,185 @@
1
+ /**
2
+ * @enableai-fonts/core v0.1.3
3
+ * (c) 2024-present Enableai
4
+ * @license Proprietary
5
+ **/
6
+ 'use strict';
7
+
8
+ Object.defineProperty(exports, '__esModule', { value: true });
9
+
10
+ class FontIconRegistry {
11
+ constructor(options) {
12
+ // font mode fields
13
+ this.fontFamily = "";
14
+ this.icons = {};
15
+ this.autoInjectCss = false;
16
+ this.cssInjected = false;
17
+ // svg mode fields
18
+ this.symbolIdPrefix = "icon-";
19
+ this.spriteUrl = "";
20
+ this.svgInjected = false;
21
+ this._initCalled = false;
22
+ this._initPromise = null;
23
+ this._svgInitPromise = null;
24
+ this.options = options;
25
+ this.icons = options.icons || {};
26
+ if (options.mode === "font") {
27
+ this.fontFamily = options.fontFamily;
28
+ this.classPrefix = options.classPrefix;
29
+ this.fontSrc = options.fontSrc;
30
+ this.autoInjectCss = !!options.autoInjectCss;
31
+ } else {
32
+ this.spriteUrl = options.fontSrc.sprite;
33
+ }
34
+ }
35
+ get initCalled() {
36
+ return this._initCalled;
37
+ }
38
+ get keys() {
39
+ return Object.keys(this.icons);
40
+ }
41
+ async init() {
42
+ if (this._initCalled) return true;
43
+ if (this._initPromise) return this._initPromise;
44
+ this._initPromise = (async () => {
45
+ try {
46
+ if (this.options.mode === "svg") {
47
+ const ok = await this.initSvgSprite();
48
+ this._initCalled = ok;
49
+ return ok;
50
+ }
51
+ if (this.fontSrc) {
52
+ const srcParts = [];
53
+ if (this.fontSrc.woff) srcParts.push(`url("${this.fontSrc.woff}") format("woff")`);
54
+ if (this.fontSrc.ttf) srcParts.push(`url("${this.fontSrc.ttf}") format("truetype")`);
55
+ if (this.fontSrc.svg) srcParts.push(`url("${this.fontSrc.svg}") format("svg")`);
56
+ if (srcParts.length === 0) {
57
+ console.warn("[FontIconRegistry] fontSrc provided but empty; falling back to document.fonts.load");
58
+ } else {
59
+ const face = new FontFace(this.fontFamily, srcParts.join(", "));
60
+ const loaded = await face.load();
61
+ document.fonts.add(loaded);
62
+ }
63
+ }
64
+ if (this.autoInjectCss && this.classPrefix && !this.cssInjected) {
65
+ this.injectCss();
66
+ }
67
+ await document.fonts.load(`1em ${this.fontFamily}`);
68
+ await document.fonts.ready;
69
+ this._initCalled = true;
70
+ return true;
71
+ } catch (err) {
72
+ console.error("[FontIconRegistry] init failed:", err);
73
+ this._initPromise = null;
74
+ return false;
75
+ }
76
+ })();
77
+ return this._initPromise;
78
+ }
79
+ /** SVG: 返回 symbol href,如 "#icon-edit" */
80
+ getSymbolHref(name) {
81
+ if (this.options.mode !== "svg") {
82
+ throw new Error("[FontIconRegistry] getSymbolHref only works in svg mode");
83
+ }
84
+ return `#${this.symbolIdPrefix}${String(name)}`;
85
+ }
86
+ /** Font: 返回 HTML 可用的 className,例如 "anticon anticon-edit" */
87
+ getClass(name, extra) {
88
+ if (this.options.mode !== "font") {
89
+ throw new Error("[FontIconRegistry] getClass only works in font mode");
90
+ }
91
+ const base = this.classPrefix ? this.classPrefix : "";
92
+ const item = this.classPrefix ? `${this.classPrefix}-${name}` : "";
93
+ return [base, item, extra].filter(Boolean).join(" ");
94
+ }
95
+ /** Font: 返回图标的 hex 码点(不带 \u) */
96
+ getHex(name) {
97
+ if (this.options.mode !== "font") {
98
+ throw new Error("[FontIconRegistry] getHex only works in font mode");
99
+ }
100
+ const hex = this.icons[name];
101
+ if (!hex) throw new Error(`[FontIconRegistry] icon "${name}" not found`);
102
+ return hex.toLowerCase();
103
+ }
104
+ /** Font: 返回可直接渲染的字符 */
105
+ getChar(name) {
106
+ const hex = this.getHex(name);
107
+ const codePoint = parseInt(hex, 16);
108
+ return String.fromCodePoint(codePoint);
109
+ }
110
+ /** Font: 返回 HTML 实体形式,例如 "&#xe600;" */
111
+ toEntity(name) {
112
+ return `&#x${this.getHex(name)};`;
113
+ }
114
+ async initSvgSprite() {
115
+ if (this.svgInjected) return true;
116
+ if (this._svgInitPromise) return this._svgInitPromise;
117
+ this._svgInitPromise = (async () => {
118
+ try {
119
+ if (!this.spriteUrl) return false;
120
+ const mark = `font-icon-sprite:${this.spriteUrl}`;
121
+ const selector = `[data-font-icon-sprite="${CSS.escape(mark)}"]`;
122
+ if (document.querySelector(selector)) {
123
+ this.svgInjected = true;
124
+ return true;
125
+ }
126
+ const res = await fetch(this.spriteUrl, { credentials: "omit", cache: "force-cache" });
127
+ if (!res.ok) throw new Error(`fetch sprite failed: ${res.status} ${res.statusText}`);
128
+ const svgText = await res.text();
129
+ if (!svgText || !svgText.includes("<svg")) throw new Error("sprite is empty or invalid");
130
+ const container = document.createElement("div");
131
+ container.setAttribute("data-font-icon-sprite", mark);
132
+ container.style.cssText = "position:absolute;width:0;height:0;overflow:hidden;";
133
+ container.innerHTML = svgText;
134
+ const svgEl = container.querySelector("svg");
135
+ if (svgEl) {
136
+ svgEl.setAttribute("aria-hidden", "true");
137
+ svgEl.style.position = "absolute";
138
+ svgEl.style.width = "0";
139
+ svgEl.style.height = "0";
140
+ svgEl.style.overflow = "hidden";
141
+ }
142
+ const mount = document.body || document.documentElement;
143
+ mount.insertBefore(container, mount.firstChild);
144
+ this.svgInjected = true;
145
+ return true;
146
+ } catch (err) {
147
+ console.error("[FontIconRegistry] initSvgSprite failed:", err);
148
+ this._svgInitPromise = null;
149
+ return false;
150
+ }
151
+ })();
152
+ return this._svgInitPromise;
153
+ }
154
+ injectCss() {
155
+ if (!this.classPrefix) return;
156
+ const prefix = this.classPrefix;
157
+ const style = document.createElement("style");
158
+ style.setAttribute("data-font-icon", this.fontFamily);
159
+ const base = `
160
+ .${prefix} {
161
+ font-family: "${this.fontFamily}" !important;
162
+ speak: none;
163
+ font-style: normal;
164
+ font-weight: normal;
165
+ font-variant: normal;
166
+ text-rendering: auto;
167
+ -webkit-font-smoothing: antialiased;
168
+ -moz-osx-font-smoothing: grayscale;
169
+ }
170
+ `.trim();
171
+ const items = Object.entries(this.icons).map(([name, hex]) => {
172
+ return `.${prefix}-${name}::before { content: "\\${hex}"; }`;
173
+ });
174
+ style.textContent = base + "\n" + items.join("\n");
175
+ document.head.appendChild(style);
176
+ this.cssInjected = true;
177
+ }
178
+ }
179
+
180
+ const ENABLEAI_FONT_STACK = `'Roboto', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Source Han Sans SC', system-ui, -apple-system,
181
+ BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji',
182
+ sans-serif`;
183
+
184
+ exports.ENABLEAI_FONT_STACK = ENABLEAI_FONT_STACK;
185
+ exports.FontIconRegistry = FontIconRegistry;
package/dist/core.css ADDED
@@ -0,0 +1,10 @@
1
+ :root {
2
+ --font-sans:
3
+ 'Roboto', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Source Han Sans SC', system-ui, -apple-system,
4
+ BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji',
5
+ sans-serif;
6
+ }
7
+
8
+ body {
9
+ font-family: var(--font-sans);
10
+ }
package/dist/core.d.ts ADDED
@@ -0,0 +1,58 @@
1
+ type IconMap<Key extends string> = Record<Key, string>;
2
+ type FontSrc = {
3
+ woff?: string;
4
+ ttf?: string;
5
+ svg?: string;
6
+ };
7
+ type SvgSpriteSrc = {
8
+ /** sprite.svg 的 URL(由 iconfont js 抽出来的那个) */
9
+ sprite: string;
10
+ };
11
+ type FontModeOptions<Key extends string> = {
12
+ mode: 'font';
13
+ fontFamily: string;
14
+ icons: IconMap<Key>;
15
+ classPrefix?: string;
16
+ fontSrc?: FontSrc;
17
+ autoInjectCss?: boolean;
18
+ };
19
+ type SvgModeOptions<Key extends string> = {
20
+ mode: 'svg';
21
+ icons: IconMap<Key>;
22
+ fontSrc: SvgSpriteSrc;
23
+ };
24
+ type Options<Key extends string> = FontModeOptions<Key> | SvgModeOptions<Key>;
25
+ export declare class FontIconRegistry<Key extends string> {
26
+ private options;
27
+ private fontFamily;
28
+ private icons;
29
+ private classPrefix?;
30
+ private fontSrc?;
31
+ private autoInjectCss;
32
+ private cssInjected;
33
+ private symbolIdPrefix;
34
+ private spriteUrl;
35
+ private svgInjected;
36
+ private _initCalled;
37
+ get initCalled(): boolean;
38
+ private _initPromise;
39
+ private _svgInitPromise;
40
+ constructor(options: Options<Key>);
41
+ get keys(): Key[];
42
+ init(): Promise<boolean>;
43
+ /** SVG: 返回 symbol href,如 "#icon-edit" */
44
+ getSymbolHref(name: Key): string;
45
+ /** Font: 返回 HTML 可用的 className,例如 "anticon anticon-edit" */
46
+ getClass(name: Key, extra?: string): string;
47
+ /** Font: 返回图标的 hex 码点(不带 \u) */
48
+ getHex(name: Key): string;
49
+ /** Font: 返回可直接渲染的字符 */
50
+ getChar(name: Key): string;
51
+ /** Font: 返回 HTML 实体形式,例如 "&#xe600;" */
52
+ toEntity(name: Key): string;
53
+ private initSvgSprite;
54
+ private injectCss;
55
+ }
56
+
57
+ export declare const ENABLEAI_FONT_STACK = "'Roboto', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Source Han Sans SC', system-ui, -apple-system,\n BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji',\n sans-serif";
58
+
@@ -0,0 +1,180 @@
1
+ /**
2
+ * @enableai-fonts/core v0.1.3
3
+ * (c) 2024-present Enableai
4
+ * @license Proprietary
5
+ **/
6
+ class FontIconRegistry {
7
+ constructor(options) {
8
+ // font mode fields
9
+ this.fontFamily = "";
10
+ this.icons = {};
11
+ this.autoInjectCss = false;
12
+ this.cssInjected = false;
13
+ // svg mode fields
14
+ this.symbolIdPrefix = "icon-";
15
+ this.spriteUrl = "";
16
+ this.svgInjected = false;
17
+ this._initCalled = false;
18
+ this._initPromise = null;
19
+ this._svgInitPromise = null;
20
+ this.options = options;
21
+ this.icons = options.icons || {};
22
+ if (options.mode === "font") {
23
+ this.fontFamily = options.fontFamily;
24
+ this.classPrefix = options.classPrefix;
25
+ this.fontSrc = options.fontSrc;
26
+ this.autoInjectCss = !!options.autoInjectCss;
27
+ } else {
28
+ this.spriteUrl = options.fontSrc.sprite;
29
+ }
30
+ }
31
+ get initCalled() {
32
+ return this._initCalled;
33
+ }
34
+ get keys() {
35
+ return Object.keys(this.icons);
36
+ }
37
+ async init() {
38
+ if (this._initCalled) return true;
39
+ if (this._initPromise) return this._initPromise;
40
+ this._initPromise = (async () => {
41
+ try {
42
+ if (this.options.mode === "svg") {
43
+ const ok = await this.initSvgSprite();
44
+ this._initCalled = ok;
45
+ return ok;
46
+ }
47
+ if (this.fontSrc) {
48
+ const srcParts = [];
49
+ if (this.fontSrc.woff) srcParts.push(`url("${this.fontSrc.woff}") format("woff")`);
50
+ if (this.fontSrc.ttf) srcParts.push(`url("${this.fontSrc.ttf}") format("truetype")`);
51
+ if (this.fontSrc.svg) srcParts.push(`url("${this.fontSrc.svg}") format("svg")`);
52
+ if (srcParts.length === 0) {
53
+ console.warn("[FontIconRegistry] fontSrc provided but empty; falling back to document.fonts.load");
54
+ } else {
55
+ const face = new FontFace(this.fontFamily, srcParts.join(", "));
56
+ const loaded = await face.load();
57
+ document.fonts.add(loaded);
58
+ }
59
+ }
60
+ if (this.autoInjectCss && this.classPrefix && !this.cssInjected) {
61
+ this.injectCss();
62
+ }
63
+ await document.fonts.load(`1em ${this.fontFamily}`);
64
+ await document.fonts.ready;
65
+ this._initCalled = true;
66
+ return true;
67
+ } catch (err) {
68
+ console.error("[FontIconRegistry] init failed:", err);
69
+ this._initPromise = null;
70
+ return false;
71
+ }
72
+ })();
73
+ return this._initPromise;
74
+ }
75
+ /** SVG: 返回 symbol href,如 "#icon-edit" */
76
+ getSymbolHref(name) {
77
+ if (this.options.mode !== "svg") {
78
+ throw new Error("[FontIconRegistry] getSymbolHref only works in svg mode");
79
+ }
80
+ return `#${this.symbolIdPrefix}${String(name)}`;
81
+ }
82
+ /** Font: 返回 HTML 可用的 className,例如 "anticon anticon-edit" */
83
+ getClass(name, extra) {
84
+ if (this.options.mode !== "font") {
85
+ throw new Error("[FontIconRegistry] getClass only works in font mode");
86
+ }
87
+ const base = this.classPrefix ? this.classPrefix : "";
88
+ const item = this.classPrefix ? `${this.classPrefix}-${name}` : "";
89
+ return [base, item, extra].filter(Boolean).join(" ");
90
+ }
91
+ /** Font: 返回图标的 hex 码点(不带 \u) */
92
+ getHex(name) {
93
+ if (this.options.mode !== "font") {
94
+ throw new Error("[FontIconRegistry] getHex only works in font mode");
95
+ }
96
+ const hex = this.icons[name];
97
+ if (!hex) throw new Error(`[FontIconRegistry] icon "${name}" not found`);
98
+ return hex.toLowerCase();
99
+ }
100
+ /** Font: 返回可直接渲染的字符 */
101
+ getChar(name) {
102
+ const hex = this.getHex(name);
103
+ const codePoint = parseInt(hex, 16);
104
+ return String.fromCodePoint(codePoint);
105
+ }
106
+ /** Font: 返回 HTML 实体形式,例如 "&#xe600;" */
107
+ toEntity(name) {
108
+ return `&#x${this.getHex(name)};`;
109
+ }
110
+ async initSvgSprite() {
111
+ if (this.svgInjected) return true;
112
+ if (this._svgInitPromise) return this._svgInitPromise;
113
+ this._svgInitPromise = (async () => {
114
+ try {
115
+ if (!this.spriteUrl) return false;
116
+ const mark = `font-icon-sprite:${this.spriteUrl}`;
117
+ const selector = `[data-font-icon-sprite="${CSS.escape(mark)}"]`;
118
+ if (document.querySelector(selector)) {
119
+ this.svgInjected = true;
120
+ return true;
121
+ }
122
+ const res = await fetch(this.spriteUrl, { credentials: "omit", cache: "force-cache" });
123
+ if (!res.ok) throw new Error(`fetch sprite failed: ${res.status} ${res.statusText}`);
124
+ const svgText = await res.text();
125
+ if (!svgText || !svgText.includes("<svg")) throw new Error("sprite is empty or invalid");
126
+ const container = document.createElement("div");
127
+ container.setAttribute("data-font-icon-sprite", mark);
128
+ container.style.cssText = "position:absolute;width:0;height:0;overflow:hidden;";
129
+ container.innerHTML = svgText;
130
+ const svgEl = container.querySelector("svg");
131
+ if (svgEl) {
132
+ svgEl.setAttribute("aria-hidden", "true");
133
+ svgEl.style.position = "absolute";
134
+ svgEl.style.width = "0";
135
+ svgEl.style.height = "0";
136
+ svgEl.style.overflow = "hidden";
137
+ }
138
+ const mount = document.body || document.documentElement;
139
+ mount.insertBefore(container, mount.firstChild);
140
+ this.svgInjected = true;
141
+ return true;
142
+ } catch (err) {
143
+ console.error("[FontIconRegistry] initSvgSprite failed:", err);
144
+ this._svgInitPromise = null;
145
+ return false;
146
+ }
147
+ })();
148
+ return this._svgInitPromise;
149
+ }
150
+ injectCss() {
151
+ if (!this.classPrefix) return;
152
+ const prefix = this.classPrefix;
153
+ const style = document.createElement("style");
154
+ style.setAttribute("data-font-icon", this.fontFamily);
155
+ const base = `
156
+ .${prefix} {
157
+ font-family: "${this.fontFamily}" !important;
158
+ speak: none;
159
+ font-style: normal;
160
+ font-weight: normal;
161
+ font-variant: normal;
162
+ text-rendering: auto;
163
+ -webkit-font-smoothing: antialiased;
164
+ -moz-osx-font-smoothing: grayscale;
165
+ }
166
+ `.trim();
167
+ const items = Object.entries(this.icons).map(([name, hex]) => {
168
+ return `.${prefix}-${name}::before { content: "\\${hex}"; }`;
169
+ });
170
+ style.textContent = base + "\n" + items.join("\n");
171
+ document.head.appendChild(style);
172
+ this.cssInjected = true;
173
+ }
174
+ }
175
+
176
+ const ENABLEAI_FONT_STACK = `'Roboto', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Source Han Sans SC', system-ui, -apple-system,
177
+ BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji',
178
+ sans-serif`;
179
+
180
+ export { ENABLEAI_FONT_STACK, FontIconRegistry };
package/index.js ADDED
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ if (process.env.NODE_ENV === 'production') {
4
+ module.exports = require('./dist/core.cjs.prod.js')
5
+ } else {
6
+ module.exports = require('./dist/core.cjs.js')
7
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@enableai-fonts/core",
3
+ "version": "0.1.3",
4
+ "order": 1,
5
+ "description": "",
6
+ "main": "index.js",
7
+ "types": "dist/core.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "index.js"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/core.d.ts",
15
+ "module": "./dist/core.esm-bundler.mjs",
16
+ "import": "./dist/core.esm-bundler.mjs",
17
+ "require": "./index.js"
18
+ },
19
+ "./*": "./*",
20
+ "./styles.css": "./dist/core.css"
21
+ },
22
+ "buildOptions": {
23
+ "name": "core",
24
+ "formats": [
25
+ "esm-bundler",
26
+ "cjs"
27
+ ]
28
+ },
29
+ "keywords": [],
30
+ "license": "Proprietary",
31
+ "dependencies": {
32
+ "@fontsource/roboto": "^5.2.9"
33
+ }
34
+ }