@openrails/contentkit-ui 0.101.1 → 0.103.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.
Files changed (53) hide show
  1. package/README.md +36 -13
  2. package/dist/client/browser-reader-preferences.d.ts +8 -9
  3. package/dist/client/browser-reader-preferences.js +71 -63
  4. package/dist/client/browser-reader-preferences.js.map +1 -1
  5. package/dist/client/discovery.d.ts +3 -1
  6. package/dist/client/discovery.js +2 -2
  7. package/dist/client/discovery.js.map +1 -1
  8. package/dist/client/generated/routes.d.ts +2 -2
  9. package/dist/client/generated/routes.js +2 -1
  10. package/dist/client/generated/routes.js.map +1 -1
  11. package/dist/client/generated/wire.d.ts +5 -10
  12. package/dist/client/generated/wire.js.map +1 -1
  13. package/dist/client/http.d.ts +3 -4
  14. package/dist/client/http.js +7 -15
  15. package/dist/client/http.js.map +1 -1
  16. package/dist/client/index.d.ts +1 -1
  17. package/dist/client/reader-preferences.d.ts +2 -2
  18. package/dist/client/reader-preferences.js +2 -6
  19. package/dist/client/reader-preferences.js.map +1 -1
  20. package/dist/components/content/daily-releases.js +39 -43
  21. package/dist/components/content/daily-releases.js.map +1 -1
  22. package/dist/components/content/filters.d.ts +2 -6
  23. package/dist/components/content/filters.js +57 -66
  24. package/dist/components/content/filters.js.map +1 -1
  25. package/dist/components/content/index.d.ts +1 -0
  26. package/dist/components/content/reader-settings.d.ts +0 -1
  27. package/dist/components/content/reader-settings.js +1 -2
  28. package/dist/components/content/reader-settings.js.map +1 -1
  29. package/dist/components/content/release-date-picker.d.ts +16 -0
  30. package/dist/components/content/release-date-picker.js +65 -0
  31. package/dist/components/content/release-date-picker.js.map +1 -0
  32. package/dist/index.js +22 -21
  33. package/dist/locales/discovery/de.js +3 -0
  34. package/dist/locales/discovery/de.js.map +1 -1
  35. package/dist/locales/discovery/en.d.ts +3 -0
  36. package/dist/locales/discovery/en.js +3 -0
  37. package/dist/locales/discovery/en.js.map +1 -1
  38. package/dist/locales/discovery/es.js +3 -0
  39. package/dist/locales/discovery/es.js.map +1 -1
  40. package/dist/locales/discovery/ja.js +3 -0
  41. package/dist/locales/discovery/ja.js.map +1 -1
  42. package/dist/locales/discovery/ko.js +3 -0
  43. package/dist/locales/discovery/ko.js.map +1 -1
  44. package/dist/locales/discovery/zh.js +3 -0
  45. package/dist/locales/discovery/zh.js.map +1 -1
  46. package/dist/locales/en.d.ts +3 -0
  47. package/dist/react/discovery.d.ts +3 -5
  48. package/dist/react/discovery.js +42 -71
  49. package/dist/react/discovery.js.map +1 -1
  50. package/dist/react/provider.js +1 -4
  51. package/dist/react/provider.js.map +1 -1
  52. package/dist/styles.css +1 -1
  53. package/package.json +1 -1
@@ -73,7 +73,6 @@ function j(y) {
73
73
  j && /* @__PURE__ */ w(M, {
74
74
  language: y.language,
75
75
  kinds: y.kinds,
76
- labels: y.labels,
77
76
  client: j,
78
77
  onError: y.onError
79
78
  }),
@@ -101,7 +100,7 @@ function M(t) {
101
100
  language: t.language,
102
101
  client: t.client
103
102
  });
