@fest-lib/fl-ui 0.1.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/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/assets/OPFS.uniform.worker-Ddx6J_37.js +2982 -0
- package/dist/fl-ui.js +16004 -0
- package/dist/font-registry-BWosB92f.js +271 -0
- package/package.json +104 -0
- package/src/styles/font-loader.ts +224 -0
- package/src/styles/font-registry.ts +275 -0
- package/src/styles/fonts/_index.scss +8 -0
- package/src/styles/fonts/_inter.scss +171 -0
- package/src/styles/index.scss +2 -0
- package/src/styles/index.ts +33 -0
- package/src/styles/layers.scss +1 -0
- package/src/styles/patch-global-native-controls.scss +13 -0
- package/src/styles/scripts/encode-fonts.ts +172 -0
- package/src/styles/skins/thin/Thin.ts +13 -0
- package/src/styles/ui/_bar.scss +271 -0
- package/src/styles/ui/_buttons.scss +215 -0
- package/src/styles/ui/_color.scss +3 -0
- package/src/styles/ui/_colorize.scss +5 -0
- package/src/styles/ui/_content.scss +169 -0
- package/src/styles/ui/_essentials-options.scss +2 -0
- package/src/styles/ui/_essentials.scss +705 -0
- package/src/styles/ui/_explorer-content.scss +362 -0
- package/src/styles/ui/_explorer.scss +237 -0
- package/src/styles/ui/_forms-bare-host-elements.scss +25 -0
- package/src/styles/ui/_forms-options.scss +2 -0
- package/src/styles/ui/_forms.scss +280 -0
- package/src/styles/ui/_forms_misc.scss +201 -0
- package/src/styles/ui/_gridbox.scss +298 -0
- package/src/styles/ui/_home-host.scss +179 -0
- package/src/styles/ui/_index.scss +22 -0
- package/src/styles/ui/_launcher-typography.scss +31 -0
- package/src/styles/ui/_layers.scss +48 -0
- package/src/styles/ui/_layout.scss +3 -0
- package/src/styles/ui/_markdown.scss +1636 -0
- package/src/styles/ui/_modal.scss +258 -0
- package/src/styles/ui/_motion.scss +3 -0
- package/src/styles/ui/_native-host-button-chrome.scss +31 -0
- package/src/styles/ui/_orientation-functions.scss +274 -0
- package/src/styles/ui/_orientbox.scss +97 -0
- package/src/styles/ui/_scrollbar.scss +257 -0
- package/src/styles/ui/_shared-overlays.scss +16 -0
- package/src/styles/ui/_sidebar.scss +477 -0
- package/src/styles/ui/_speed-dial.scss +47 -0
- package/src/styles/ui/_tables.scss +28 -0
- package/src/styles/ui/_tabs.scss +406 -0
- package/src/styles/ui/_taskbar.scss +215 -0
- package/src/styles/ui/_toolbars.scss +165 -0
- package/src/styles/ui/_typography.scss +11 -0
- package/src/styles/ui/_utils.scss +8 -0
- package/src/styles/ui/_variables.scss +45 -0
- package/src/styles/ui/_viewport.scss +469 -0
- package/src/styles/ui/_window-frame.scss +658 -0
- package/src/styles/ui/home-host-apply.scss +10 -0
package/package.json
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fest-lib/fl-ui",
|
|
3
|
+
"description": "FL-UI components and services (fest-lib). Dev HTTPS: npm run dev → https://localhost:8434/?suite=explorer (OPFS). HTTP-only: npm run dev:http.",
|
|
4
|
+
"version": "0.1.1",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": true,
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/fest-live/fl.ui.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/fest-live/fl.ui/issues"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/fest-live/fl.ui#readme",
|
|
22
|
+
"keywords": [
|
|
23
|
+
"fest",
|
|
24
|
+
"fest-lib",
|
|
25
|
+
"ui",
|
|
26
|
+
"components",
|
|
27
|
+
"scss"
|
|
28
|
+
],
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"src/styles",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
34
|
+
"main": "./dist/fl-ui.js",
|
|
35
|
+
"module": "./dist/fl-ui.js",
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"import": "./dist/fl-ui.js",
|
|
39
|
+
"default": "./dist/fl-ui.js"
|
|
40
|
+
},
|
|
41
|
+
"./styles": {
|
|
42
|
+
"import": "./src/styles/_index.scss",
|
|
43
|
+
"default": "./src/styles/_index.scss"
|
|
44
|
+
},
|
|
45
|
+
"./styles/core": {
|
|
46
|
+
"import": "./src/styles/core/_index.scss",
|
|
47
|
+
"default": "./src/styles/core/_index.scss"
|
|
48
|
+
},
|
|
49
|
+
"./styles/lib": {
|
|
50
|
+
"import": "./src/styles/lib/_index.scss",
|
|
51
|
+
"default": "./src/styles/lib/_index.scss"
|
|
52
|
+
},
|
|
53
|
+
"./styles/patch-global-native-controls": {
|
|
54
|
+
"import": "./src/styles/patch-global-native-controls.scss",
|
|
55
|
+
"default": "./src/styles/patch-global-native-controls.scss"
|
|
56
|
+
},
|
|
57
|
+
"./items/*": {
|
|
58
|
+
"import": "./src/ui/items/*",
|
|
59
|
+
"default": "./src/ui/items/*"
|
|
60
|
+
},
|
|
61
|
+
"./package.json": "./package.json"
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"@fest-lib/core": "^0.1.0",
|
|
65
|
+
"@fest-lib/dom": "^0.1.0",
|
|
66
|
+
"@fest-lib/lure": "^0.1.0",
|
|
67
|
+
"@fest-lib/object": "^0.1.0",
|
|
68
|
+
"@fest-lib/uniform": "^0.1.0",
|
|
69
|
+
"@fest-lib/veela": "^0.1.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependenciesMeta": {
|
|
72
|
+
"@fest-lib/veela": {
|
|
73
|
+
"optional": true
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"scripts": {
|
|
77
|
+
"test": "vite --host 0.0.0.0",
|
|
78
|
+
"dev": "vite --host 0.0.0.0",
|
|
79
|
+
"dev:http": "VIEW_DEV_HTTP=1 vite --host 0.0.0.0",
|
|
80
|
+
"dev:8434": "VIEW_DEV_PORT=8434 vite --host 0.0.0.0",
|
|
81
|
+
"watch": "vite build --watch",
|
|
82
|
+
"build": "vite build --config vite.config.js && npm run docs && npm run docs:md",
|
|
83
|
+
"build:publish": "FEST_NPM_IMPORTS=1 vite build --config vite.config.js",
|
|
84
|
+
"prepack": "npm run build:publish",
|
|
85
|
+
"docs": "typedoc ./src/index.ts --options ./typedoc.json --out ./docs --tsconfig ./tsconfig.json --excludeExternals --excludePrivate --excludeProtected --skipErrorChecking",
|
|
86
|
+
"docs:clean": "rm -rf ./docs && typedoc --options ./typedoc.json",
|
|
87
|
+
"docs:md": "typedoc --options ./typedoc.md.json",
|
|
88
|
+
"docs:md:clean": "rm -rf ./docs-md && typedoc --options ./typedoc.md.json",
|
|
89
|
+
"preview": "vite preview",
|
|
90
|
+
"release": "PUPPETEER_SKIP_DOWNLOAD=1 npm run build:publish && npm publish --access public --ignore-scripts",
|
|
91
|
+
"publish": "npm run release"
|
|
92
|
+
},
|
|
93
|
+
"devDependencies": {
|
|
94
|
+
"@types/node": ">=22.8.1",
|
|
95
|
+
"@vitejs/plugin-basic-ssl": "^2.1.0",
|
|
96
|
+
"puppeteer": ">=24.42.0",
|
|
97
|
+
"sass": ">=1.93.3",
|
|
98
|
+
"typedoc": "^0.28.20",
|
|
99
|
+
"typedoc-plugin-markdown": "^4.12.0",
|
|
100
|
+
"typescript": ">=6.0.0-beta",
|
|
101
|
+
"vite": ">=8.0.10",
|
|
102
|
+
"vite-plugin-external": ">=6.2.2"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font Loader Module
|
|
3
|
+
*
|
|
4
|
+
* Loads fonts from base64-encoded, compressed data using:
|
|
5
|
+
* - Compression Streams API for decompression
|
|
6
|
+
* - Blob URL for caching
|
|
7
|
+
* - FontFace API (CSS Font Loading API) for font loading
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Font metadata for loading
|
|
12
|
+
*/
|
|
13
|
+
export interface FontMetadata {
|
|
14
|
+
/** Base64-encoded, compressed font data */
|
|
15
|
+
base64: string;
|
|
16
|
+
/** Font family name */
|
|
17
|
+
family: string;
|
|
18
|
+
/** Font style (normal, italic, etc.) */
|
|
19
|
+
style?: string;
|
|
20
|
+
/** Font weight (100-900 or "100 900" for variable fonts) */
|
|
21
|
+
weight?: string | number;
|
|
22
|
+
/** Whether the font is compressed (requires decompression) */
|
|
23
|
+
compressed?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Cache for Blob URLs to avoid re-creating them
|
|
28
|
+
*/
|
|
29
|
+
const blobUrlCache = new Map<string, string>();
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Cache for FontFace instances
|
|
33
|
+
*/
|
|
34
|
+
const fontFaceCache = new Map<string, FontFace>();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Decode base64 string to Uint8Array
|
|
38
|
+
* Uses Uint8Array.fromBase64 if available, otherwise falls back to atob
|
|
39
|
+
*/
|
|
40
|
+
function decodeBase64(base64: string): Uint8Array {
|
|
41
|
+
// Check if Uint8Array.fromBase64 is available (ES2024+)
|
|
42
|
+
if (typeof Uint8Array.fromBase64 === 'function') {
|
|
43
|
+
return Uint8Array.fromBase64(base64);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Fallback: use atob for older browsers
|
|
47
|
+
const binaryString = atob(base64);
|
|
48
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
49
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
50
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
51
|
+
}
|
|
52
|
+
return bytes;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Decompress data using Compression Streams API
|
|
57
|
+
* Only used for fonts that were compressed (e.g., gzip)
|
|
58
|
+
* woff2 files are already compressed and don't need decompression
|
|
59
|
+
*/
|
|
60
|
+
async function decompress(data: Uint8Array, algorithm: CompressionFormat = 'gzip'): Promise<Uint8Array> {
|
|
61
|
+
// Check if CompressionStream is available
|
|
62
|
+
if (typeof CompressionStream === 'undefined') {
|
|
63
|
+
throw new Error('Compression Streams API is not supported in this browser');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Use DecompressionStream for decompression
|
|
67
|
+
const stream = new DecompressionStream(algorithm);
|
|
68
|
+
const writer = stream.writable.getWriter();
|
|
69
|
+
const reader = stream.readable.getReader();
|
|
70
|
+
|
|
71
|
+
// Write compressed data
|
|
72
|
+
writer.write(data);
|
|
73
|
+
writer.close();
|
|
74
|
+
|
|
75
|
+
// Read decompressed chunks
|
|
76
|
+
const chunks: Uint8Array[] = [];
|
|
77
|
+
let done = false;
|
|
78
|
+
|
|
79
|
+
while (!done) {
|
|
80
|
+
const { value, done: readerDone } = await reader.read();
|
|
81
|
+
done = readerDone;
|
|
82
|
+
if (value) {
|
|
83
|
+
chunks.push(value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Combine chunks
|
|
88
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
89
|
+
const result = new Uint8Array(totalLength);
|
|
90
|
+
let offset = 0;
|
|
91
|
+
for (const chunk of chunks) {
|
|
92
|
+
result.set(chunk, offset);
|
|
93
|
+
offset += chunk.length;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get or create a Blob URL from font data
|
|
101
|
+
* Caches the URL to avoid re-creating Blobs
|
|
102
|
+
*/
|
|
103
|
+
async function getBlobUrl(fontData: Uint8Array, cacheKey: string, mimeType: string = 'font/woff2'): Promise<string> {
|
|
104
|
+
// Check cache first
|
|
105
|
+
if (blobUrlCache.has(cacheKey)) {
|
|
106
|
+
return blobUrlCache.get(cacheKey)!;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Create Blob and URL
|
|
110
|
+
const blob = new Blob([fontData], { type: mimeType });
|
|
111
|
+
const url = URL.createObjectURL(blob);
|
|
112
|
+
|
|
113
|
+
// Cache the URL
|
|
114
|
+
blobUrlCache.set(cacheKey, url);
|
|
115
|
+
|
|
116
|
+
return url;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Load a font from base64-encoded, compressed data
|
|
121
|
+
*/
|
|
122
|
+
export async function loadFont(metadata: FontMetadata): Promise<FontFace> {
|
|
123
|
+
const { base64, family, style = 'normal', weight = 'normal', compressed = false } = metadata;
|
|
124
|
+
|
|
125
|
+
// Create cache key
|
|
126
|
+
const cacheKey = `${family}-${style}-${weight}`;
|
|
127
|
+
|
|
128
|
+
// Check if FontFace is already cached
|
|
129
|
+
if (fontFaceCache.has(cacheKey)) {
|
|
130
|
+
return fontFaceCache.get(cacheKey)!;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Decode base64 to Uint8Array
|
|
134
|
+
const encodedData = decodeBase64(base64);
|
|
135
|
+
|
|
136
|
+
// Decompress if needed (woff2 files are already compressed, don't decompress)
|
|
137
|
+
// Only decompress if explicitly marked as compressed (e.g., gzip)
|
|
138
|
+
const fontData = compressed ? await decompress(encodedData) : encodedData;
|
|
139
|
+
|
|
140
|
+
// Determine MIME type based on compression
|
|
141
|
+
// woff2 files are already compressed, so if not compressed, assume woff2
|
|
142
|
+
const mimeType = compressed ? 'application/octet-stream' : 'font/woff2';
|
|
143
|
+
|
|
144
|
+
// Get or create Blob URL
|
|
145
|
+
const blobUrl = await getBlobUrl(fontData, cacheKey, mimeType);
|
|
146
|
+
|
|
147
|
+
// Create FontFace instance
|
|
148
|
+
const fontFace = new FontFace(
|
|
149
|
+
family,
|
|
150
|
+
`url(${blobUrl}) format('woff2')`,
|
|
151
|
+
{
|
|
152
|
+
style,
|
|
153
|
+
weight: typeof weight === 'string' ? weight : `${weight}`,
|
|
154
|
+
display: 'swap'
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// Load the font
|
|
159
|
+
await fontFace.load();
|
|
160
|
+
|
|
161
|
+
// Add to document.fonts
|
|
162
|
+
document.fonts.add(fontFace);
|
|
163
|
+
|
|
164
|
+
// Cache the FontFace instance
|
|
165
|
+
fontFaceCache.set(cacheKey, fontFace);
|
|
166
|
+
|
|
167
|
+
return fontFace;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Load multiple fonts
|
|
172
|
+
*/
|
|
173
|
+
export async function loadFonts(metadataArray: FontMetadata[]): Promise<FontFace[]> {
|
|
174
|
+
const promises = metadataArray.map(metadata => loadFont(metadata));
|
|
175
|
+
return Promise.all(promises);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let loadingFontRegistry: any = null;
|
|
179
|
+
export async function loadFontRegistry(): Promise<any> {
|
|
180
|
+
if (loadingFontRegistry) {
|
|
181
|
+
return loadingFontRegistry;
|
|
182
|
+
}
|
|
183
|
+
loadingFontRegistry = import("./font-registry")?.catch?.((error: any) => { console.error("Failed to load font registry:", error); });;
|
|
184
|
+
return loadingFontRegistry;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Load all fonts from the registry
|
|
189
|
+
*/
|
|
190
|
+
export async function loadAllFonts(): Promise<FontFace[]> {
|
|
191
|
+
const fontRegistry = await loadFontRegistry();
|
|
192
|
+
const metadataArray = Object.values(fontRegistry.fontRegistry as Record<string, FontMetadata>);
|
|
193
|
+
return loadFonts(metadataArray);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Load fonts by family name
|
|
198
|
+
*/
|
|
199
|
+
export async function loadFontsByFamily(family: string): Promise<FontFace[]> {
|
|
200
|
+
const fontRegistry = await loadFontRegistry();
|
|
201
|
+
const metadataArray = Object.values(fontRegistry.fontRegistry as Record<string, FontMetadata>).filter(
|
|
202
|
+
metadata => metadata.family === family
|
|
203
|
+
);
|
|
204
|
+
return loadFonts(metadataArray);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Clear font caches (useful for testing or memory management)
|
|
209
|
+
*/
|
|
210
|
+
export function clearFontCache(): void {
|
|
211
|
+
// Revoke all Blob URLs
|
|
212
|
+
for (const url of blobUrlCache.values()) {
|
|
213
|
+
URL.revokeObjectURL(url);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
blobUrlCache.clear();
|
|
217
|
+
fontFaceCache.clear();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Font data registry (populated by Vite plugin)
|
|
222
|
+
* Import from generated font-registry module
|
|
223
|
+
*/
|
|
224
|
+
//export { fontRegistry } from './font-registry';
|