@scopeact/autoi18n 1.1.2 → 1.2.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 (46) hide show
  1. package/README.md +117 -256
  2. package/dist/chunk-36HQJSGI.cjs +40 -0
  3. package/dist/chunk-36HQJSGI.cjs.map +1 -0
  4. package/dist/chunk-NYRK5OWZ.js +40 -0
  5. package/dist/chunk-NYRK5OWZ.js.map +1 -0
  6. package/dist/chunk-RLNUAAKC.js +296 -0
  7. package/dist/chunk-RLNUAAKC.js.map +1 -0
  8. package/dist/chunk-TJT6ONON.cjs +41 -0
  9. package/dist/chunk-TJT6ONON.cjs.map +1 -0
  10. package/dist/chunk-TWTG4RTI.cjs +296 -0
  11. package/dist/chunk-TWTG4RTI.cjs.map +1 -0
  12. package/dist/chunk-VBBY4GSW.js +41 -0
  13. package/dist/chunk-VBBY4GSW.js.map +1 -0
  14. package/dist/cli.cjs +18 -0
  15. package/dist/cli.cjs.map +1 -0
  16. package/dist/cli.d.cts +1 -0
  17. package/dist/cli.d.ts +1 -0
  18. package/dist/cli.js +18 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/client.cjs +10 -0
  21. package/dist/client.cjs.map +1 -0
  22. package/dist/client.d.cts +17 -0
  23. package/dist/client.d.ts +17 -0
  24. package/dist/client.js +10 -0
  25. package/dist/client.js.map +1 -0
  26. package/dist/core/index.cjs +9 -0
  27. package/dist/core/index.cjs.map +1 -0
  28. package/dist/core/index.d.cts +4 -0
  29. package/dist/core/index.d.ts +4 -0
  30. package/dist/core/index.js +9 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/index.cjs +21 -0
  33. package/dist/index.cjs.map +1 -0
  34. package/dist/index.d.cts +5 -0
  35. package/dist/index.d.ts +5 -3
  36. package/dist/index.js +20 -18
  37. package/dist/index.js.map +1 -1
  38. package/dist/server.cjs +9 -0
  39. package/dist/server.cjs.map +1 -0
  40. package/dist/server.d.cts +10 -0
  41. package/dist/server.d.ts +10 -0
  42. package/dist/server.js +9 -0
  43. package/dist/server.js.map +1 -0
  44. package/package.json +79 -26
  45. package/schema.json +51 -0
  46. package/dist/index.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,256 +1,117 @@
