nuxt-i18n-micro 3.26.0 → 3.28.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/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/{p3h4sA5-.js → 310ILlAI.js} +1 -1
- package/dist/client/_nuxt/{Bwak6g3b.js → B-YzqMkr.js} +2 -2
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/474d10ab-3098-4ac2-b6bf-953a2bab350d.json +1 -0
- package/dist/client/_nuxt/{entry.CJGrD2VA.css → entry.DYT8mjOq.css} +1 -1
- package/dist/client/_nuxt/error-404.BSWsfFTk.css +1 -0
- package/dist/client/_nuxt/error-500.D5pBYfcl.css +1 -0
- package/dist/client/_nuxt/{DyIrbWYh.js → h3a7H7XS.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +36 -7
- package/dist/runtime/devtools/bridge.d.ts +11 -0
- package/dist/runtime/devtools/bridge.js +8 -0
- package/dist/runtime/devtools/vue-devtools.d.ts +18 -0
- package/dist/runtime/devtools/vue-devtools.js +261 -0
- package/dist/runtime/plugins/01.plugin.js +9 -0
- package/dist/runtime/plugins/04.vue-devtools.d.ts +2 -0
- package/dist/runtime/plugins/04.vue-devtools.js +60 -0
- package/dist/runtime/utils/nuxt-i18n.d.ts +24 -2
- package/dist/runtime/utils/nuxt-i18n.js +72 -7
- package/package.json +24 -23
- package/dist/client/_nuxt/builds/meta/24f3c578-f02b-41bc-953a-c7ff7b504d56.json +0 -1
- package/dist/client/_nuxt/error-404.Cr1POox6.css +0 -1
- package/dist/client/_nuxt/error-500.BcWSpGYy.css +0 -1
|
@@ -10,10 +10,19 @@ export interface RouteTranslationContext {
|
|
|
10
10
|
}
|
|
11
11
|
export type GetRouteTranslationContext = (route?: unknown) => RouteTranslationContext;
|
|
12
12
|
export type LoadTranslationsChunk = (locale: string, routeName?: string) => Promise<Record<string, unknown>>;
|
|
13
|
+
/** Why the active translation context changed — used by Vue DevTools timeline. */
|
|
14
|
+
export type I18nContextChangeReason = 'load' | 'refresh';
|
|
13
15
|
export interface NuxtI18nOptions extends BaseI18nOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Cap for `storage.translations` chunk entries (`locale:route`).
|
|
18
|
+
* `0` means no limit. Same module option as the fetch/server `CacheControl` layers.
|
|
19
|
+
* @default 0
|
|
20
|
+
*/
|
|
21
|
+
cacheMaxSize?: number;
|
|
14
22
|
}
|
|
15
23
|
export declare class NuxtI18n extends BaseI18n {
|
|
16
24
|
readonly storage: TranslationStorage;
|
|
25
|
+
private readonly cacheMaxSize;
|
|
17
26
|
private readonly contextSignal;
|
|
18
27
|
private cachedTranslations;
|
|
19
28
|
/**
|
|
@@ -26,6 +35,8 @@ export declare class NuxtI18n extends BaseI18n {
|
|
|
26
35
|
private currentLocale;
|
|
27
36
|
private currentRouteName;
|
|
28
37
|
private resolveRouteContext?;
|
|
38
|
+
private readonly contextListeners;
|
|
39
|
+
private missingKeyListener?;
|
|
29
40
|
constructor(options?: NuxtI18nOptions);
|
|
30
41
|
setRouteContextResolver(resolver: GetRouteTranslationContext): void;
|
|
31
42
|
getCacheKey(locale: string, routeName?: string): string;
|
|
@@ -43,11 +54,22 @@ export declare class NuxtI18n extends BaseI18n {
|
|
|
43
54
|
seedChunks(chunks: Record<string, Record<string, unknown>>): void;
|
|
44
55
|
hasChunk(locale: string, routeName?: string): boolean;
|
|
45
56
|
mergeChunk(locale: string, routeName: string | undefined, data: Record<string, unknown>): Record<string, unknown>;
|
|
57
|
+
/**
|
|
58
|
+
* FIFO eviction for `storage.translations` when `cacheMaxSize` is set.
|
|
59
|
+
* Prefers keeping the just-written key and the active page chunk. When those
|
|
60
|
+
* two differ and `max` cannot hold both (e.g. `cacheMaxSize: 1`), the just-written
|
|
61
|
+
* key wins so the configured bound is always enforced. Active `$t` still resolves
|
|
62
|
+
* via `cachedTranslations` / `outgoingTranslations` even if the Map drops the old key.
|
|
63
|
+
*/
|
|
64
|
+
private evictChunks;
|
|
46
65
|
getLocale(): string;
|
|
47
66
|
getFallbackLocale(): string;
|
|
48
67
|
getRoute(): string;
|
|
49
68
|
getCurrentLocale(): string;
|
|
50
69
|
getCurrentRouteName(): string;
|
|
70
|
+
onContextChange(listener: (reason: I18nContextChangeReason) => void): () => void;
|
|
71
|
+
onMissingKey(listener: (locale: string, key: string, routeName: string) => void): () => void;
|
|
72
|
+
private notifyContextChange;
|
|
51
73
|
/**
|
|
52
74
|
* Hot-reload the active dictionary for the next route — replace, don't merge.
|
|
53
75
|
*
|
|
@@ -82,11 +104,11 @@ export declare class NuxtI18n extends BaseI18n {
|
|
|
82
104
|
* first makes the change vanish on the next navigation.
|
|
83
105
|
*/
|
|
84
106
|
setTranslation(key: TranslationKey, value: unknown): void;
|
|
85
|
-
protected getMissingContext(
|
|
107
|
+
protected getMissingContext(routeContext?: unknown): {
|
|
86
108
|
locale: string;
|
|
87
109
|
routeName: string;
|
|
88
110
|
};
|
|
89
|
-
protected warnMissing(key: TranslationKey): void;
|
|
111
|
+
protected warnMissing(key: TranslationKey, routeContext?: unknown): void;
|
|
90
112
|
clearCache(): void;
|
|
91
113
|
}
|
|
92
114
|
export interface NuxtTranslationLoaderOptions {
|
|
@@ -32,6 +32,7 @@ function extractSwitchLocalePath(localeRoute, resolveRouter) {
|
|
|
32
32
|
}
|
|
33
33
|
export class NuxtI18n extends BaseI18n {
|
|
34
34
|
storage;
|
|
35
|
+
cacheMaxSize;
|
|
35
36
|
contextSignal;
|
|
36
37
|
cachedTranslations = {};
|
|
37
38
|
/**
|
|
@@ -44,12 +45,16 @@ export class NuxtI18n extends BaseI18n {
|
|
|
44
45
|
currentLocale = "";
|
|
45
46
|
currentRouteName = "";
|
|
46
47
|
resolveRouteContext;
|
|
48
|
+
contextListeners = /* @__PURE__ */ new Set();
|
|
49
|
+
missingKeyListener;
|
|
47
50
|
constructor(options = {}) {
|
|
51
|
+
const { cacheMaxSize = 0, ...baseOptions } = options;
|
|
48
52
|
const storage = {
|
|
49
53
|
translations: /* @__PURE__ */ new Map()
|
|
50
54
|
};
|
|
51
|
-
super({ ...
|
|
55
|
+
super({ ...baseOptions, storage });
|
|
52
56
|
this.storage = storage;
|
|
57
|
+
this.cacheMaxSize = cacheMaxSize;
|
|
53
58
|
this.contextSignal = shallowRef(0);
|
|
54
59
|
}
|
|
55
60
|
setRouteContextResolver(resolver) {
|
|
@@ -62,7 +67,9 @@ export class NuxtI18n extends BaseI18n {
|
|
|
62
67
|
return this.storage.translations.get(this.getCacheKey(locale, routeName)) ?? {};
|
|
63
68
|
}
|
|
64
69
|
setChunk(locale, routeName, data) {
|
|
65
|
-
this.
|
|
70
|
+
const key = this.getCacheKey(locale, routeName);
|
|
71
|
+
this.storage.translations.set(key, data);
|
|
72
|
+
this.evictChunks(key);
|
|
66
73
|
}
|
|
67
74
|
/**
|
|
68
75
|
* Install SSR chunks from the payload into the view layer.
|
|
@@ -74,11 +81,14 @@ export class NuxtI18n extends BaseI18n {
|
|
|
74
81
|
* Existing entries win: anything already loaded is kept as-is.
|
|
75
82
|
*/
|
|
76
83
|
seedChunks(chunks) {
|
|
84
|
+
let lastKey = "";
|
|
77
85
|
for (const [cacheKey, data] of Object.entries(chunks)) {
|
|
78
86
|
if (!this.storage.translations.has(cacheKey)) {
|
|
79
87
|
this.storage.translations.set(cacheKey, data);
|
|
88
|
+
lastKey = cacheKey;
|
|
80
89
|
}
|
|
81
90
|
}
|
|
91
|
+
if (lastKey) this.evictChunks(lastKey);
|
|
82
92
|
}
|
|
83
93
|
hasChunk(locale, routeName) {
|
|
84
94
|
return this.storage.translations.has(this.getCacheKey(locale, routeName));
|
|
@@ -88,8 +98,32 @@ export class NuxtI18n extends BaseI18n {
|
|
|
88
98
|
const existing = this.storage.translations.get(cacheKey) ?? {};
|
|
89
99
|
const merged = mergeTranslationChunk(existing, data);
|
|
90
100
|
this.storage.translations.set(cacheKey, merged);
|
|
101
|
+
this.evictChunks(cacheKey);
|
|
91
102
|
return merged;
|
|
92
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* FIFO eviction for `storage.translations` when `cacheMaxSize` is set.
|
|
106
|
+
* Prefers keeping the just-written key and the active page chunk. When those
|
|
107
|
+
* two differ and `max` cannot hold both (e.g. `cacheMaxSize: 1`), the just-written
|
|
108
|
+
* key wins so the configured bound is always enforced. Active `$t` still resolves
|
|
109
|
+
* via `cachedTranslations` / `outgoingTranslations` even if the Map drops the old key.
|
|
110
|
+
*/
|
|
111
|
+
evictChunks(keep) {
|
|
112
|
+
const max = this.cacheMaxSize;
|
|
113
|
+
if (max <= 0 || this.storage.translations.size <= max) return;
|
|
114
|
+
const current = this.getCacheKey(this.currentLocale, this.currentRouteName);
|
|
115
|
+
for (const key of this.storage.translations.keys()) {
|
|
116
|
+
if (this.storage.translations.size <= max) break;
|
|
117
|
+
if (key === keep || key === current) continue;
|
|
118
|
+
this.storage.translations.delete(key);
|
|
119
|
+
}
|
|
120
|
+
if (this.storage.translations.size <= max) return;
|
|
121
|
+
for (const key of this.storage.translations.keys()) {
|
|
122
|
+
if (this.storage.translations.size <= max) break;
|
|
123
|
+
if (key === keep) continue;
|
|
124
|
+
this.storage.translations.delete(key);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
93
127
|
getLocale() {
|
|
94
128
|
return this.currentLocale;
|
|
95
129
|
}
|
|
@@ -105,6 +139,25 @@ export class NuxtI18n extends BaseI18n {
|
|
|
105
139
|
getCurrentRouteName() {
|
|
106
140
|
return this.currentRouteName;
|
|
107
141
|
}
|
|
142
|
+
onContextChange(listener) {
|
|
143
|
+
this.contextListeners.add(listener);
|
|
144
|
+
return () => {
|
|
145
|
+
this.contextListeners.delete(listener);
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
onMissingKey(listener) {
|
|
149
|
+
this.missingKeyListener = listener;
|
|
150
|
+
return () => {
|
|
151
|
+
if (this.missingKeyListener === listener) {
|
|
152
|
+
this.missingKeyListener = void 0;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
notifyContextChange(reason) {
|
|
157
|
+
for (const listener of this.contextListeners) {
|
|
158
|
+
listener(reason);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
108
161
|
/**
|
|
109
162
|
* Hot-reload the active dictionary for the next route — replace, don't merge.
|
|
110
163
|
*
|
|
@@ -116,29 +169,33 @@ export class NuxtI18n extends BaseI18n {
|
|
|
116
169
|
* and correct for the short hot-reload window.
|
|
117
170
|
*/
|
|
118
171
|
applySwitchContext(locale, routeName, data) {
|
|
119
|
-
this.setChunk(locale, routeName, data);
|
|
120
172
|
this.outgoingTranslations = this.currentLocale === locale ? this.cachedTranslations : null;
|
|
121
173
|
this.cachedTranslations = data;
|
|
122
174
|
this.currentLocale = locale;
|
|
123
175
|
this.currentRouteName = routeName || "";
|
|
176
|
+
this.setChunk(locale, routeName, data);
|
|
124
177
|
triggerRef(this.contextSignal);
|
|
178
|
+
this.notifyContextChange("load");
|
|
125
179
|
}
|
|
126
180
|
/** The outgoing page has unmounted, so its keys must stop resolving. */
|
|
127
181
|
finishTransition() {
|
|
128
182
|
if (this.outgoingTranslations === null) return;
|
|
129
183
|
this.outgoingTranslations = null;
|
|
130
184
|
triggerRef(this.contextSignal);
|
|
185
|
+
this.notifyContextChange("refresh");
|
|
131
186
|
}
|
|
132
187
|
mergeTranslations(newTranslations) {
|
|
133
188
|
this.mergeChunk(this.currentLocale, this.currentRouteName, newTranslations);
|
|
134
189
|
this.cachedTranslations = deepMergeTranslationsRecursive(this.cachedTranslations, newTranslations);
|
|
135
190
|
triggerRef(this.contextSignal);
|
|
191
|
+
this.notifyContextChange("load");
|
|
136
192
|
}
|
|
137
193
|
async loadPageTranslations(locale, routeName, translations) {
|
|
138
194
|
this.mergeChunk(locale, routeName, translations);
|
|
139
195
|
if (locale === this.currentLocale && routeName === this.currentRouteName) {
|
|
140
196
|
this.cachedTranslations = deepMergeTranslationsRecursive(this.cachedTranslations, translations);
|
|
141
197
|
triggerRef(this.contextSignal);
|
|
198
|
+
this.notifyContextChange("load");
|
|
142
199
|
}
|
|
143
200
|
}
|
|
144
201
|
async mergeTranslationAsync(locale, routeName, newTranslations, loadIfMissing) {
|
|
@@ -150,6 +207,7 @@ export class NuxtI18n extends BaseI18n {
|
|
|
150
207
|
if (locale === this.currentLocale && routeName === this.currentRouteName) {
|
|
151
208
|
this.cachedTranslations = deepMergeTranslationsRecursive(this.cachedTranslations, newTranslations);
|
|
152
209
|
triggerRef(this.contextSignal);
|
|
210
|
+
this.notifyContextChange("load");
|
|
153
211
|
}
|
|
154
212
|
}
|
|
155
213
|
tForRoute(route) {
|
|
@@ -201,18 +259,24 @@ export class NuxtI18n extends BaseI18n {
|
|
|
201
259
|
setTranslationAtKey(this.getChunk(this.currentLocale, this.currentRouteName), String(key), value)
|
|
202
260
|
);
|
|
203
261
|
triggerRef(this.contextSignal);
|
|
262
|
+
this.notifyContextChange("refresh");
|
|
204
263
|
}
|
|
205
|
-
getMissingContext(
|
|
264
|
+
getMissingContext(routeContext) {
|
|
265
|
+
if (routeContext && this.resolveRouteContext) {
|
|
266
|
+
return this.resolveRouteContext(routeContext);
|
|
267
|
+
}
|
|
206
268
|
return { locale: this.currentLocale, routeName: this.currentRouteName };
|
|
207
269
|
}
|
|
208
|
-
warnMissing(key) {
|
|
270
|
+
warnMissing(key, routeContext) {
|
|
271
|
+
const { locale, routeName } = this.getMissingContext(routeContext);
|
|
272
|
+
this.missingKeyListener?.(locale, String(key), routeName);
|
|
209
273
|
const customHandler = this.getCustomMissingHandler?.();
|
|
210
274
|
if (customHandler) {
|
|
211
|
-
customHandler(
|
|
275
|
+
customHandler(locale, String(key), routeName);
|
|
212
276
|
return;
|
|
213
277
|
}
|
|
214
278
|
if (this.missingWarn && process.env.NODE_ENV !== "production" && import.meta.client) {
|
|
215
|
-
console.warn(`[i18n] Missing key '${key}' in '${
|
|
279
|
+
console.warn(`[i18n] Missing key '${key}' in '${locale}' for route '${routeName}'`);
|
|
216
280
|
}
|
|
217
281
|
}
|
|
218
282
|
clearCache() {
|
|
@@ -220,6 +284,7 @@ export class NuxtI18n extends BaseI18n {
|
|
|
220
284
|
this.cachedTranslations = {};
|
|
221
285
|
this.outgoingTranslations = null;
|
|
222
286
|
triggerRef(this.contextSignal);
|
|
287
|
+
this.notifyContextChange("refresh");
|
|
223
288
|
}
|
|
224
289
|
}
|
|
225
290
|
export class NuxtTranslationLoader {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-i18n-micro",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.28.0",
|
|
4
|
+
"description": "Fast, lightweight i18n for Nuxt with strategy-based routing and minimal overhead.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"i18n",
|
|
7
7
|
"internationalization",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"seo",
|
|
15
15
|
"translation"
|
|
16
16
|
],
|
|
17
|
-
"homepage": "https://github.
|
|
17
|
+
"homepage": "https://s00d.github.io/nuxt-i18n-micro/",
|
|
18
18
|
"bugs": {
|
|
19
19
|
"url": "https://github.com/s00d/nuxt-i18n-micro/issues"
|
|
20
20
|
},
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"repository": "s00d/nuxt-i18n-micro",
|
|
28
28
|
"workspaces": [
|
|
29
29
|
"client",
|
|
30
|
+
"docs",
|
|
30
31
|
"packages/**",
|
|
31
32
|
"test/fixtures/**/*"
|
|
32
33
|
],
|
|
@@ -81,38 +82,38 @@
|
|
|
81
82
|
"sirv": "^2.0.3",
|
|
82
83
|
"ufo": "^1.5.4",
|
|
83
84
|
"unplugin": "^1.16.1 || ^2.0.0 || ^3.3.0",
|
|
84
|
-
"@i18n-micro/
|
|
85
|
-
"@i18n-micro/
|
|
86
|
-
"@i18n-micro/
|
|
87
|
-
"@i18n-micro/
|
|
88
|
-
"@i18n-micro/
|
|
89
|
-
"@i18n-micro/utils": "^1.0.
|
|
90
|
-
"@i18n-micro/
|
|
85
|
+
"@i18n-micro/route-strategy": "^1.1.15",
|
|
86
|
+
"@i18n-micro/core": "^1.3.13",
|
|
87
|
+
"@i18n-micro/hmr": "^1.0.7",
|
|
88
|
+
"@i18n-micro/types": "^1.2.11",
|
|
89
|
+
"@i18n-micro/path-strategy": "^1.3.10",
|
|
90
|
+
"@i18n-micro/utils": "^1.0.14",
|
|
91
|
+
"@i18n-micro/test-utils": "^1.2.9"
|
|
91
92
|
},
|
|
92
93
|
"devDependencies": {
|
|
93
94
|
"@nuxt/devtools": "^3.3.1",
|
|
94
95
|
"@nuxt/devtools-ui-kit": "^3.3.1",
|
|
96
|
+
"@vue/devtools-api": "^8.1.5",
|
|
95
97
|
"@nuxt/module-builder": "^1.0.0",
|
|
96
98
|
"@nuxt/schema": "^3.18.1 || ^4.0.0",
|
|
97
99
|
"@nuxt/test-utils": "^4.0.3",
|
|
98
100
|
"@playwright/test": "^1.55.0",
|
|
99
101
|
"@types/node": "^20.19.26",
|
|
100
102
|
"changelogen": "^0.5.7",
|
|
101
|
-
"chart.js": "^4.5.1",
|
|
102
|
-
"mermaid": "^11.12.2",
|
|
103
103
|
"oxfmt": "^0.43.0",
|
|
104
104
|
"oxlint": "^1.58.0",
|
|
105
105
|
"typescript": "^5.9.3",
|
|
106
|
-
"vitepress": "^2.0.0-alpha.18",
|
|
107
|
-
"vitepress-plugin-chartjs": "^1.2.1",
|
|
108
|
-
"vitepress-plugin-folder-tree": "^1.2.1",
|
|
109
|
-
"vitepress-plugin-llms": "^1.13.3",
|
|
110
|
-
"vitepress-plugin-mermaid": "^2.0.17",
|
|
111
106
|
"vitest": "^4.1.10",
|
|
112
107
|
"vue-tsc": "^2.0.21"
|
|
113
108
|
},
|
|
114
109
|
"peerDependencies": {
|
|
115
|
-
"vue-router": "^4.6.3 || ^5.0.0"
|
|
110
|
+
"vue-router": "^4.6.3 || ^5.0.0",
|
|
111
|
+
"@vue/devtools-api": "^8.1.5"
|
|
112
|
+
},
|
|
113
|
+
"peerDependenciesMeta": {
|
|
114
|
+
"@vue/devtools-api": {
|
|
115
|
+
"optional": true
|
|
116
|
+
}
|
|
116
117
|
},
|
|
117
118
|
"optionalDependencies": {
|
|
118
119
|
"@rollup/rollup-linux-x64-gnu": "^4.28.1",
|
|
@@ -161,11 +162,11 @@
|
|
|
161
162
|
"test:dist:packages": "pnpm --filter \"@i18n-micro/core\" --filter \"@i18n-micro/utils\" --filter \"@i18n-micro/hmr\" --filter \"@i18n-micro/vue\" --filter \"@i18n-micro/path-strategy\" --filter \"@i18n-micro/route-strategy\" --filter \"@i18n-micro/devtools-ui\" --filter \"@i18n-micro/astro\" run test:dist",
|
|
162
163
|
"typecheck": "tsc --noEmit",
|
|
163
164
|
"typecheck:nuxt": "nuxt typecheck --no-emit",
|
|
164
|
-
"docs:dev": "pnpm
|
|
165
|
-
"docs:build": "pnpm
|
|
166
|
-
"docs:serve": "
|
|
167
|
-
"docs:clean-dev": "pnpm
|
|
168
|
-
"docs:preview": "
|
|
165
|
+
"docs:dev": "pnpm --filter @i18n-micro/docs dev",
|
|
166
|
+
"docs:build": "pnpm --filter @i18n-micro/docs build",
|
|
167
|
+
"docs:serve": "pnpm --filter @i18n-micro/docs preview",
|
|
168
|
+
"docs:clean-dev": "pnpm --filter @i18n-micro/docs clean-dev",
|
|
169
|
+
"docs:preview": "pnpm --filter @i18n-micro/docs preview",
|
|
169
170
|
"smoke:pack": "pnpm -C scripts cli smoke-pack",
|
|
170
171
|
"smoke:verify": "pnpm -C scripts cli smoke-verify",
|
|
171
172
|
"smoke:browser": "pnpm -C scripts cli smoke-browser",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"id":"24f3c578-f02b-41bc-953a-c7ff7b504d56","timestamp":1785527483273,"prerendered":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.grid[data-v-2a91edc4]{display:grid}.mb-2[data-v-2a91edc4]{margin-bottom:.5rem}.mb-4[data-v-2a91edc4]{margin-bottom:1rem}.max-w-520px[data-v-2a91edc4]{max-width:520px}.min-h-screen[data-v-2a91edc4]{min-height:100vh}.w-full[data-v-2a91edc4]{width:100%}.flex[data-v-2a91edc4]{display:flex}.place-content-center[data-v-2a91edc4]{place-content:center}.items-center[data-v-2a91edc4]{align-items:center}.justify-center[data-v-2a91edc4]{justify-content:center}.overflow-hidden[data-v-2a91edc4]{overflow:hidden}.bg-white[data-v-2a91edc4]{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.px-2[data-v-2a91edc4]{padding-left:.5rem;padding-right:.5rem}.text-center[data-v-2a91edc4]{text-align:center}.text-\[80px\][data-v-2a91edc4]{font-size:80px}.text-2xl[data-v-2a91edc4]{font-size:1.5rem;line-height:2rem}.text-sm[data-v-2a91edc4]{font-size:.875rem;line-height:1.25rem}.text-\[\#020420\][data-v-2a91edc4]{--un-text-opacity:1;color:rgb(2 4 32/var(--un-text-opacity))}.text-\[\#64748B\][data-v-2a91edc4]{--un-text-opacity:1;color:rgb(100 116 139/var(--un-text-opacity))}.hover\:text-\[\#00DC82\][data-v-2a91edc4]:hover{--un-text-opacity:1;color:rgb(0 220 130/var(--un-text-opacity))}.font-medium[data-v-2a91edc4]{font-weight:500}.font-semibold[data-v-2a91edc4]{font-weight:600}.leading-none[data-v-2a91edc4]{line-height:1}.tracking-wide[data-v-2a91edc4]{letter-spacing:.025em}.font-sans[data-v-2a91edc4]{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.tabular-nums[data-v-2a91edc4]{--un-numeric-spacing:tabular-nums;font-variant-numeric:var(--un-ordinal) var(--un-slashed-zero) var(--un-numeric-figure) var(--un-numeric-spacing) var(--un-numeric-fraction)}.underline[data-v-2a91edc4]{text-decoration-line:underline}.underline-offset-3[data-v-2a91edc4]{text-underline-offset:3px}.antialiased[data-v-2a91edc4]{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media (prefers-color-scheme:dark){.dark\:bg-\[\#020420\][data-v-2a91edc4]{--un-bg-opacity:1;background-color:rgb(2 4 32/var(--un-bg-opacity))}.dark\:text-white[data-v-2a91edc4]{--un-text-opacity:1;color:rgb(255 255 255/var(--un-text-opacity))}}@media (width>=640px){.sm\:text-\[110px\][data-v-2a91edc4]{font-size:110px}.sm\:text-3xl[data-v-2a91edc4]{font-size:1.875rem;line-height:2.25rem}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.grid[data-v-f975ae25]{display:grid}.mb-2[data-v-f975ae25]{margin-bottom:.5rem}.mb-4[data-v-f975ae25]{margin-bottom:1rem}.max-w-520px[data-v-f975ae25]{max-width:520px}.min-h-screen[data-v-f975ae25]{min-height:100vh}.place-content-center[data-v-f975ae25]{place-content:center}.overflow-hidden[data-v-f975ae25]{overflow:hidden}.bg-white[data-v-f975ae25]{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.px-2[data-v-f975ae25]{padding-left:.5rem;padding-right:.5rem}.text-center[data-v-f975ae25]{text-align:center}.text-\[80px\][data-v-f975ae25]{font-size:80px}.text-2xl[data-v-f975ae25]{font-size:1.5rem;line-height:2rem}.text-\[\#020420\][data-v-f975ae25]{--un-text-opacity:1;color:rgb(2 4 32/var(--un-text-opacity))}.text-\[\#64748B\][data-v-f975ae25]{--un-text-opacity:1;color:rgb(100 116 139/var(--un-text-opacity))}.font-semibold[data-v-f975ae25]{font-weight:600}.leading-none[data-v-f975ae25]{line-height:1}.tracking-wide[data-v-f975ae25]{letter-spacing:.025em}.font-sans[data-v-f975ae25]{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.tabular-nums[data-v-f975ae25]{--un-numeric-spacing:tabular-nums;font-variant-numeric:var(--un-ordinal) var(--un-slashed-zero) var(--un-numeric-figure) var(--un-numeric-spacing) var(--un-numeric-fraction)}.antialiased[data-v-f975ae25]{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media (prefers-color-scheme:dark){.dark\:bg-\[\#020420\][data-v-f975ae25]{--un-bg-opacity:1;background-color:rgb(2 4 32/var(--un-bg-opacity))}.dark\:text-white[data-v-f975ae25]{--un-text-opacity:1;color:rgb(255 255 255/var(--un-text-opacity))}}@media (width>=640px){.sm\:text-\[110px\][data-v-f975ae25]{font-size:110px}.sm\:text-3xl[data-v-f975ae25]{font-size:1.875rem;line-height:2.25rem}}
|