104
- return i && !Object.entries(i.kinds).some(([e, n]) => n.length && (!t.kinds || t.kinds.includes(e))) ? null : /* @__PURE__ */ T("section", {
103
+ return i && (!i.filters.length || t.kinds && !t.kinds.some((e) => i.applies_to.includes(e))) ? null : /* @__PURE__ */ T("section", {
105
104
  "aria-labelledby": r,
106
105
  className: "grid gap-3",
107
106
  children: [/* @__PURE__ */ w("h3", {
@@ -1 +1 @@
1
- {"version":3,"file":"reader-settings.js","names":[],"sources":["../../../src/components/content/reader-settings.tsx"],"sourcesContent":["import { Settings01Icon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { useContext, useId, useSyncExternalStore } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useOptionalContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContentFilters } from \"../../react/discovery.js\";\nimport { AppearanceContext, ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from \"#ckui/ui/dialog\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { ContentFilterSettings, type ContentFilterSettingsProps } from \"./filters.js\";\nimport { ErrorLine, useAction } from \"../social/parts.js\";\nimport type { ReaderPreferenceState } from \"../../client/reader-preferences.js\";\n\nconst EMPTY_PREFERENCES: ReaderPreferenceState = Object.freeze({ loading: false, saving: false, pending: false });\nconst noPreferences = () => EMPTY_PREFERENCES;\nconst noSubscription = () => () => {};\n\nexport interface ReaderSettingsProps {\n /** Content kinds offered in this dialog; omitted shows all configured kinds. */\n kinds?: ContentFilterSettingsProps[\"kinds\"];\n labels?: ContentFilterSettingsProps[\"labels\"];\n languages: readonly { value: string; label: string }[];\n /** The current language, controlled by the host. */\n language: string;\n /** The host applies its own route and preference changes. */\n onLanguageChange: (language: string) => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** Language preferences and filters for both guests and signed-in readers. */\nexport function ReaderSettings(p: ReaderSettingsProps) {\n const messages = useMessages();\n const { t } = messages;\n const id = useId();\n const client = useOptionalContentKitClient(p.client);\n const preferences = client?.preferences;\n const state = useSyncExternalStore(preferences?.subscribe ?? noSubscription, preferences?.snapshot ?? noPreferences, noPreferences);\n // The selector describes the current view. A saved default must not make a\n // localized deep link look selected in a language it is not displaying.\n const selectedLanguage = p.language;\n const save = useAction(\"preferences.save\", p.onError);\n const inheritedAppearance = useContext(AppearanceContext);\n const appearance = p.appearance ?? inheritedAppearance;\n return (\n <AppearanceContext.Provider value={appearance}>\n <ContentKitUiRoot className={p.className} appearance={appearance} data-ckui=\"reader-settings\">\n <Dialog>\n <DialogTrigger render={<Button type=\"button\" variant=\"ghost\" size=\"sm\" aria-label={t(\"readerSettings.title\")} data-ckui=\"reader-settings-trigger\" />}>\n <HugeiconsIcon icon={Settings01Icon} strokeWidth={2} className=\"size-4\" aria-hidden />\n <span>{p.language.toUpperCase()}</span>\n </DialogTrigger>\n <DialogContent className=\"max-h-[calc(100dvh-2rem)] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>{t(\"readerSettings.title\")}</DialogTitle>\n <DialogDescription className=\"sr-only\">{t(\"readerSettings.description\")}</DialogDescription>\n </DialogHeader>\n <div className=\"grid gap-3\">\n <Label htmlFor={`${id}-language`}>{t(\"listing.language\")}</Label>\n <Select value={selectedLanguage} disabled={save.pending || state.loading} onValueChange={(value) => {\n if (!value || value === selectedLanguage) return;\n void save.run(async () => { await preferences?.setLanguage(value); p.onLanguageChange(value); });\n }}>\n <SelectTrigger id={`${id}-language`} className=\"w-full\">\n <SelectValue>{p.languages.find((item) => item.value === selectedLanguage)?.label ?? selectedLanguage}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {p.languages.map((item) => <SelectItem key={item.value} value={item.value}>{item.label}</SelectItem>)}\n </SelectContent>\n </Select>\n </div>\n {client && <ReaderFilters language={p.language} kinds={p.kinds} labels={p.labels} client={client} onError={p.onError} />}\n {(state.pending || save.pending) && !state.error && <p className=\"text-sm text-muted-foreground\" role=\"status\">{t(\"readerSettings.savingPreferences\")}</p>}\n <ErrorLine>{state.error || save.error ? t(\"readerSettings.preferencesNotSaved\") : null}</ErrorLine>\n {state.error && preferences && <Button type=\"button\" variant=\"outline\" onClick={() => { void save.run(() => preferences.retry()); }}>{t(\"common.retry\")}</Button>}\n </DialogContent>\n </Dialog>\n </ContentKitUiRoot>\n </AppearanceContext.Provider>\n );\n}\n\nfunction ReaderFilters(p: Pick<ReaderSettingsProps, \"language\" | \"client\" | \"onError\" | \"kinds\" | \"labels\">) {\n const { t } = useMessages();\n const id = useId();\n const { filters } = useContentFilters({ language: p.language, client: p.client });\n if (filters && !Object.entries(filters.kinds).some(([kind, rows]) => rows.length && (!p.kinds || p.kinds.includes(kind)))) return null;\n return (\n <section aria-labelledby={id} className=\"grid gap-3\">\n <h3 id={id} className=\"font-medium\">{t(\"readerSettings.filters\")}</h3>\n <ContentFilterSettings {...p} includeDeleted={false} />\n </section>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"reader-settings.js","names":[],"sources":["../../../src/components/content/reader-settings.tsx"],"sourcesContent":["import { Settings01Icon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { useContext, useId, useSyncExternalStore } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useOptionalContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContentFilters } from \"../../react/discovery.js\";\nimport { AppearanceContext, ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from \"#ckui/ui/dialog\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { ContentFilterSettings, type ContentFilterSettingsProps } from \"./filters.js\";\nimport { ErrorLine, useAction } from \"../social/parts.js\";\nimport type { ReaderPreferenceState } from \"../../client/reader-preferences.js\";\n\nconst EMPTY_PREFERENCES: ReaderPreferenceState = Object.freeze({ loading: false, saving: false, pending: false });\nconst noPreferences = () => EMPTY_PREFERENCES;\nconst noSubscription = () => () => {};\n\nexport interface ReaderSettingsProps {\n /** Content kinds offered in this dialog; omitted shows all configured kinds. */\n kinds?: ContentFilterSettingsProps[\"kinds\"];\n languages: readonly { value: string; label: string }[];\n /** The current language, controlled by the host. */\n language: string;\n /** The host applies its own route and preference changes. */\n onLanguageChange: (language: string) => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** Language preferences and filters for both guests and signed-in readers. */\nexport function ReaderSettings(p: ReaderSettingsProps) {\n const messages = useMessages();\n const { t } = messages;\n const id = useId();\n const client = useOptionalContentKitClient(p.client);\n const preferences = client?.preferences;\n const state = useSyncExternalStore(preferences?.subscribe ?? noSubscription, preferences?.snapshot ?? noPreferences, noPreferences);\n // The selector describes the current view. A saved default must not make a\n // localized deep link look selected in a language it is not displaying.\n const selectedLanguage = p.language;\n const save = useAction(\"preferences.save\", p.onError);\n const inheritedAppearance = useContext(AppearanceContext);\n const appearance = p.appearance ?? inheritedAppearance;\n return (\n <AppearanceContext.Provider value={appearance}>\n <ContentKitUiRoot className={p.className} appearance={appearance} data-ckui=\"reader-settings\">\n <Dialog>\n <DialogTrigger render={<Button type=\"button\" variant=\"ghost\" size=\"sm\" aria-label={t(\"readerSettings.title\")} data-ckui=\"reader-settings-trigger\" />}>\n <HugeiconsIcon icon={Settings01Icon} strokeWidth={2} className=\"size-4\" aria-hidden />\n <span>{p.language.toUpperCase()}</span>\n </DialogTrigger>\n <DialogContent className=\"max-h-[calc(100dvh-2rem)] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>{t(\"readerSettings.title\")}</DialogTitle>\n <DialogDescription className=\"sr-only\">{t(\"readerSettings.description\")}</DialogDescription>\n </DialogHeader>\n <div className=\"grid gap-3\">\n <Label htmlFor={`${id}-language`}>{t(\"listing.language\")}</Label>\n <Select value={selectedLanguage} disabled={save.pending || state.loading} onValueChange={(value) => {\n if (!value || value === selectedLanguage) return;\n void save.run(async () => { await preferences?.setLanguage(value); p.onLanguageChange(value); });\n }}>\n <SelectTrigger id={`${id}-language`} className=\"w-full\">\n <SelectValue>{p.languages.find((item) => item.value === selectedLanguage)?.label ?? selectedLanguage}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {p.languages.map((item) => <SelectItem key={item.value} value={item.value}>{item.label}</SelectItem>)}\n </SelectContent>\n </Select>\n </div>\n {client && <ReaderFilters language={p.language} kinds={p.kinds} client={client} onError={p.onError} />}\n {(state.pending || save.pending) && !state.error && <p className=\"text-sm text-muted-foreground\" role=\"status\">{t(\"readerSettings.savingPreferences\")}</p>}\n <ErrorLine>{state.error || save.error ? t(\"readerSettings.preferencesNotSaved\") : null}</ErrorLine>\n {state.error && preferences && <Button type=\"button\" variant=\"outline\" onClick={() => { void save.run(() => preferences.retry()); }}>{t(\"common.retry\")}</Button>}\n </DialogContent>\n </Dialog>\n </ContentKitUiRoot>\n </AppearanceContext.Provider>\n );\n}\n\nfunction ReaderFilters(p: Pick<ReaderSettingsProps, \"language\" | \"client\" | \"onError\" | \"kinds\">) {\n const { t } = useMessages();\n const id = useId();\n const { filters } = useContentFilters({ language: p.language, client: p.client });\n if (filters && (!filters.filters.length || p.kinds && !p.kinds.some(kind=>filters.applies_to.includes(kind)))) return null;\n return (\n <section aria-labelledby={id} className=\"grid gap-3\">\n <h3 id={id} className=\"font-medium\">{t(\"readerSettings.filters\")}</h3>\n <ContentFilterSettings {...p} includeDeleted={false} />\n </section>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { ContentKitUiAppearance } from '../../appearance.js';
2
+ /** Today's YYYY-MM-DD in the browser's local calendar, without a UTC shift. */
3
+ export declare function localReleaseDate(now?: Date): string;
4
+ /** A real calendar date on or before max; empty selects the latest window. */
5
+ export declare function isReleaseDate(value: string, max?: string): boolean;
6
+ export interface ReleaseDatePickerProps {
7
+ /** Inclusive end_date of a DailyReleases query; empty means latest. */
8
+ value?: string;
9
+ onValueChange: (value: string) => void;
10
+ /** YYYY-MM-DD in the listing's calendar. Browser-local today by default. */
11
+ max?: string;
12
+ className?: string;
13
+ appearance?: ContentKitUiAppearance;
14
+ }
15
+ /** Native calendar with an editable year, so old dates need no month-by-month paging. */
16
+ export declare function ReleaseDatePicker({ value, onValueChange, max, className, appearance }: ReleaseDatePickerProps): import('react').JSX.Element;
@@ -0,0 +1,65 @@
1
+ import { useMessages as e } from "../../i18n/context.js";
2
+ import { ContentKitUiRoot as t } from "../../scope.js";
3
+ import { Button as n } from "../../ui/button.js";
4
+ import { Input as r } from "../../ui/input.js";
5
+ import { useId as i } from "react";
6
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
7
+ import { cn as s } from "cn";
8
+ //#region src/components/content/release-date-picker.tsx
9
+ function c(e = /* @__PURE__ */ new Date()) {
10
+ return `${String(e.getFullYear()).padStart(4, "0")}-${String(e.getMonth() + 1).padStart(2, "0")}-${String(e.getDate()).padStart(2, "0")}`;
11
+ }
12
+ function l(e, t = c()) {
13
+ if (!e) return !0;
14
+ if (!/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(e) || e.startsWith("0000") || e > t) return !1;
15
+ let n = /* @__PURE__ */ new Date(`${e}T12:00:00Z`);
16
+ return Number.isFinite(n.getTime()) && n.toISOString().slice(0, 10) === e;
17
+ }
18
+ function u({ value: u = "", onValueChange: d, max: f = c(), className: p, appearance: m }) {
19
+ let h = i(), { t: g } = e(), _ = l(u, f);
20
+ return /* @__PURE__ */ o(t, {
21
+ appearance: m,
22
+ className: s("grid justify-items-start gap-2", p),
23
+ "data-ckui": "release-date-picker",
24
+ children: [/* @__PURE__ */ o("div", {
25
+ className: "flex flex-wrap items-center gap-3",
26
+ children: [
27
+ /* @__PURE__ */ a("label", {
28
+ htmlFor: h,
29
+ className: "text-base font-medium",
30
+ children: g("releases.throughDate")
31
+ }),
32
+ /* @__PURE__ */ a(r, {
33
+ id: h,
34
+ type: "date",
35
+ min: "0001-01-01",
36
+ max: f,
37
+ value: _ ? u : "",
38
+ className: "h-11 w-auto min-w-40 text-base",
39
+ "aria-invalid": !_ || void 0,
40
+ "aria-describedby": _ ? void 0 : `${h}-error`,
41
+ onChange: (e) => {
42
+ let t = e.currentTarget.value;
43
+ l(t, f) && d(t);
44
+ }
45
+ }),
46
+ /* @__PURE__ */ a(n, {
47
+ type: "button",
48
+ variant: "outline",
49
+ disabled: !u,
50
+ onClick: () => d(""),
51
+ children: g("releases.latest")
52
+ })
53
+ ]
54
+ }), !_ && /* @__PURE__ */ a("p", {
55
+ id: `${h}-error`,
56
+ role: "alert",
57
+ className: "text-sm text-destructive",
58
+ children: g("releases.invalidDate")
59
+ })]
60
+ });
61
+ }
62
+ //#endregion
63
+ export { u as ReleaseDatePicker, l as isReleaseDate, c as localReleaseDate };
64
+
65
+ //# sourceMappingURL=release-date-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-date-picker.js","names":[],"sources":["../../../src/components/content/release-date-picker.tsx"],"sourcesContent":["import { useId } from \"react\";\nimport { cn } from \"cn\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\n\n/** Today's YYYY-MM-DD in the browser's local calendar, without a UTC shift. */\nexport function localReleaseDate(now = new Date()): string {\n return `${String(now.getFullYear()).padStart(4, \"0\")}-${String(now.getMonth() + 1).padStart(2, \"0\")}-${String(now.getDate()).padStart(2, \"0\")}`;\n}\n\n/** A real calendar date on or before max; empty selects the latest window. */\nexport function isReleaseDate(value: string, max = localReleaseDate()): boolean {\n if (!value) return true;\n if (!/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(value) || value.startsWith(\"0000\") || value > max) return false;\n const at = new Date(`${value}T12:00:00Z`);\n return Number.isFinite(at.getTime()) && at.toISOString().slice(0, 10) === value;\n}\n\nexport interface ReleaseDatePickerProps {\n /** Inclusive end_date of a DailyReleases query; empty means latest. */\n value?: string;\n onValueChange: (value: string) => void;\n /** YYYY-MM-DD in the listing's calendar. Browser-local today by default. */\n max?: string;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** Native calendar with an editable year, so old dates need no month-by-month paging. */\nexport function ReleaseDatePicker({ value = \"\", onValueChange, max = localReleaseDate(), className, appearance }: ReleaseDatePickerProps) {\n const id = useId();\n const { t } = useMessages();\n const valid = isReleaseDate(value, max);\n return <ContentKitUiRoot appearance={appearance} className={cn(\"grid justify-items-start gap-2\", className)} data-ckui=\"release-date-picker\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label htmlFor={id} className=\"text-base font-medium\">{t(\"releases.throughDate\")}</label>\n <Input id={id} type=\"date\" min=\"0001-01-01\" max={max} value={valid ? value : \"\"} className=\"h-11 w-auto min-w-40 text-base\" aria-invalid={!valid || undefined} aria-describedby={!valid ? `${id}-error` : undefined} onChange={(event) => {\n const next = event.currentTarget.value;\n if (isReleaseDate(next, max)) onValueChange(next);\n }} />\n <Button type=\"button\" variant=\"outline\" disabled={!value} onClick={() => onValueChange(\"\")}>{t(\"releases.latest\")}</Button>\n </div>\n {!valid && <p id={`${id}-error`} role=\"alert\" className=\"text-sm text-destructive\">{t(\"releases.invalidDate\")}</p>}\n </ContentKitUiRoot>;\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,EAAiB,oBAAM,IAAI,KAAK,GAAW;CACzD,OAAO,GAAG,OAAO,EAAI,YAAY,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,OAAO,EAAI,SAAS,IAAI,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,OAAO,EAAI,QAAQ,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;AAC9I;AAGA,SAAgB,EAAc,GAAe,IAAM,EAAiB,GAAY;CAC9E,IAAI,CAAC,GAAO,OAAO;CACnB,IAAI,CAAC,+BAA+B,KAAK,CAAK,KAAK,EAAM,WAAW,MAAM,KAAK,IAAQ,GAAK,OAAO;CACnG,IAAM,oBAAK,IAAI,KAAK,GAAG,EAAM,WAAW;CACxC,OAAO,OAAO,SAAS,EAAG,QAAQ,CAAC,KAAK,EAAG,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE,MAAM;AAC5E;AAaA,SAAgB,EAAkB,EAAE,WAAQ,IAAI,kBAAe,SAAM,EAAiB,GAAG,cAAW,iBAAsC;CACxI,IAAM,IAAK,EAAM,GACX,EAAE,SAAM,EAAY,GACpB,IAAQ,EAAc,GAAO,CAAG;CACtC,OAAO,kBAAC,GAAD;EAA8B;EAAY,WAAW,EAAG,kCAAkC,CAAS;EAAG,aAAU;EAAhH,UAAA,CACL,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA;IACE,kBAAC,SAAD;KAAO,SAAS;KAAI,WAAU;KAAyB,UAAA,EAAE,sBAAsB;IAAS,CAAA;IACxF,kBAAC,GAAD;KAAW;KAAI,MAAK;KAAO,KAAI;KAAkB;KAAK,OAAO,IAAQ,IAAQ;KAAI,WAAU;KAAiC,gBAAc,CAAC,KAAS,KAAA;KAAW,oBAAmB,IAAwB,KAAA,IAAhB,GAAG,EAAG;KAAqB,WAAW,MAAU;MACxO,IAAM,IAAO,EAAM,cAAc;MACjC,AAAI,EAAc,GAAM,CAAG,KAAG,EAAc,CAAI;KAClD;IAAI,CAAA;IACJ,kBAAC,GAAD;KAAQ,MAAK;KAAS,SAAQ;KAAU,UAAU,CAAC;KAAO,eAAe,EAAc,EAAE;KAAI,UAAA,EAAE,iBAAiB;IAAU,CAAA;GACvH;EACJ,CAAA,GAAA,CAAC,KAAS,kBAAC,KAAD;GAAG,IAAI,GAAG,EAAG;GAAS,MAAK;GAAQ,WAAU;GAA4B,UAAA,EAAE,sBAAsB;EAAK,CAAA,CACjG;;AACpB"}