@odla-ai/chapter 0.20.2 → 0.21.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/README.md +78 -32
- package/dist/chunk-3JG5X2LT.js +80 -0
- package/dist/chunk-3JG5X2LT.js.map +1 -0
- package/dist/chunk-AOTWFOH4.js +1481 -0
- package/dist/chunk-AOTWFOH4.js.map +1 -0
- package/dist/{chunk-LME2IHY5.js → chunk-ILGUJXSB.js} +1 -1
- package/dist/chunk-ILGUJXSB.js.map +1 -0
- package/dist/index.cjs +61 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -19
- package/dist/index.d.ts +64 -19
- package/dist/index.js +61 -6
- package/dist/index.js.map +1 -1
- package/dist/{types-D2fGiNG8.d.ts → types-CXDiDj_s.d.ts} +36 -8
- package/dist/ui/admin/index.d.ts +85 -84
- package/dist/ui/admin/index.js +8 -2
- package/dist/ui/index.d.ts +3 -2
- package/dist/ui/index.js +9 -3
- package/dist/ui/member/index.d.ts +3 -1
- package/dist/ui/member/index.js +2 -2
- package/dist/worker/index.cjs +28 -16
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +36 -8
- package/dist/worker/index.d.ts +36 -8
- package/dist/worker/index.js +28 -16
- package/dist/worker/index.js.map +1 -1
- package/package.json +3 -3
- package/runbooks/adopt-existing.md +13 -4
- package/runbooks/greenfield.md +19 -9
- package/dist/chunk-LME2IHY5.js.map +0 -1
- package/dist/chunk-MKXHZMAP.js +0 -51
- package/dist/chunk-MKXHZMAP.js.map +0 -1
- package/dist/chunk-UIWFEESI.js +0 -922
- package/dist/chunk-UIWFEESI.js.map +0 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ email. The host still builds the public pages, routing, and brand presentation;
|
|
|
8
8
|
Chapter supplies their application mechanics.
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
|
-
npm i --save-exact @odla-ai/chapter@0.
|
|
11
|
+
npm i --save-exact @odla-ai/chapter@0.21.0
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
> **Agentic experiment.** Built and maintained by AI agents from bounded runbooks
|
|
@@ -101,34 +101,38 @@ secrets out of source and require development proof before production.
|
|
|
101
101
|
presentational component without the Clerk browser SDK. (A bundle-graph test
|
|
102
102
|
enforces this.)
|
|
103
103
|
- `@odla-ai/chapter/ui/admin` — the Clerk-gated console `ChapterAdmin` + the
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
workspace catalog. In chapter mode, `<ChapterAdmin chapter={chapter}/>`
|
|
105
|
+
defaults to just three top-level workspaces: Dashboard, People, and
|
|
106
|
+
Settings. Operational detail belongs in nested page tabs; CRM record
|
|
107
|
+
operations belong in record tabs. Pass `workspaces` to transform or replace
|
|
108
|
+
that information architecture. This entry needs auth-clerk + crm/ui.
|
|
108
109
|
- `@odla-ai/chapter/ui` — the full barrel, for back-compat.
|
|
109
110
|
|
|
110
111
|
Authored against React, rendered as Preact via `preact/compat` in the reference
|
|
111
112
|
sites. Brand tokens (`brandTokens`/`<BrandStyle>`) re-skin all of it from
|
|
112
113
|
`brand` (now light **and** dark, via `brand.palette` + `brand.paletteDark`).
|
|
113
114
|
|
|
114
|
-
### Theme tokens
|
|
115
|
+
### Theme tokens and scoped branding
|
|
115
116
|
|
|
116
117
|
The UI reads its colors from `--ui-*` design tokens whose **values come from a
|
|
117
|
-
theme layer, not `@odla-ai/ui/index.css`.**
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
theme layer, not `@odla-ai/ui/index.css`.** For an admin surface isolated from
|
|
119
|
+
the public site, import one scoped theme, the shared component sheet, and the
|
|
120
|
+
CRM layout:
|
|
120
121
|
|
|
121
122
|
```ts
|
|
122
|
-
import "@odla-ai/ui/
|
|
123
|
+
import "@odla-ai/ui/fonts/plex.css";
|
|
124
|
+
import "@odla-ai/ui/themes/paper/scope.css";
|
|
125
|
+
import "@odla-ai/ui/index.css";
|
|
126
|
+
import "@odla-ai/crm/ui.css";
|
|
123
127
|
```
|
|
124
128
|
|
|
125
129
|
`ChapterAdmin` guards against this at runtime: if a `.panel` has no background
|
|
126
130
|
(the theme layer is missing), it renders a **loud red banner** at the top of the
|
|
127
|
-
console instead of failing silently.
|
|
128
|
-
`brand.
|
|
129
|
-
`
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
console instead of failing silently. Set `brand.theme`, `brand.colorScheme`,
|
|
132
|
+
and semantic `brand.tokens` / `brand.tokensDark` for normal configuration.
|
|
133
|
+
`brand.palette` / `brand.paletteDark` remain the low-level custom-property
|
|
134
|
+
escape hatch. Chapter scopes these overrides to `[data-chapter-admin]`, so an
|
|
135
|
+
admin brand cannot recolor the document root or vendor sign-in UI.
|
|
132
136
|
|
|
133
137
|
## API quick start
|
|
134
138
|
|
|
@@ -144,7 +148,13 @@ export const chapter = defineChapter({
|
|
|
144
148
|
badge: "EX",
|
|
145
149
|
wordmark: "Example Chapter",
|
|
146
150
|
tagline: "Capital and craft for durable local businesses.",
|
|
147
|
-
|
|
151
|
+
theme: "paper",
|
|
152
|
+
colorScheme: "light",
|
|
153
|
+
tokens: {
|
|
154
|
+
accent: "#2f6f4f",
|
|
155
|
+
accentStrong: "#244f3b",
|
|
156
|
+
accentSoft: "#dfece4",
|
|
157
|
+
},
|
|
148
158
|
fonts: { display: "GT Sectra" },
|
|
149
159
|
},
|
|
150
160
|
prices: { standardCents: 100000, foundingDiscountCents: 10000 },
|
|
@@ -160,8 +170,11 @@ export default chapterWorker({ chapter });
|
|
|
160
170
|
```
|
|
161
171
|
|
|
162
172
|
```tsx
|
|
163
|
-
// src/app/admin.tsx — brand + familiar
|
|
164
|
-
import "@odla-ai/ui/
|
|
173
|
+
// src/app/admin.tsx — brand + familiar workspaces come from the same config.
|
|
174
|
+
import "@odla-ai/ui/fonts/plex.css";
|
|
175
|
+
import "@odla-ai/ui/themes/paper/scope.css";
|
|
176
|
+
import "@odla-ai/ui/index.css";
|
|
177
|
+
import "@odla-ai/crm/ui.css";
|
|
165
178
|
import { render } from "preact";
|
|
166
179
|
import { ChapterAdmin } from "@odla-ai/chapter/ui/admin";
|
|
167
180
|
import { chapter } from "../chapter.config.mjs";
|
|
@@ -274,24 +287,24 @@ records, not `crm_link` relation rows; create or curate `works_at` links locally
|
|
|
274
287
|
|
|
275
288
|
#### Custom leader consoles must mount the sharing UI
|
|
276
289
|
|
|
277
|
-
Automatic “Share with …” actions come from
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
290
|
+
Automatic “Share with …” actions come from Chapter's standard People workspace.
|
|
291
|
+
They are present when the console uses `<ChapterAdmin chapter={chapter} />`.
|
|
292
|
+
Passing a `workspaces` array replaces that catalog, so a custom console must
|
|
293
|
+
deliberately compose it:
|
|
281
294
|
|
|
282
295
|
```tsx
|
|
283
296
|
import {
|
|
284
297
|
ChapterAdmin,
|
|
285
|
-
|
|
298
|
+
defaultAdminWorkspaces,
|
|
286
299
|
} from "@odla-ai/chapter/ui/admin";
|
|
287
300
|
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
...
|
|
301
|
+
const workspaces = [
|
|
302
|
+
customOperationsWorkspace,
|
|
303
|
+
...defaultAdminWorkspaces(chapter),
|
|
291
304
|
];
|
|
292
305
|
|
|
293
306
|
render(
|
|
294
|
-
<ChapterAdmin chapter={chapter}
|
|
307
|
+
<ChapterAdmin chapter={chapter} workspaces={workspaces} />,
|
|
295
308
|
document.getElementById("admin-root"),
|
|
296
309
|
);
|
|
297
310
|
```
|
|
@@ -333,10 +346,43 @@ Only after this contract passes against development origins should the leader
|
|
|
333
346
|
target be changed to a production follower origin and the matching production
|
|
334
347
|
vault values be installed.
|
|
335
348
|
|
|
336
|
-
|
|
337
|
-
`/admin/
|
|
338
|
-
|
|
339
|
-
|
|
349
|
+
Admin navigation defaults to link-backed fragments:
|
|
350
|
+
`/admin/#people/person/record-id/profile`. The four segments are workspace,
|
|
351
|
+
nested view, selected record, and record-detail tab. This keeps meaningful,
|
|
352
|
+
reloadable URLs without requiring a server-side SPA fallback, and it avoids
|
|
353
|
+
competing with Clerk's sign-in hash: while signed out, Chapter carries the
|
|
354
|
+
requested state through the redirect query and canonicalizes it after sign-in.
|
|
355
|
+
Legacy `?tab=` and path links still open, while `routing="query"` and
|
|
356
|
+
`routing="path"` remain compatibility modes.
|
|
357
|
+
|
|
358
|
+
### Admin information architecture and migration
|
|
359
|
+
|
|
360
|
+
The default chapter console deliberately has only three top-level workspaces:
|
|
361
|
+
|
|
362
|
+
- **Dashboard** — Overview and Billing page tabs.
|
|
363
|
+
- **People** — configured CRM collections as page tabs, then a master/detail
|
|
364
|
+
record view with Stage, Profile, communications, scheduling, Billing, Notes,
|
|
365
|
+
Connections, Access, and Sharing record tabs when those capabilities apply.
|
|
366
|
+
- **Settings** — Calendar and Email page tabs.
|
|
367
|
+
|
|
368
|
+
Use `chrome="editorial"` (the default), `chrome="none"` when the host supplies
|
|
369
|
+
its own site header, or `renderHeader` for a fully custom header. The deprecated
|
|
370
|
+
`chrome="topbar"` and `sections` APIs remain available for migration, but do
|
|
371
|
+
not add operational detail back to the global site navigation.
|
|
372
|
+
|
|
373
|
+
When upgrading a flat console:
|
|
374
|
+
|
|
375
|
+
1. Import the scoped UI theme, `@odla-ai/ui/index.css`, and
|
|
376
|
+
`@odla-ai/crm/ui.css`.
|
|
377
|
+
2. Remove top-level Billing, Email, Calendar, or collection links that duplicate
|
|
378
|
+
the standard nested tabs.
|
|
379
|
+
3. Replace `sections` with `workspaces`, or omit it to accept the defaults.
|
|
380
|
+
4. Remove host-owned record drawers and compose extra operations with
|
|
381
|
+
`collectionSection` or CRM `extendRecordTabs`.
|
|
382
|
+
5. Change generated links to `adminRouteHref`; keep legacy query/path URLs only
|
|
383
|
+
as inbound compatibility links.
|
|
384
|
+
6. Verify refresh, back/forward navigation, keyboard tab behavior, mobile
|
|
385
|
+
list/detail switching, and brand containment before deleting old routes.
|
|
340
386
|
|
|
341
387
|
## Adopting into an existing site
|
|
342
388
|
|
|
@@ -467,7 +513,7 @@ These bite silently — a smoke test won't catch them:
|
|
|
467
513
|
invitation mail. Importing the full `@odla-ai/chapter/ui` barrel pulls the admin
|
|
468
514
|
half, so prefer the narrower entry.
|
|
469
515
|
- **Known-good application set** (installs clean, no flags):
|
|
470
|
-
`@odla-ai/chapter` 0.
|
|
516
|
+
`@odla-ai/chapter` 0.21.0, `@odla-ai/ui` 0.11.0, `@odla-ai/crm` 0.2.0,
|
|
471
517
|
`@odla-ai/db` 0.6.6,
|
|
472
518
|
`@odla-ai/calendar` 0.2.0, `@odla-ai/email` 0.3.1,
|
|
473
519
|
`@odla-ai/auth-clerk` 0.4.0, `@odla-ai/o11y` 2.2.2, `jose` 6.2.3, React
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/brand.ts
|
|
2
|
+
function paletteVar(key) {
|
|
3
|
+
return key.startsWith("--") ? key : `--${key}`;
|
|
4
|
+
}
|
|
5
|
+
function cleanValue(value) {
|
|
6
|
+
return value.replace(/[<>{};]/g, "").trim();
|
|
7
|
+
}
|
|
8
|
+
function paletteDecls(palette) {
|
|
9
|
+
const decls = [];
|
|
10
|
+
for (const [key, value] of Object.entries(palette ?? {})) {
|
|
11
|
+
if (typeof value === "string" && value.trim()) decls.push(`${paletteVar(key)}: ${cleanValue(value)};`);
|
|
12
|
+
}
|
|
13
|
+
return decls;
|
|
14
|
+
}
|
|
15
|
+
var TOKEN_VARS = {
|
|
16
|
+
background: "--ui-bg",
|
|
17
|
+
surface: "--ui-surface",
|
|
18
|
+
surface2: "--ui-surface-2",
|
|
19
|
+
text: "--ui-text",
|
|
20
|
+
textMuted: "--ui-text-muted",
|
|
21
|
+
textFaint: "--ui-text-faint",
|
|
22
|
+
border: "--ui-border",
|
|
23
|
+
borderStrong: "--ui-border-strong",
|
|
24
|
+
accent: "--ui-accent",
|
|
25
|
+
accentStrong: "--ui-accent-strong",
|
|
26
|
+
accentSoft: "--ui-accent-soft",
|
|
27
|
+
onAccent: "--ui-on-accent",
|
|
28
|
+
good: "--ui-good",
|
|
29
|
+
warn: "--ui-warn",
|
|
30
|
+
danger: "--ui-danger",
|
|
31
|
+
chart1: "--ui-chart-1",
|
|
32
|
+
chart2: "--ui-chart-2",
|
|
33
|
+
chartPositive: "--ui-chart-pos",
|
|
34
|
+
chartNegative: "--ui-chart-neg"
|
|
35
|
+
};
|
|
36
|
+
function semanticDecls(tokens) {
|
|
37
|
+
return Object.entries(tokens ?? {}).flatMap(
|
|
38
|
+
([key, value]) => typeof value === "string" && value.trim() ? [`${TOKEN_VARS[key]}: ${cleanValue(value)};`] : []
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
function brandTokens(brand, options = {}) {
|
|
42
|
+
if (!brand) return "";
|
|
43
|
+
const light = [...paletteDecls(brand.palette), ...semanticDecls(brand.tokens)];
|
|
44
|
+
const fonts = brand.fonts;
|
|
45
|
+
if (fonts?.display) light.push(`--ui-font-display: ${cleanValue(fonts.display)};`);
|
|
46
|
+
if (fonts?.body) light.push(`--ui-font-sans: ${cleanValue(fonts.body)};`);
|
|
47
|
+
if (fonts?.numeral) light.push(`--ui-font-numeral: ${cleanValue(fonts.numeral)};`);
|
|
48
|
+
const dark = [...paletteDecls(brand.paletteDark), ...semanticDecls(brand.tokensDark)];
|
|
49
|
+
const selector = options.selector ?? ":root";
|
|
50
|
+
const darkSelector = selector === ":root" ? ':root[data-theme="dark"]' : `${selector}[data-theme="dark"]`;
|
|
51
|
+
const systemSelector = selector === ":root" ? ':root:not([data-theme="light"])' : `${selector}:not([data-theme="light"])`;
|
|
52
|
+
let css = light.length ? `${selector} {
|
|
53
|
+
${light.join("\n ")}
|
|
54
|
+
}
|
|
55
|
+
` : "";
|
|
56
|
+
if (dark.length) {
|
|
57
|
+
const block = `{
|
|
58
|
+
${dark.join("\n ")}
|
|
59
|
+
}`;
|
|
60
|
+
css += `${darkSelector} ${block}
|
|
61
|
+
@media (prefers-color-scheme: dark) {
|
|
62
|
+
${systemSelector} ${block}
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
return css;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/ui/brand-style.tsx
|
|
70
|
+
import { jsx } from "react/jsx-runtime";
|
|
71
|
+
function BrandStyle(props) {
|
|
72
|
+
const css = brandTokens(props.brand, { selector: props.selector });
|
|
73
|
+
if (!css) return null;
|
|
74
|
+
return /* @__PURE__ */ jsx("style", { children: css });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
BrandStyle
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=chunk-3JG5X2LT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/brand.ts","../src/ui/brand-style.tsx"],"sourcesContent":["// Brand tokens (H4). defineChapter accepts a `brand` block; this turns it into a\n// `:root { --…: … }` CSS block, so a chapter re-skins the WHOLE UI — the\n// @odla-ai/ui components, the admin shell, and the member islands, which all read\n// --ui-* design tokens — from one config instead of hand-writing inline CSS.\n//\n// Pure string generation, so it is unit-testable and can be emitted at\n// build/SSR time into the page <head> (no flash of unstyled content), or via the\n// <BrandStyle> component from @odla-ai/chapter/ui.\nimport type { ChapterBrand, ChapterBrandTokens } from \"./types\";\n\n// A palette entry is either a direct custom property (already `--…`, e.g.\n// `--ui-accent` to retheme components) or a bare name we expose as `--<name>`\n// (e.g. `moss` → `--moss`, for a site to reference in its own CSS).\nfunction paletteVar(key: string): string {\n return key.startsWith(\"--\") ? key : `--${key}`;\n}\n\n// Strip characters that could break out of a `--var: value;` declaration or the\n// surrounding <style>. Brand config is trusted author input, so this is a\n// belt-and-suspenders guard, not a security boundary.\nfunction cleanValue(value: string): string {\n return value.replace(/[<>{};]/g, \"\").trim();\n}\n\nfunction paletteDecls(palette: Record<string, string> | undefined): string[] {\n const decls: string[] = [];\n for (const [key, value] of Object.entries(palette ?? {})) {\n if (typeof value === \"string\" && value.trim()) decls.push(`${paletteVar(key)}: ${cleanValue(value)};`);\n }\n return decls;\n}\n\nconst TOKEN_VARS: Record<keyof ChapterBrandTokens, string> = {\n background: \"--ui-bg\",\n surface: \"--ui-surface\",\n surface2: \"--ui-surface-2\",\n text: \"--ui-text\",\n textMuted: \"--ui-text-muted\",\n textFaint: \"--ui-text-faint\",\n border: \"--ui-border\",\n borderStrong: \"--ui-border-strong\",\n accent: \"--ui-accent\",\n accentStrong: \"--ui-accent-strong\",\n accentSoft: \"--ui-accent-soft\",\n onAccent: \"--ui-on-accent\",\n good: \"--ui-good\",\n warn: \"--ui-warn\",\n danger: \"--ui-danger\",\n chart1: \"--ui-chart-1\",\n chart2: \"--ui-chart-2\",\n chartPositive: \"--ui-chart-pos\",\n chartNegative: \"--ui-chart-neg\",\n};\n\nfunction semanticDecls(tokens: ChapterBrandTokens | undefined): string[] {\n return Object.entries(tokens ?? {}).flatMap(([key, value]) =>\n typeof value === \"string\" && value.trim()\n ? [`${TOKEN_VARS[key as keyof ChapterBrandTokens]}: ${cleanValue(value)};`]\n : [],\n );\n}\n\n/**\n * Build the CSS that maps a chapter's brand onto the design tokens the UI reads:\n * each `palette` entry becomes a custom property (light, and dark unless\n * `paletteDark` overrides), and `fonts` (display/body/numeral) map to\n * `--ui-font-display` / `--ui-font-sans` / `--ui-font-numeral`. The dark block is\n * emitted under both `:root[data-theme=\"dark\"]` (the odla-ui theme toggle) and\n * `@media (prefers-color-scheme: dark)`. Returns \"\" when there is nothing to\n * theme. These are brand OVERRIDES on top of a base theme — they do not replace\n * the theme layer the components need (see {@link brandTokens} usage in the docs).\n */\nexport function brandTokens(\n brand: ChapterBrand | undefined,\n options: { selector?: string } = {},\n): string {\n if (!brand) return \"\";\n const light = [...paletteDecls(brand.palette), ...semanticDecls(brand.tokens)];\n const fonts = brand.fonts;\n if (fonts?.display) light.push(`--ui-font-display: ${cleanValue(fonts.display)};`);\n if (fonts?.body) light.push(`--ui-font-sans: ${cleanValue(fonts.body)};`);\n if (fonts?.numeral) light.push(`--ui-font-numeral: ${cleanValue(fonts.numeral)};`);\n const dark = [...paletteDecls(brand.paletteDark), ...semanticDecls(brand.tokensDark)];\n const selector = options.selector ?? \":root\";\n const darkSelector = selector === \":root\" ? ':root[data-theme=\"dark\"]' : `${selector}[data-theme=\"dark\"]`;\n const systemSelector = selector === \":root\"\n ? ':root:not([data-theme=\"light\"])'\n : `${selector}:not([data-theme=\"light\"])`;\n\n let css = light.length ? `${selector} {\\n ${light.join(\"\\n \")}\\n}\\n` : \"\";\n if (dark.length) {\n const block = `{\\n ${dark.join(\"\\n \")}\\n}`;\n css += `${darkSelector} ${block}\\n@media (prefers-color-scheme: dark) {\\n ${systemSelector} ${block}\\n}\\n`;\n }\n return css;\n}\n","// Client-side convenience for brand tokens: render the chapter's brand as a\n// <style> tag. Prefer emitting brandTokens() into the page <head> at build/SSR\n// time (no flash); use this when that isn't available (e.g. a pure SPA mount).\nimport { brandTokens } from \"../brand.js\";\nimport type { ChapterBrand } from \"../types\";\n\n/** Props for {@link BrandStyle}. */\nexport interface BrandStyleProps {\n brand: ChapterBrand | undefined;\n /** CSS selector receiving the variables. Default `:root`. */\n selector?: string;\n}\n\n/** Render a chapter's brand tokens as an inline <style> block (or nothing when\n * there is no brand to theme). */\nexport function BrandStyle(props: BrandStyleProps) {\n const css = brandTokens(props.brand, { selector: props.selector });\n if (!css) return null;\n return <style>{css}</style>;\n}\n"],"mappings":";AAaA,SAAS,WAAW,KAAqB;AACvC,SAAO,IAAI,WAAW,IAAI,IAAI,MAAM,KAAK,GAAG;AAC9C;AAKA,SAAS,WAAW,OAAuB;AACzC,SAAO,MAAM,QAAQ,YAAY,EAAE,EAAE,KAAK;AAC5C;AAEA,SAAS,aAAa,SAAuD;AAC3E,QAAM,QAAkB,CAAC;AACzB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,WAAW,CAAC,CAAC,GAAG;AACxD,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAG,OAAM,KAAK,GAAG,WAAW,GAAG,CAAC,KAAK,WAAW,KAAK,CAAC,GAAG;AAAA,EACvG;AACA,SAAO;AACT;AAEA,IAAM,aAAuD;AAAA,EAC3D,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,UAAU;AAAA,EACV,MAAM;AAAA,EACN,WAAW;AAAA,EACX,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,eAAe;AACjB;AAEA,SAAS,cAAc,QAAkD;AACvE,SAAO,OAAO,QAAQ,UAAU,CAAC,CAAC,EAAE;AAAA,IAAQ,CAAC,CAAC,KAAK,KAAK,MACtD,OAAO,UAAU,YAAY,MAAM,KAAK,IACpC,CAAC,GAAG,WAAW,GAA+B,CAAC,KAAK,WAAW,KAAK,CAAC,GAAG,IACxE,CAAC;AAAA,EACP;AACF;AAYO,SAAS,YACd,OACA,UAAiC,CAAC,GAC1B;AACR,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,QAAQ,CAAC,GAAG,aAAa,MAAM,OAAO,GAAG,GAAG,cAAc,MAAM,MAAM,CAAC;AAC7E,QAAM,QAAQ,MAAM;AACpB,MAAI,OAAO,QAAS,OAAM,KAAK,sBAAsB,WAAW,MAAM,OAAO,CAAC,GAAG;AACjF,MAAI,OAAO,KAAM,OAAM,KAAK,mBAAmB,WAAW,MAAM,IAAI,CAAC,GAAG;AACxE,MAAI,OAAO,QAAS,OAAM,KAAK,sBAAsB,WAAW,MAAM,OAAO,CAAC,GAAG;AACjF,QAAM,OAAO,CAAC,GAAG,aAAa,MAAM,WAAW,GAAG,GAAG,cAAc,MAAM,UAAU,CAAC;AACpF,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,eAAe,aAAa,UAAU,6BAA6B,GAAG,QAAQ;AACpF,QAAM,iBAAiB,aAAa,UAChC,oCACA,GAAG,QAAQ;AAEf,MAAI,MAAM,MAAM,SAAS,GAAG,QAAQ;AAAA,IAAS,MAAM,KAAK,MAAM,CAAC;AAAA;AAAA,IAAU;AACzE,MAAI,KAAK,QAAQ;AACf,UAAM,QAAQ;AAAA,IAAQ,KAAK,KAAK,MAAM,CAAC;AAAA;AACvC,WAAO,GAAG,YAAY,IAAI,KAAK;AAAA;AAAA,IAA8C,cAAc,IAAI,KAAK;AAAA;AAAA;AAAA,EACtG;AACA,SAAO;AACT;;;AC7ES;AAHF,SAAS,WAAW,OAAwB;AACjD,QAAM,MAAM,YAAY,MAAM,OAAO,EAAE,UAAU,MAAM,SAAS,CAAC;AACjE,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,oBAAC,WAAO,eAAI;AACrB;","names":[]}
|