metaowl 0.4.1 → 0.6.0
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/CHANGELOG.md +50 -0
- package/README.md +267 -2
- package/build/runtime/bin/metaowl-build.js +10 -0
- package/{bin → build/runtime/bin}/metaowl-create.js +96 -177
- package/build/runtime/bin/metaowl-dev.js +10 -0
- package/build/runtime/bin/metaowl-generate.js +231 -0
- package/build/runtime/bin/metaowl-lint.js +58 -0
- package/build/runtime/bin/utils.js +68 -0
- package/build/runtime/index.js +144 -0
- package/build/runtime/modules/app-mounter.js +73 -0
- package/build/runtime/modules/auto-import.js +140 -0
- package/build/runtime/modules/cache.js +49 -0
- package/build/runtime/modules/composables.js +353 -0
- package/build/runtime/modules/constants.js +38 -0
- package/build/runtime/modules/error-boundary.js +116 -0
- package/build/runtime/modules/fetch.js +31 -0
- package/build/runtime/modules/file-router.js +207 -0
- package/build/runtime/modules/fonts.js +172 -0
- package/build/runtime/modules/forms.js +193 -0
- package/build/runtime/modules/i18n.js +180 -0
- package/build/runtime/modules/image.js +175 -0
- package/build/runtime/modules/layouts.js +214 -0
- package/build/runtime/modules/link.js +141 -0
- package/build/runtime/modules/meta.js +117 -0
- package/build/runtime/modules/odoo-rpc.js +265 -0
- package/build/runtime/modules/pwa.js +272 -0
- package/build/runtime/modules/router.js +384 -0
- package/build/runtime/modules/seo.js +186 -0
- package/build/runtime/modules/store.js +198 -0
- package/build/runtime/modules/templates-manager.js +52 -0
- package/build/runtime/modules/test-utils.js +238 -0
- package/build/runtime/vite/plugin.js +197 -0
- package/eslint.js +29 -0
- package/package.json +45 -27
- package/CONTRIBUTING.md +0 -49
- package/bin/metaowl-build.js +0 -12
- package/bin/metaowl-dev.js +0 -12
- package/bin/metaowl-generate.js +0 -339
- package/bin/metaowl-lint.js +0 -71
- package/bin/utils.js +0 -82
- package/eslint.config.js +0 -3
- package/index.js +0 -328
- package/modules/app-mounter.js +0 -104
- package/modules/auto-import.js +0 -225
- package/modules/cache.js +0 -59
- package/modules/composables.js +0 -600
- package/modules/error-boundary.js +0 -228
- package/modules/fetch.js +0 -51
- package/modules/file-router.js +0 -478
- package/modules/forms.js +0 -353
- package/modules/i18n.js +0 -333
- package/modules/layouts.js +0 -431
- package/modules/link.js +0 -255
- package/modules/meta.js +0 -119
- package/modules/odoo-rpc.js +0 -511
- package/modules/pwa.js +0 -515
- package/modules/router.js +0 -769
- package/modules/seo.js +0 -501
- package/modules/store.js +0 -409
- package/modules/templates-manager.js +0 -89
- package/modules/test-utils.js +0 -532
- package/test/auto-import.test.js +0 -110
- package/test/cache.test.js +0 -55
- package/test/composables.test.js +0 -103
- package/test/dynamic-routes.test.js +0 -469
- package/test/error-boundary.test.js +0 -126
- package/test/fetch.test.js +0 -100
- package/test/file-router.test.js +0 -55
- package/test/forms.test.js +0 -203
- package/test/i18n.test.js +0 -188
- package/test/layouts.test.js +0 -395
- package/test/link.test.js +0 -189
- package/test/meta.test.js +0 -146
- package/test/odoo-rpc.test.js +0 -547
- package/test/pwa.test.js +0 -154
- package/test/router-guards.test.js +0 -229
- package/test/router.test.js +0 -77
- package/test/seo.test.js +0 -353
- package/test/store.test.js +0 -476
- package/test/templates-manager.test.js +0 -83
- package/test/test-utils.test.js +0 -314
- package/vite/plugin.js +0 -290
- package/vitest.config.js +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,56 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.6.0] - 2026-04-24
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Nested layouts support** — layouts can now have parent-child relationships using `setParentLayout()`, `getParentLayout()`, and `getLayoutChain()`. The `createNestedLayoutWrapper()` function creates wrapper components for multi-level nesting. The `defineNestedLayout()` decorator simplifies declaring nested layouts on components.
|
|
13
|
+
- **Image optimization module** — new `modules/image.ts` with `generateSrcSet()`, `calculateAspectRatio()`, `generateSizesAttribute()`, `createResponsiveImage()`, `prefetchImage()`, `prefetchImages()`, `isImageLoaded()`, `getImageDimensions()`, `observeImageVisibility()`, `swapImageSource()`, and `generateDominantColorPlaceholder()`.
|
|
14
|
+
- **Fonts optimization module** — new `modules/fonts.ts` with `defineFontFace()`, `loadFont()`, `loadFontFamily()`, `isFontLoaded()`, `preloadFont()`, `removeFontPreload()`, `createFontFaceRule()`, `injectFontFaceRules()`, `measureTextWidth()`, `estimateFontMetrics()`, `adjustFontForFout()`, and `getFontLoadStatus()`.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **TypeScript migration completed** — the framework source, Vite integration, CLI entrypoints,
|
|
19
|
+
and test suite now use TypeScript as the primary source of truth while preserving the existing
|
|
20
|
+
public API and runtime behavior.
|
|
21
|
+
- **Runtime build output separated from source** — publishable JavaScript is now emitted to
|
|
22
|
+
`build/runtime`, and package `main`, `exports`, and `bin` entries resolve from that generated
|
|
23
|
+
runtime output.
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
- **Redundant source JavaScript files** — legacy hand-maintained `.js` source files in `modules/`,
|
|
28
|
+
`bin/`, `vite/`, and the root entrypoint were removed in favor of the TypeScript sources and the
|
|
29
|
+
generated runtime build.
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **Release build workflow** — added dedicated runtime build and release helper scripts for
|
|
34
|
+
clean runtime generation, release checks, and package dry-run validation.
|
|
35
|
+
|
|
36
|
+
## [0.5.0] - 2026-04-24
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **TypeScript migration completed** — the framework source, Vite integration, CLI entrypoints,
|
|
41
|
+
and test suite now use TypeScript as the primary source of truth while preserving the existing
|
|
42
|
+
public API and runtime behavior.
|
|
43
|
+
- **Runtime build output separated from source** — publishable JavaScript is now emitted to
|
|
44
|
+
`build/runtime`, and package `main`, `exports`, and `bin` entries resolve from that generated
|
|
45
|
+
runtime output.
|
|
46
|
+
|
|
47
|
+
### Removed
|
|
48
|
+
|
|
49
|
+
- **Redundant source JavaScript files** — legacy hand-maintained `.js` source files in `modules/`,
|
|
50
|
+
`bin/`, `vite/`, and the root entrypoint were removed in favor of the TypeScript sources and the
|
|
51
|
+
generated runtime build.
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- **Release build workflow** — added dedicated runtime build and release helper scripts for
|
|
56
|
+
clean runtime generation, release checks, and package dry-run validation.
|
|
57
|
+
|
|
8
58
|
## [0.4.1] - 2026-03-25
|
|
9
59
|
|
|
10
60
|
### Added
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ metaowl is a complete solution for building OWL applications with everything you
|
|
|
17
17
|
|
|
18
18
|
**Developer Experience:** Composables for common patterns (auth, localStorage, fetching), form handling with validation, error boundaries, and internationalization.
|
|
19
19
|
|
|
20
|
-
**SEO & PWA:** Sitemap/robots.txt generation, structured data support, service worker integration, web app manifest, and push notifications.
|
|
20
|
+
**SEO & PWA:** Sitemap/robots.txt generation, structured data support, image optimization with srcset generation, font optimization with preloading, service worker integration, web app manifest, and push notifications.
|
|
21
21
|
|
|
22
22
|
**Testing & Quality:** Mock stores, router mocking, component testing utilities, plus bundled ESLint and PostCSS configs.
|
|
23
23
|
|
|
@@ -44,6 +44,8 @@ All powered by a batteries-included Vite plugin that handles the build pipeline,
|
|
|
44
44
|
- [Auto-Import](#auto-import)
|
|
45
45
|
- [Odoo JSON-RPC Service](#odoo-json-rpc-service)
|
|
46
46
|
- [Composables / Hooks](#composables--hooks)
|
|
47
|
+
- [Image Optimization](#image-optimization)
|
|
48
|
+
- [Font Optimization](#font-optimization)
|
|
47
49
|
- [CLI Reference](#cli-reference)
|
|
48
50
|
- [API Reference](#api-reference)
|
|
49
51
|
- [boot](#bootroutes)
|
|
@@ -61,6 +63,8 @@ All powered by a batteries-included Vite plugin that handles the build pipeline,
|
|
|
61
63
|
- [Forms](#forms-api)
|
|
62
64
|
- [OdooService](#odooservice-api)
|
|
63
65
|
- [Composables](#composables-api)
|
|
66
|
+
- [Image API](#image-api)
|
|
67
|
+
- [Fonts API](#fonts-api)
|
|
64
68
|
- [Vite Plugin](#vite-plugin)
|
|
65
69
|
- [metaowlPlugin](#metaowlpluginoptions)
|
|
66
70
|
- [metaowlConfig](#metaowlconfigoptions)
|
|
@@ -93,6 +97,8 @@ All powered by a batteries-included Vite plugin that handles the build pipeline,
|
|
|
93
97
|
- **Composables** — reusable hooks for auth, localStorage, fetching, and more
|
|
94
98
|
- **Testing Utilities** — mock store, router mocking, component mount helpers
|
|
95
99
|
- **SEO Utils** — sitemap, robots.txt, JSON-LD, Open Graph, Twitter Cards
|
|
100
|
+
- **Image Optimization** — responsive srcset generation, lazy loading, placeholder support, dominant color extraction
|
|
101
|
+
- **Font Optimization** — FontFace creation, font preloading, @font-face CSS generation, FOUT handling
|
|
96
102
|
- **PWA Support** — service worker, manifest generation, push notifications
|
|
97
103
|
- **SSG generator** — statically pre-renders HTML pages with correct meta tags at build time
|
|
98
104
|
- **Vite plugin** — handles `COMPONENTS` injection, XML template copying, CSS auto-import, chunk splitting, and env filtering
|
|
@@ -117,6 +123,18 @@ npm install metaowl
|
|
|
117
123
|
|
|
118
124
|
`@odoo/owl` is bundled with metaowl and resolved automatically — no separate installation required.
|
|
119
125
|
|
|
126
|
+
### Release Workflow
|
|
127
|
+
|
|
128
|
+
For the package itself, the TypeScript sources remain the source of truth and the publishable runtime files are generated into `build/runtime`.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npm run release:check # typecheck + tests
|
|
132
|
+
npm run build:runtime # clean build/runtime and emit JS
|
|
133
|
+
npm run release:pack # full check + build + npm pack --dry-run
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`npm pack` and `npm publish` also trigger `prepack`, which rebuilds `build/runtime` automatically.
|
|
137
|
+
|
|
120
138
|
---
|
|
121
139
|
|
|
122
140
|
## Create a New Project
|
|
@@ -377,6 +395,37 @@ If no layout is specified, the `default` layout is used automatically.
|
|
|
377
395
|
</templates>
|
|
378
396
|
```
|
|
379
397
|
|
|
398
|
+
### Nested Layouts
|
|
399
|
+
|
|
400
|
+
Layouts can be nested in a parent-child hierarchy. Use `setParentLayout()` to define relationships:
|
|
401
|
+
|
|
402
|
+
```js
|
|
403
|
+
import { setParentLayout, getLayoutChain } from 'metaowl'
|
|
404
|
+
|
|
405
|
+
// Define layout hierarchy
|
|
406
|
+
setParentLayout('inner', 'middle')
|
|
407
|
+
setParentLayout('middle', 'outer')
|
|
408
|
+
|
|
409
|
+
// Get the full chain for a layout
|
|
410
|
+
const chain = getLayoutChain('inner')
|
|
411
|
+
// ['inner', 'middle', 'outer']
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Or use the `defineNestedLayout()` decorator:
|
|
415
|
+
|
|
416
|
+
```js
|
|
417
|
+
import { defineNestedLayout } from 'metaowl'
|
|
418
|
+
|
|
419
|
+
export class AdminDashboard extends Component {
|
|
420
|
+
static template = 'AdminDashboard'
|
|
421
|
+
static layout = 'admin-dashboard'
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
defineNestedLayout('admin-dashboard', 'admin-base')(AdminDashboard)
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
When a page uses `admin-dashboard` layout, it's rendered with: `outer > middle > admin-dashboard > page`.
|
|
428
|
+
|
|
380
429
|
---
|
|
381
430
|
|
|
382
431
|
## Navigation Guards
|
|
@@ -889,6 +938,98 @@ class MyComponent extends Component {
|
|
|
889
938
|
|
|
890
939
|
---
|
|
891
940
|
|
|
941
|
+
## Image Optimization
|
|
942
|
+
|
|
943
|
+
Responsive image handling with srcset generation, lazy loading, and placeholder support:
|
|
944
|
+
|
|
945
|
+
```js
|
|
946
|
+
import { Image } from 'metaowl'
|
|
947
|
+
|
|
948
|
+
// Generate srcset for responsive images
|
|
949
|
+
const srcset = Image.generateSrcSet('https://example.com/image.jpg', [320, 640, 960, 1280])
|
|
950
|
+
// "https://example.com/image.jpg?width=320&quality=80 320w, ..."
|
|
951
|
+
|
|
952
|
+
// Create a fully configured responsive image object
|
|
953
|
+
const responsive = Image.createResponsiveImage({
|
|
954
|
+
src: 'https://example.com/hero.jpg',
|
|
955
|
+
alt: 'Hero image',
|
|
956
|
+
widths: [640, 1024, 1920],
|
|
957
|
+
lazy: true,
|
|
958
|
+
placeholder: true
|
|
959
|
+
})
|
|
960
|
+
// Returns: { src, srcset, loading: 'lazy', decoding: 'async', placeholder, ... }
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
**Image utilities:**
|
|
964
|
+
|
|
965
|
+
| Function | Description |
|
|
966
|
+
|---|---|
|
|
967
|
+
| `generateSrcSet(src, widths, options)` | Generates responsive srcset string |
|
|
968
|
+
| `calculateAspectRatio(width, height)` | Computes aspect ratio as `W/H` |
|
|
969
|
+
| `generateSizesAttribute(src, breakpoints)` | Generates sizes attribute with breakpoints |
|
|
970
|
+
| `createResponsiveImage(options)` | Creates responsive image object |
|
|
971
|
+
| `prefetchImage(src)` / `prefetchImages(sources)` | Prefetch images for faster loading |
|
|
972
|
+
| `isImageLoaded(img)` | Check if image has finished loading |
|
|
973
|
+
| `getImageDimensions(src)` | Get natural width/height of image |
|
|
974
|
+
| `observeImageVisibility(img, callback)` | IntersectionObserver for lazy loading |
|
|
975
|
+
| `swapImageSource(img, newSrc, newSrcset)` | Swap src/srcset atomically |
|
|
976
|
+
| `generateDominantColorPlaceholder(src)` | Extract dominant color as placeholder |
|
|
977
|
+
|
|
978
|
+
---
|
|
979
|
+
|
|
980
|
+
## Font Optimization
|
|
981
|
+
|
|
982
|
+
Font loading and management with preloading and FOUT (Flash of Unstyled Text) handling:
|
|
983
|
+
|
|
984
|
+
```js
|
|
985
|
+
import { Fonts } from 'metaowl'
|
|
986
|
+
|
|
987
|
+
// Define and load a font
|
|
988
|
+
const fontFace = await Fonts.loadFont({
|
|
989
|
+
family: 'Inter',
|
|
990
|
+
src: 'https://fonts.example.com/inter.woff2',
|
|
991
|
+
weight: 'normal',
|
|
992
|
+
display: 'swap'
|
|
993
|
+
})
|
|
994
|
+
|
|
995
|
+
// Preload font for critical text
|
|
996
|
+
Fonts.preloadFont('Inter', 'https://fonts.example.com/inter.woff2')
|
|
997
|
+
|
|
998
|
+
// Generate @font-face CSS rule
|
|
999
|
+
const cssRule = Fonts.createFontFaceRule({
|
|
1000
|
+
family: 'Inter',
|
|
1001
|
+
src: 'https://fonts.example.com/inter.woff2',
|
|
1002
|
+
weight: 'bold',
|
|
1003
|
+
display: 'optional'
|
|
1004
|
+
})
|
|
1005
|
+
// @font-face { font-family: 'Inter'; src: url("..."); font-weight: bold; font-display: optional; }
|
|
1006
|
+
|
|
1007
|
+
// Inject font faces into document
|
|
1008
|
+
Fonts.injectFontFaceRules({
|
|
1009
|
+
family: 'Inter',
|
|
1010
|
+
src: ['woff2', 'woff'].map(f => `https://fonts.example.com/${f}`),
|
|
1011
|
+
weight: '400 700'
|
|
1012
|
+
})
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
**Font utilities:**
|
|
1016
|
+
|
|
1017
|
+
| Function | Description |
|
|
1018
|
+
|---|---|
|
|
1019
|
+
| `defineFontFace(options)` | Creates a FontFace object |
|
|
1020
|
+
| `loadFont(options)` | Loads font and tracks it |
|
|
1021
|
+
| `loadFontFamily(family, variants)` | Load multiple variants |
|
|
1022
|
+
| `isFontLoaded(family, weight?)` | Check if font is loaded |
|
|
1023
|
+
| `preloadFont(family, src, options)` | Add preload link for font |
|
|
1024
|
+
| `removeFontPreload(family, weight?)` | Remove preload link |
|
|
1025
|
+
| `createFontFaceRule(options)` | Generate @font-face CSS |
|
|
1026
|
+
| `injectFontFaceRules(options)` | Inject @font-face rules |
|
|
1027
|
+
| `measureTextWidth(text, font, size)` | Measure rendered text width |
|
|
1028
|
+
| `adjustFontForFout(el, fallback, timeout)` | Handle FOUT gracefully |
|
|
1029
|
+
| `getFontLoadStatus()` | Get loaded fonts status |
|
|
1030
|
+
|
|
1031
|
+
---
|
|
1032
|
+
|
|
892
1033
|
## CLI Reference
|
|
893
1034
|
|
|
894
1035
|
metaowl ships four CLI commands that use its own bundled Vite, Prettier, and ESLint binaries — no need to install them separately in your project.
|
|
@@ -1107,11 +1248,24 @@ Functions for layout management.
|
|
|
1107
1248
|
|
|
1108
1249
|
| Function | Description |
|
|
1109
1250
|
|---|---|
|
|
1110
|
-
| `registerLayout(name, Component)` | Register a layout |
|
|
1251
|
+
| `registerLayout(name, Component, options?)` | Register a layout |
|
|
1252
|
+
| `unregisterLayout(name)` | Remove a layout |
|
|
1111
1253
|
| `getLayout(name)` | Get layout component by name |
|
|
1254
|
+
| `hasLayout(name)` | Check if layout exists |
|
|
1255
|
+
| `getLayoutNames()` | Get all registered layout names |
|
|
1112
1256
|
| `setDefaultLayout(name)` | Set default layout |
|
|
1257
|
+
| `getDefaultLayout()` | Get default layout name |
|
|
1113
1258
|
| `resolveLayout(Component, path?)` | Resolve layout for component |
|
|
1259
|
+
| `setRouteLayout(routePath, layoutName)` | Set layout for a route |
|
|
1260
|
+
| `getRouteLayout(routePath)` | Get layout for a route |
|
|
1261
|
+
| `setParentLayout(layoutName, parentName)` | Set parent-child relationship |
|
|
1262
|
+
| `getParentLayout(layoutName)` | Get parent layout name |
|
|
1263
|
+
| `getLayoutChain(layoutName)` | Get full layout chain |
|
|
1264
|
+
| `createNestedLayoutWrapper(layouts, page, props)` | Create nested layout wrapper |
|
|
1114
1265
|
| `subscribeToLayouts(callback)` | Listen to layout events |
|
|
1266
|
+
| `clearLayouts()` | Clear all layouts |
|
|
1267
|
+
| `defineLayout(name, options?)` | Decorator for layout |
|
|
1268
|
+
| `defineNestedLayout(name, parent, options?)` | Decorator for nested layout |
|
|
1115
1269
|
|
|
1116
1270
|
**Component Layout Property:**
|
|
1117
1271
|
|
|
@@ -1446,6 +1600,117 @@ const { value, set, get, remove, clear } = useCache('user-prefs', {})
|
|
|
1446
1600
|
|
|
1447
1601
|
---
|
|
1448
1602
|
|
|
1603
|
+
### `Image API`
|
|
1604
|
+
|
|
1605
|
+
Image optimization utilities.
|
|
1606
|
+
|
|
1607
|
+
```ts
|
|
1608
|
+
import { Image } from 'metaowl'
|
|
1609
|
+
|
|
1610
|
+
// Generate srcset for responsive images
|
|
1611
|
+
Image.generateSrcSet('https://example.com/image.jpg', [320, 640, 960])
|
|
1612
|
+
|
|
1613
|
+
// Calculate aspect ratio
|
|
1614
|
+
Image.calculateAspectRatio(1920, 1080) // "16/9"
|
|
1615
|
+
|
|
1616
|
+
// Generate sizes attribute
|
|
1617
|
+
Image.generateSizesAttribute('https://example.com/image.jpg', {
|
|
1618
|
+
'(min-width: 1024px)': 1000,
|
|
1619
|
+
'(min-width: 768px)': 800
|
|
1620
|
+
})
|
|
1621
|
+
|
|
1622
|
+
// Create responsive image object
|
|
1623
|
+
const responsive = Image.createResponsiveImage({
|
|
1624
|
+
src: 'https://example.com/hero.jpg',
|
|
1625
|
+
alt: 'Hero',
|
|
1626
|
+
widths: [320, 640, 1024],
|
|
1627
|
+
lazy: true,
|
|
1628
|
+
placeholder: true
|
|
1629
|
+
})
|
|
1630
|
+
|
|
1631
|
+
// Prefetch images
|
|
1632
|
+
await Image.prefetchImages(['/img1.jpg', '/img2.jpg'])
|
|
1633
|
+
|
|
1634
|
+
// Check if loaded
|
|
1635
|
+
Image.isImageLoaded(imgElement)
|
|
1636
|
+
|
|
1637
|
+
// Get dimensions
|
|
1638
|
+
const dims = await Image.getImageDimensions('https://example.com/image.jpg')
|
|
1639
|
+
|
|
1640
|
+
// Observe visibility
|
|
1641
|
+
const observer = Image.observeImageVisibility(imgElement, (visible) => {
|
|
1642
|
+
if (visible) loadHighResImage()
|
|
1643
|
+
})
|
|
1644
|
+
|
|
1645
|
+
// Swap source
|
|
1646
|
+
Image.swapImageSource(imgElement, '/new-image.jpg', '/new-image-2x.jpg 2x')
|
|
1647
|
+
|
|
1648
|
+
// Generate dominant color placeholder
|
|
1649
|
+
const placeholder = await Image.generateDominantColorPlaceholder(src)
|
|
1650
|
+
```
|
|
1651
|
+
|
|
1652
|
+
---
|
|
1653
|
+
|
|
1654
|
+
### `Fonts API`
|
|
1655
|
+
|
|
1656
|
+
Font optimization utilities.
|
|
1657
|
+
|
|
1658
|
+
```ts
|
|
1659
|
+
import { Fonts } from 'metaowl'
|
|
1660
|
+
|
|
1661
|
+
// Define a font face
|
|
1662
|
+
const fontFace = Fonts.defineFontFace({
|
|
1663
|
+
family: 'Inter',
|
|
1664
|
+
src: 'https://fonts.example.com/inter.woff2',
|
|
1665
|
+
weight: '400',
|
|
1666
|
+
style: 'normal',
|
|
1667
|
+
display: 'swap'
|
|
1668
|
+
})
|
|
1669
|
+
|
|
1670
|
+
// Load and track a font
|
|
1671
|
+
await Fonts.loadFont({
|
|
1672
|
+
family: 'Inter',
|
|
1673
|
+
src: 'https://fonts.example.com/inter.woff2'
|
|
1674
|
+
})
|
|
1675
|
+
|
|
1676
|
+
// Check if loaded
|
|
1677
|
+
Fonts.isFontLoaded('Inter') // true
|
|
1678
|
+
|
|
1679
|
+
// Preload a font
|
|
1680
|
+
Fonts.preloadFont('Inter', 'https://fonts.example.com/inter.woff2')
|
|
1681
|
+
|
|
1682
|
+
// Remove preload link
|
|
1683
|
+
Fonts.removeFontPreload('Inter')
|
|
1684
|
+
|
|
1685
|
+
// Generate @font-face CSS rule
|
|
1686
|
+
const rule = Fonts.createFontFaceRule({
|
|
1687
|
+
family: 'Inter',
|
|
1688
|
+
src: 'https://fonts.example.com/inter.woff2',
|
|
1689
|
+
weight: 'bold'
|
|
1690
|
+
})
|
|
1691
|
+
|
|
1692
|
+
// Inject @font-face rules into document
|
|
1693
|
+
Fonts.injectFontFaceRules({
|
|
1694
|
+
family: 'Inter',
|
|
1695
|
+
src: ['woff2', 'woff'].map(f => `https://fonts.example.com/${f}`),
|
|
1696
|
+
weight: '400 700'
|
|
1697
|
+
})
|
|
1698
|
+
|
|
1699
|
+
// Measure text width
|
|
1700
|
+
Fonts.measureTextWidth('Hello', 'Inter', 16)
|
|
1701
|
+
|
|
1702
|
+
// Adjust for FOUT
|
|
1703
|
+
await Fonts.adjustFontForFout(element, 'sans-serif', 3000)
|
|
1704
|
+
|
|
1705
|
+
// Get loaded font status
|
|
1706
|
+
Fonts.getFontLoadStatus() // { 'Inter': true }
|
|
1707
|
+
|
|
1708
|
+
// Clear all loaded fonts
|
|
1709
|
+
Fonts.clearLoadedFonts()
|
|
1710
|
+
```
|
|
1711
|
+
|
|
1712
|
+
---
|
|
1713
|
+
|
|
1449
1714
|
## Vite Plugin
|
|
1450
1715
|
|
|
1451
1716
|
### `metaowlPlugin(options)`
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* metaowl build — lint then production build.
|
|
4
|
+
*/
|
|
5
|
+
import { banner, resolveBin, resolveOwnRuntimeBin, run, success } from './utils.js';
|
|
6
|
+
banner('build');
|
|
7
|
+
run('Linting', `node "${resolveOwnRuntimeBin('metaowl-lint')}"`);
|
|
8
|
+
run('Building', `"${resolveBin('vite')}" build`);
|
|
9
|
+
success('Build complete');
|
|
10
|
+
console.log();
|