1
- # @scopeact/autoi18n
2
-
3
- [![npm version](https://img.shields.io/npm/v/@scopeact/autoi18n.svg)](https://www.npmjs.com/package/@scopeact/autoi18n)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
-
6
- > CLI to automatically migrate React / TypeScript projects to i18n using AST and LLMs.
7
-
8
- **auto-i18n** scans TS/TSX files, extracts hardcoded strings, replaces them with `t("key")`, generates translation files, and optionally injects the correct i18n imports — all with explicit, opinionated trade-offs.
9
-
10
- This is a **migration tool**, not a runtime framework.
11
-
12
- ---
13
-
14
- ## Why this exists
15
-
16
- Internationalizing a React codebase usually means:
17
-
18
- * Manually hunting hardcoded strings
19
- * Guessing translation keys
20
- * Rewriting components by hand
21
- * Copy-pasting JSON across languages
22
-
23
- It’s repetitive, boring, and easy to screw up.
24
-
25
- **auto-i18n automates the mechanical work and delegates semantic decisions to an LLM.**
26
-
27
- ---
28
-
29
- ## What this tool does
30
-
31
- * Parses **TypeScript and TSX using AST** (no regex hacks)
32
- * Detects static hardcoded strings
33
- * Rewrites code to use `t("key")`
34
- * Generates translation files
35
- * Uses an **LLM to generate semantic translation keys**
36
- * Optionally injects i18n imports automatically
37
- * Supports different i18n libraries
38
-
39
- ---
40
-
41
- ## Installation
42
-
43
- No global install required:
44
-
45
- ```bash
46
- npx @scopeact/autoi18n init
47
- ```
48
-
49
- ---
50
-
51
- ## Usage
52
-
53
- ### 1. Initialize configuration
54
-
55
- ```bash
56
- npx @scopeact/autoi18n init
57
- ```
58
-
59
- This creates:
60
-
61
- * `auto-i18n.config.json`
62
-
63
- ---
64
-
65
- ### 2. Run the migration
66
-
67
- ```bash
68
- npx @scopeact/autoi18n run
69
- ```
70
-
71
- The tool will:
72
-
73
- 1. Parse files into AST
74
- 2. Detect static string literals
75
- 3. Ask the LLM to infer **semantic translation keys**
76
- 4. Rewrite source code
77
- 5. Inject i18n imports if missing
78
- 6. Generate translation files
79
-
80
- ---
81
-
82
- ## Example
83
-
84
- ### Before
85
-
86
- ```tsx
87
- export function Home() {
88
- return (
89
- <div>
90
- <h1>Hello world</h1>
91
- <p>Welcome to our platform</p>
92
- </div>
93
- );
94
- }
95
- ```
96
-
97
- ### After
98
-
99
- ```tsx
100
- import { useTranslation } from "react-i18next";
101
-
102
- export function Home() {
103
- return (
104
- <div>
105
- <h1>{t("greeting")}</h1>
106
- <p>{t("description")}</p>
107
- </div>
108
- );
109
- }
110
- ```
111
-
112
- Generated translation file:
113
-
114
- ```json
115
- {
116
- "greeting": "Hello world",
117
- "description": "Welcome to our platform"
118
- }
119
- ```
120
-
121
- ---
122
-
123
- ## Configuration
124
-
125
- Example `auto-i18n.config.json`:
126
-
127
- ```json
128
- {
129
- "sourceLang": "pt",
130
- "targetLangs": [ "en", "es", "de" ],
131
- "autoInject": true,
132
- "i18nLibrary": "react-i18next",
133
- "provider": "ollama",
134
- "localesDir": "./locales",
135
- "model": "gemma3-translator",
136
- "files": [ "**/*.tsx" ]
137
- }
138
- ```
139
-
140
- ### Options
141
-
142
- #### `autoInject`
143
-
144
- ```json
145
- {
146
- "autoInject": true
147
- }
148
- ```
149
-
150
- Automatically injects the required i18n import at the top of the file **if it does not already exist**.
151
-
152
- This avoids manual setup and keeps the migration fully automated.
153
-
154
- ---
155
-
156
- #### `i18nLibrary`
157
-
158
- ```json
159
- {
160
- "i18nLibrary": "react-i18next"
161
- }
162
- ```
163
-
164
- Defines which i18n library should be used when injecting imports and hooks.
165
-
166
- Supported values:
167
-
168
- * `react-i18next`
169
- * `next-i18n`
170
-
171
- This affects:
172
-
173
- * import statements
174
- * generated code structure
175
-
176
- ---
177
-
178
- ## Why keys are AI-generated (no dry-run)
179
-
180
- Translation key naming is a **semantic problem**, not a mechanical one.
181
-
182
- For example:
183
-
184
- * Is `"Hello world"` a title, a CTA, or a heading?
185
- * Does it belong to `home`, `layout`, or `marketing`?
186
-
187
- These decisions cannot be inferred deterministically.
188
-
189
- **auto-i18n intentionally requires an LLM to:**
190
-
191
- * infer intent
192
- * generate meaningful keys
193
- * avoid arbitrary conventions
194
-
195
- Because of this, a traditional dry-run would produce **misleading results** and is intentionally not supported.
196
-
197
- This trade-off is explicit.
198
-
199
- ---
200
-
201
- ## Why AST instead of regex
202
-
203
- Regex does not understand JSX or TypeScript.
204
-
205
- AST parsing:
206
-
207
- * Preserves syntax structure
208
- * Avoids accidental replacements
209
- * Handles real-world React code
210
- * Produces predictable transformations
211
-
212
- This tool is designed for **production codebases**, not demos.
213
-
214
- ---
215
-
216
- ## Limitations
217
-
218
- This tool does **not** handle:
219
-
220
- * Dynamic strings (`"Hello " + name`)
221
- * Template literals with expressions
222
- * Runtime-generated text
223
- * Context-dependent translations
224
-
225
- It is meant to **bootstrap i18n**, not replace human review.
226
-
227
- ---
228
-
229
- ## When you should NOT use this
230
-
231
- * Your project already has a mature i18n setup
232
- * Translations depend heavily on runtime logic
233
- * You expect zero review after migration
234
-
235
- ---
236
-
237
- ## Design philosophy
238
-
239
- * Explicit over clever
240
- * Predictable over magical
241
- * Narrow scope over feature bloat
242
-
243
- This tool solves **one specific problem**, deliberately.
244
-
245
- ---
246
-
247
- ## License
248
-
249
- Distributed under MIT license. See [LICENSE](LICENSE) for more details.
250
-
251
- ---
252
-
253
- ## 🇧🇷 Nota
254
-
255
- README principal em inglês por usabilidade global.
256
- Português aqui só pra lembrar que esse projeto nasceu no Brasil.
1
+
2
+ # @scopeact/autoi18n
3
+
4
+ [![npm version](https://img.shields.io/npm/v/@scopeact/autoi18n.svg?style=flat-square)](https://www.npmjs.com/package/@scopeact/autoi18n)
5
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/felipevetter/auto-i18n/publish.yml?style=flat-square)](https://github.com/felipevetter/auto-i18n/actions)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-emerald.svg?style=flat-square)](https://opensource.org/licenses/MIT)
7
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-blue.svg?style=flat-square)](https://github.com/felipevetter/auto-i18n/pulls)
8
+
9
+ > The only i18n tool that handles both **Extraction** (via AST + AI) and **Runtime** (Zero-Config).
10
+
11
+ **autoi18n** is a dual-purpose tool designed to take a hardcoded React/Next.js project to full internationalization in minutes, not hours.
12
+
13
+ ---
14
+
15
+ ## ⚡ The Core: CLI Migration
16
+
17
+ The CLI scans your `TSX` files, extracts hardcoded strings using **Abstract Syntax Tree (AST)**, and uses **AI to generate semantic keys**.
18
+
19
+ - **No Regex:** Safe code transformations that won't break your syntax.
20
+ - **AI-Powered:** Keys like `welcome_title` instead of `text_1`.
21
+ - **Auto-Inject:** Automatically adds imports to your files.
22
+
23
+ ### Quick Start
24
+ ```bash
25
+ npx @scopeact/autoi18n init
26
+ npx @scopeact/autoi18n run
27
+ ```
28
+
29
+ ---
30
+
31
+ ## 🚀 The Feature: Zero-Config Runtime
32
+
33
+ We noticed that even after extracting strings, configuring i18n in **Next.js (App Router)** is a nightmare (Middleware, `[locale]` folders, etc).
34
+
35
+ So we built a **minimalist, high-performance runtime** specifically for the CLI output.
36
+
37
+ ### 1. Setup the Provider (Root Layout)
38
+ No need to move files into `[locale]` folders. Just wrap your layout.
39
+
40
+ ```tsx
41
+ // app/layout.tsx
42
+ import { I18nProvider } from "@scopeact/autoi18n/client";
43
+ import { getI18nConfig } from "@scopeact/autoi18n/server";
44
+
45
+ export default async function RootLayout({ children }) {
46
+ const i18n = await getI18nConfig('en'); // Default language
47
+
48
+ return (
49
+ <html lang={i18n.locale}>
50
+ <body>
51
+ <I18nProvider locale={i18n.locale} messages={i18n.messages}>
52
+ {children}
53
+ </I18nProvider>
54
+ </body>
55
+ </html>
56
+ );
57
+ }
58
+ ```
59
+
60
+ ### 2. Usage in Server Components
61
+ ```tsx
62
+ // app/page.tsx (Server)
63
+ import { getI18n } from '@scopeact/autoi18n/server';
64
+
65
+ export default async function Page() {
66
+ const { t } = await getI18n();
67
+ return <h1>{t("hero_title")}</h1>;
68
+ }
69
+ ```
70
+
71
+ ### 3. Usage in Client Components
72
+ ```tsx
73
+ // components/Button.tsx (Client)
74
+ 'use client';
75
+ import { useI18n } from '@scopeact/autoi18n/client';
76
+
77
+ export function HeroButton() {
78
+ const { t } = useI18n();
79
+ return <button>{t("get_started")}</button>;
80
+ }
81
+ ```
82
+
83
+ ---
84
+
85
+ ## 🛠 Configuration
86
+
87
+ Created via `init`, the `auto-i18n.config.json` controls the magic:
88
+
89
+ ```json
90
+ {
91
+ "sourceLang": "pt",
92
+ "targetLangs": ["en", "es"],
93
+ "i18nLibrary": "@scopeact/autoi18n",
94
+ "provider": "openai",
95
+ "localesDir": "./locales",
96
+ "files": ["src/**/*.tsx"]
97
+ }
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 💎 Why autoi18n?
103
+
104
+ | Feature | Tradicional (next-intl/i18next) | **autoi18n** |
105
+ | :--- | :--- | :--- |
106
+ | **Key Creation** | Manual (Hours of copy-paste) | **AI-Automated** (Seconds) |
107
+ | **Code Rewrite** | Manual | **AST-Automated** |
108
+ | **Folder Structure** | Forced `[locale]` nesting | **Stay as you are** |
109
+ | **Next.js Setup** | Complex (Middleware/Config) | **Zero-Config** |
110
+
111
+ ---
112
+
113
+ ## 🇧🇷 Born in Brazil
114
+ Projeto desenvolvido com foco em resolver a dor real de desenvolvedores que precisam entregar projetos globais rápido.
115
+
116
+ ## License
117
+ MIT © [Felipe Vetter](https://github.com/felipevetter)
@@ -0,0 +1,40 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }// src/runtime/client.tsx
2
+ var _react = require('react'); var _react2 = _interopRequireDefault(_react);
3
+ var _navigation = require('next/navigation');
4
+ var _jsxruntime = require('react/jsx-runtime');
5
+ var I18nContext = _react.createContext.call(void 0, void 0);
6
+ function I18nProvider({
7
+ children,
8
+ locale: initialLocale,
9
+ messages: initialMessages
10
+ }) {
11
+ const [locale, setLocaleState] = _react.useState.call(void 0, initialLocale);
12
+ const [messages, setMessages] = _react.useState.call(void 0, initialMessages);
13
+ const router = _navigation.useRouter.call(void 0, );
14
+ _react2.default.useEffect(() => {
15
+ setLocaleState(initialLocale);
16
+ setMessages(initialMessages);
17
+ }, [initialLocale, initialMessages]);
18
+ const t = (key) => {
19
+ return messages[key] || key;
20
+ };
21
+ const setLocale = (newLocale) => {
22
+ document.cookie = `NEXT_LOCALE=${newLocale}; path=/; max-age=31536000`;
23
+ setLocaleState(newLocale);
24
+ router.refresh();
25
+ };
26
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, I18nContext.Provider, { value: { locale, messages, t, setLocale }, children });
27
+ }
28
+ function useI18n() {
29
+ const context = _react.useContext.call(void 0, I18nContext);
30
+ if (!context) {
31
+ throw new Error("useI18n deve ser usado dentro de um I18nProvider");
32
+ }
33
+ return context;
34
+ }
35
+
36
+
37
+
38
+
39
+ exports.I18nProvider = I18nProvider; exports.useI18n = useI18n;
40
+ //# sourceMappingURL=chunk-36HQJSGI.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/auto-i18n/auto-i18n/packages/autoi18n/dist/chunk-36HQJSGI.cjs","../src/runtime/client.tsx"],"names":[],"mappings":"AAAA;ACGA,4EAAsE;AAEtE,6CAA0B;AAwClB,+CAAA;AA/BR,IAAM,YAAA,EAAc,kCAAA,KAA2C,CAAS,CAAA;AAEjE,SAAS,YAAA,CAAa;AAAA,EACzB,QAAA;AAAA,EACA,MAAA,EAAQ,aAAA;AAAA,EACR,QAAA,EAAU;AACd,CAAA,EAIG;AACC,EAAA,MAAM,CAAC,MAAA,EAAQ,cAAc,EAAA,EAAI,6BAAA,aAAsB,CAAA;AACvD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,EAAA,EAAI,6BAAA,eAAwB,CAAA;AACxD,EAAA,MAAM,OAAA,EAAS,mCAAA,CAAU;AAEzB,EAAA,eAAA,CAAM,SAAA,CAAU,CAAA,EAAA,GAAM;AAClB,IAAA,cAAA,CAAe,aAAa,CAAA;AAC5B,IAAA,WAAA,CAAY,eAAe,CAAA;AAAA,EAC/B,CAAA,EAAG,CAAC,aAAA,EAAe,eAAe,CAAC,CAAA;AAEnC,EAAA,MAAM,EAAA,EAAI,CAAC,GAAA,EAAA,GAAgB;AACvB,IAAA,OAAO,QAAA,CAAS,GAAG,EAAA,GAAK,GAAA;AAAA,EAC5B,CAAA;AAEA,EAAA,MAAM,UAAA,EAAY,CAAC,SAAA,EAAA,GAAsB;AACrC,IAAA,QAAA,CAAS,OAAA,EAAS,CAAA,YAAA,EAAe,SAAS,CAAA,0BAAA,CAAA;AAC1C,IAAA,cAAA,CAAe,SAAS,CAAA;AACxB,IAAA,MAAA,CAAO,OAAA,CAAQ,CAAA;AAAA,EACnB,CAAA;AAEA,EAAA,uBACI,6BAAA,WAAC,CAAY,QAAA,EAAZ,EAAqB,KAAA,EAAO,EAAE,MAAA,EAAQ,QAAA,EAAU,CAAA,EAAG,UAAU,CAAA,EACzD,SAAA,CACL,CAAA;AAER;AAEO,SAAS,OAAA,CAAA,EAAU;AACtB,EAAA,MAAM,QAAA,EAAU,+BAAA,WAAsB,CAAA;AACtC,EAAA,GAAA,CAAI,CAAC,OAAA,EAAS;AACV,IAAA,MAAM,IAAI,KAAA,CAAM,kDAAkD,CAAA;AAAA,EACtE;AACA,EAAA,OAAO,OAAA;AACX;ADvBA;AACA;AACE;AACA;AACF,+DAAC","file":"/home/runner/work/auto-i18n/auto-i18n/packages/autoi18n/dist/chunk-36HQJSGI.cjs","sourcesContent":[null,"'use client';\n\n// @ts-ignore\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n// @ts-ignore\nimport { useRouter } from 'next/navigation';\n\ntype I18nContextType = {\n locale: string;\n messages: Record<string, string>;\n t: (key: string) => string;\n setLocale: (newLocale: string) => void;\n};\n\nconst I18nContext = createContext<I18nContextType | undefined>(undefined);\n\nexport function I18nProvider({\n children,\n locale: initialLocale,\n messages: initialMessages\n}: {\n children: ReactNode;\n locale: string;\n messages: Record<string, string>;\n}) {\n const [locale, setLocaleState] = useState(initialLocale);\n const [messages, setMessages] = useState(initialMessages);\n const router = useRouter();\n\n React.useEffect(() => {\n setLocaleState(initialLocale);\n setMessages(initialMessages);\n }, [initialLocale, initialMessages]);\n\n const t = (key: string) => {\n return messages[key] || key;\n };\n\n const setLocale = (newLocale: string) => {\n document.cookie = `NEXT_LOCALE=${newLocale}; path=/; max-age=31536000`;\n setLocaleState(newLocale);\n router.refresh();\n };\n\n return (\n <I18nContext.Provider value={{ locale, messages, t, setLocale }}>\n {children}\n </I18nContext.Provider>\n );\n}\n\nexport function useI18n() {\n const context = useContext(I18nContext);\n if (!context) {\n throw new Error('useI18n deve ser usado dentro de um I18nProvider');\n }\n return context;\n}"]}
@@ -0,0 +1,40 @@
1
+ // src/runtime/client.tsx
2
+ import React, { createContext, useContext, useState } from "react";
3
+ import { useRouter } from "next/navigation";
4
+ import { jsx } from "react/jsx-runtime";
5
+ var I18nContext = createContext(void 0);
6
+ function I18nProvider({
7
+ children,
8
+ locale: initialLocale,
9
+ messages: initialMessages
10
+ }) {
11
+ const [locale, setLocaleState] = useState(initialLocale);
12
+ const [messages, setMessages] = useState(initialMessages);
13
+ const router = useRouter();
14
+ React.useEffect(() => {
15
+ setLocaleState(initialLocale);
16
+ setMessages(initialMessages);
17
+ }, [initialLocale, initialMessages]);
18
+ const t = (key) => {
19
+ return messages[key] || key;
20
+ };
21
+ const setLocale = (newLocale) => {
22
+ document.cookie = `NEXT_LOCALE=${newLocale}; path=/; max-age=31536000`;
23
+ setLocaleState(newLocale);
24
+ router.refresh();
25
+ };
26
+ return /* @__PURE__ */ jsx(I18nContext.Provider, { value: { locale, messages, t, setLocale }, children });
27
+ }
28
+ function useI18n() {
29
+ const context = useContext(I18nContext);
30
+ if (!context) {
31
+ throw new Error("useI18n deve ser usado dentro de um I18nProvider");
32
+ }
33
+ return context;
34
+ }
35
+
36
+ export {
37
+ I18nProvider,
38
+ useI18n
39
+ };
40
+ //# sourceMappingURL=chunk-NYRK5OWZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/client.tsx"],"sourcesContent":["'use client';\n\n// @ts-ignore\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n// @ts-ignore\nimport { useRouter } from 'next/navigation';\n\ntype I18nContextType = {\n locale: string;\n messages: Record<string, string>;\n t: (key: string) => string;\n setLocale: (newLocale: string) => void;\n};\n\nconst I18nContext = createContext<I18nContextType | undefined>(undefined);\n\nexport function I18nProvider({\n children,\n locale: initialLocale,\n messages: initialMessages\n}: {\n children: ReactNode;\n locale: string;\n messages: Record<string, string>;\n}) {\n const [locale, setLocaleState] = useState(initialLocale);\n const [messages, setMessages] = useState(initialMessages);\n const router = useRouter();\n\n React.useEffect(() => {\n setLocaleState(initialLocale);\n setMessages(initialMessages);\n }, [initialLocale, initialMessages]);\n\n const t = (key: string) => {\n return messages[key] || key;\n };\n\n const setLocale = (newLocale: string) => {\n document.cookie = `NEXT_LOCALE=${newLocale}; path=/; max-age=31536000`;\n setLocaleState(newLocale);\n router.refresh();\n };\n\n return (\n <I18nContext.Provider value={{ locale, messages, t, setLocale }}>\n {children}\n </I18nContext.Provider>\n );\n}\n\nexport function useI18n() {\n const context = useContext(I18nContext);\n if (!context) {\n throw new Error('useI18n deve ser usado dentro de um I18nProvider');\n }\n return context;\n}"],"mappings":";AAGA,OAAO,SAAS,eAAe,YAAY,gBAA2B;AAEtE,SAAS,iBAAiB;AAwClB;AA/BR,IAAM,cAAc,cAA2C,MAAS;AAEjE,SAAS,aAAa;AAAA,EACzB;AAAA,EACA,QAAQ;AAAA,EACR,UAAU;AACd,GAIG;AACC,QAAM,CAAC,QAAQ,cAAc,IAAI,SAAS,aAAa;AACvD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,eAAe;AACxD,QAAM,SAAS,UAAU;AAEzB,QAAM,UAAU,MAAM;AAClB,mBAAe,aAAa;AAC5B,gBAAY,eAAe;AAAA,EAC/B,GAAG,CAAC,eAAe,eAAe,CAAC;AAEnC,QAAM,IAAI,CAAC,QAAgB;AACvB,WAAO,SAAS,GAAG,KAAK;AAAA,EAC5B;AAEA,QAAM,YAAY,CAAC,cAAsB;AACrC,aAAS,SAAS,eAAe,SAAS;AAC1C,mBAAe,SAAS;AACxB,WAAO,QAAQ;AAAA,EACnB;AAEA,SACI,oBAAC,YAAY,UAAZ,EAAqB,OAAO,EAAE,QAAQ,UAAU,GAAG,UAAU,GACzD,UACL;AAER;AAEO,SAAS,UAAU;AACtB,QAAM,UAAU,WAAW,WAAW;AACtC,MAAI,CAAC,SAAS;AACV,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACtE;AACA,SAAO;AACX;","names":[]}