@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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/assets/OPFS.uniform.worker-Ddx6J_37.js +2982 -0
  4. package/dist/fl-ui.js +16004 -0
  5. package/dist/font-registry-BWosB92f.js +271 -0
  6. package/package.json +104 -0
  7. package/src/styles/font-loader.ts +224 -0
  8. package/src/styles/font-registry.ts +275 -0
  9. package/src/styles/fonts/_index.scss +8 -0
  10. package/src/styles/fonts/_inter.scss +171 -0
  11. package/src/styles/index.scss +2 -0
  12. package/src/styles/index.ts +33 -0
  13. package/src/styles/layers.scss +1 -0
  14. package/src/styles/patch-global-native-controls.scss +13 -0
  15. package/src/styles/scripts/encode-fonts.ts +172 -0
  16. package/src/styles/skins/thin/Thin.ts +13 -0
  17. package/src/styles/ui/_bar.scss +271 -0
  18. package/src/styles/ui/_buttons.scss +215 -0
  19. package/src/styles/ui/_color.scss +3 -0
  20. package/src/styles/ui/_colorize.scss +5 -0
  21. package/src/styles/ui/_content.scss +169 -0
  22. package/src/styles/ui/_essentials-options.scss +2 -0
  23. package/src/styles/ui/_essentials.scss +705 -0
  24. package/src/styles/ui/_explorer-content.scss +362 -0
  25. package/src/styles/ui/_explorer.scss +237 -0
  26. package/src/styles/ui/_forms-bare-host-elements.scss +25 -0
  27. package/src/styles/ui/_forms-options.scss +2 -0
  28. package/src/styles/ui/_forms.scss +280 -0
  29. package/src/styles/ui/_forms_misc.scss +201 -0
  30. package/src/styles/ui/_gridbox.scss +298 -0
  31. package/src/styles/ui/_home-host.scss +179 -0
  32. package/src/styles/ui/_index.scss +22 -0
  33. package/src/styles/ui/_launcher-typography.scss +31 -0
  34. package/src/styles/ui/_layers.scss +48 -0
  35. package/src/styles/ui/_layout.scss +3 -0
  36. package/src/styles/ui/_markdown.scss +1636 -0
  37. package/src/styles/ui/_modal.scss +258 -0
  38. package/src/styles/ui/_motion.scss +3 -0
  39. package/src/styles/ui/_native-host-button-chrome.scss +31 -0
  40. package/src/styles/ui/_orientation-functions.scss +274 -0
  41. package/src/styles/ui/_orientbox.scss +97 -0
  42. package/src/styles/ui/_scrollbar.scss +257 -0
  43. package/src/styles/ui/_shared-overlays.scss +16 -0
  44. package/src/styles/ui/_sidebar.scss +477 -0
  45. package/src/styles/ui/_speed-dial.scss +47 -0
  46. package/src/styles/ui/_tables.scss +28 -0
  47. package/src/styles/ui/_tabs.scss +406 -0
  48. package/src/styles/ui/_taskbar.scss +215 -0
  49. package/src/styles/ui/_toolbars.scss +165 -0
  50. package/src/styles/ui/_typography.scss +11 -0
  51. package/src/styles/ui/_utils.scss +8 -0
  52. package/src/styles/ui/_variables.scss +45 -0
  53. package/src/styles/ui/_viewport.scss +469 -0
  54. package/src/styles/ui/_window-frame.scss +658 -0
  55. 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';