@tenphi/starlight 0.4.0 → 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.
@@ -0,0 +1,39 @@
1
+ ---
2
+ import { LogoRoot } from "./LayoutComponents.js";
3
+
4
+ interface Props {
5
+ label?: string;
6
+ decorative?: boolean;
7
+ class?: string;
8
+ }
9
+
10
+ const {
11
+ label = "Cookbook",
12
+ decorative = false,
13
+ class: className,
14
+ } = Astro.props;
15
+ ---
16
+
17
+ <LogoRoot
18
+ className={className}
19
+ aria-hidden={decorative ? "true" : undefined}
20
+ data-tasty-anatomy="Logo"
21
+ >
22
+ <svg
23
+ xmlns="http://www.w3.org/2000/svg"
24
+ viewBox="0 0 64 64"
25
+ role={decorative ? undefined : "img"}
26
+ aria-label={decorative ? undefined : label}
27
+ focusable="false"
28
+ >
29
+ <rect width="64" height="64" rx="14" fill="currentColor"></rect>
30
+ <path
31
+ fill="#fff"
32
+ d="M14.8 16c6.7.2 12.3 2 16.7 5.4v28.4c-4.4-3.1-10-4.7-16.6-4.9a3 3 0 0 1-2.9-3V19a3 3 0 0 1 2.8-3Z"
33
+ ></path>
34
+ <path
35
+ fill="#fff"
36
+ d="M49.2 16c-6.7.2-12.3 2-16.7 5.4v28.4c4.4-3.1 10-4.7 16.6-4.9a3 3 0 0 0 2.9-3V19a3 3 0 0 0-2.8-3Z"
37
+ ></path>
38
+ </svg>
39
+ </LogoRoot>
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import { layout } from "virtual:cookbook/layout";
3
3
  import { activeNavigationTab, navigationPath } from "../navigation.js";
4
+ import { MobileNavigationTabsRoot } from "./LayoutComponents.js";
4
5
 
5
6
  interface Props {
6
7
  base?: string;
@@ -21,7 +22,11 @@ function tabHref(link: string): string {
21
22
 
22
23
  {
23
24
  layout.tabs.length > 0 && (
24
- <nav class="td-mobile-tabs print:hidden" aria-label="Sections">
25
+ <MobileNavigationTabsRoot
26
+ className="td-mobile-tabs print:hidden"
27
+ aria-label="Sections"
28
+ data-tasty-anatomy="MobileNavigationTabs"
29
+ >
25
30
  <span class="td-mobile-tabs__label">Sections</span>
26
31
  <ul>
27
32
  {layout.tabs.map((tab, index) => (
@@ -35,6 +40,6 @@ function tabHref(link: string): string {
35
40
  </li>
36
41
  ))}
37
42
  </ul>
38
- </nav>
43
+ </MobileNavigationTabsRoot>
39
44
  )
40
45
  }
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import type { DocsRoute, NavigationItem } from "@tenphi/docs";
3
3
  import { normalizeNavigationPath } from "../navigation.js";
4
+ import { NavigationTreeRoot } from "./LayoutComponents.js";
4
5
 
5
6
  interface Props {
6
7
  items: NavigationItem[];
@@ -36,7 +37,10 @@ const generatedItems = (directory: string): NavigationItem[] => {
36
37
  };
37
38
  ---
38
39
 
39
- <ul class:list={{ "td-shell__nav-root": !nested }}>
40
+ <NavigationTreeRoot
41
+ className={nested ? undefined : "td-shell__nav-root"}
42
+ data-tasty-anatomy="NavigationTree"
43
+ >
40
44
  {
41
45
  items.map((item) => {
42
46
  if (typeof item === "string") {
@@ -95,4 +99,4 @@ const generatedItems = (directory: string): NavigationItem[] => {
95
99
  );
96
100
  })
97
101
  }
98
- </ul>
102
+ </NavigationTreeRoot>
@@ -0,0 +1,22 @@
1
+ ---
2
+ import { PackageVersionRoot } from "./LayoutComponents.js";
3
+
4
+ interface Props {
5
+ version?: string;
6
+ }
7
+
8
+ const version = Astro.props.version?.trim();
9
+ const label = version?.startsWith("v") ? version : version ? `v${version}` : "";
10
+ ---
11
+
12
+ {
13
+ label && (
14
+ <PackageVersionRoot
15
+ className="td-package-version"
16
+ aria-label={`Version ${version}`}
17
+ data-tasty-anatomy="PackageVersion"
18
+ >
19
+ {label}
20
+ </PackageVersionRoot>
21
+ )
22
+ }
@@ -1,4 +1,6 @@
1
1
  ---
2
+ import { PreviewRoot } from "./TastyComponents.js";
3
+
2
4
  interface Props {
3
5
  title: string;
4
6
  html?: string;
@@ -10,22 +12,31 @@ const { title, html = "", css = "", javascript } = Astro.props;
10
12
  const source = `<!doctype html><meta charset="utf-8"><style>${css}</style>${html}${javascript ? `<script>${javascript}<\/script>` : ""}`;
11
13
  ---
12
14
 
13
- <figure class="td-preview" aria-label={title} data-tasty-anatomy="Preview">
14
- <figcaption><strong>{title}</strong></figcaption>
15
+ <PreviewRoot
16
+ className="td-preview"
17
+ aria-label={title}
18
+ data-tasty-anatomy="Preview"
19
+ >
20
+ <PreviewRoot.Caption><strong>{title}</strong></PreviewRoot.Caption>
15
21
  {
16
22
  javascript ? (
17
- <iframe title={title} sandbox="allow-scripts" srcdoc={source} />
23
+ <PreviewRoot.Frame
24
+ className="td-preview__frame"
25
+ title={title}
26
+ sandbox="allow-scripts"
27
+ srcDoc={source}
28
+ />
18
29
  ) : (
19
- <div class="td-preview__stage">
30
+ <PreviewRoot.Stage className="td-preview__stage">
20
31
  <template
21
32
  shadowrootmode="open"
22
33
  set:html={`<style>${css}</style>${html}`}
23
34
  />
24
- </div>
35
+ </PreviewRoot.Stage>
25
36
  )
26
37
  }
27
- <details class="td-preview__code">
28
- <summary>View source</summary>
29
- <pre><code>{source}</code></pre>
30
- </details>
31
- </figure>
38
+ <PreviewRoot.Code className="td-preview__code">
39
+ <PreviewRoot.Summary>View source</PreviewRoot.Summary>
40
+ <PreviewRoot.Pre><code>{source}</code></PreviewRoot.Pre>
41
+ </PreviewRoot.Code>
42
+ </PreviewRoot>
@@ -1 +1,5 @@
1
- <ol class="td-steps" data-tasty-anatomy="Steps"><slot /></ol>
1
+ ---
2
+ import { StepsRoot } from "./TastyComponents.js";
3
+ ---
4
+
5
+ <StepsRoot className="td-steps" data-tasty-anatomy="Steps"><slot /></StepsRoot>
@@ -1,10 +1,17 @@
1
1
  ---
2
+ import { TabsRoot } from "./TastyComponents.js";
3
+
2
4
  interface Props {
3
5
  label?: string;
4
6
  }
5
7
  const { label = "Examples" } = Astro.props;
6
8
  ---
7
9
 
8
- <div class="td-tabs" role="group" aria-label={label} data-tasty-anatomy="Tabs">
10
+ <TabsRoot
11
+ className="td-tabs"
12
+ role="group"
13
+ aria-label={label}
14
+ data-tasty-anatomy="Tabs"
15
+ >
9
16
  <slot />
10
- </div>
17
+ </TabsRoot>
@@ -0,0 +1,156 @@
1
+ import { tasty } from "@tenphi/tasty";
2
+ import { customizeComponent } from "./customize-component.js";
3
+ import { configureCookbookStates } from "./tasty-states.js";
4
+
5
+ configureCookbookStates();
6
+
7
+ export const CardRoot = customizeComponent(
8
+ "Card",
9
+ { as: "article" },
10
+ tasty({
11
+ as: "article",
12
+ styles: {
13
+ display: "grid",
14
+ gap: "1.5x",
15
+ padding: "2.5x",
16
+ color: "#text",
17
+ textDecoration: "none",
18
+ border: true,
19
+ radius: "1cr",
20
+ fill: "#surface-2",
21
+ shadow: "0 1px 2px #shadow",
22
+ transition: "fill 120ms ease, shadow 120ms ease, translate 120ms ease",
23
+
24
+ Heading2: {
25
+ $: "h2",
26
+ margin: "0",
27
+ },
28
+ Heading3: {
29
+ $: "h3",
30
+ margin: "0",
31
+ },
32
+ Paragraph: {
33
+ $: "p",
34
+ margin: "0",
35
+ },
36
+ },
37
+ }),
38
+ );
39
+
40
+ export const CardLink = tasty(CardRoot, {
41
+ as: "a",
42
+ styles: {
43
+ fill: {
44
+ "": "#surface-2",
45
+ ":hover": "#surface-2-hover",
46
+ ":active": "#surface-2-pressed",
47
+ },
48
+ shadow: {
49
+ "": "0 1px 2px #shadow",
50
+ ":hover": "0 .75x 2x #shadow",
51
+ },
52
+ translate: {
53
+ "": "0",
54
+ ":hover": "0 -1px",
55
+ ":active": "0",
56
+ },
57
+ },
58
+ });
59
+
60
+ const previewElements = {
61
+ Caption: "figcaption",
62
+ Stage: "div",
63
+ Frame: "iframe",
64
+ Code: "details",
65
+ Summary: "summary",
66
+ Pre: "pre",
67
+ };
68
+
69
+ export const PreviewRoot = customizeComponent(
70
+ "Preview",
71
+ { as: "figure", elements: previewElements },
72
+ tasty({
73
+ as: "figure",
74
+ styles: {
75
+ margin: "0",
76
+ overflow: "clip",
77
+ color: "#text",
78
+ border: true,
79
+ radius: "1cr",
80
+ fill: "#surface",
81
+ shadow: "0 1px 2px #shadow",
82
+
83
+ Caption: {
84
+ padding: "1.5x 2x",
85
+ preset: "small",
86
+ fill: "#surface-2",
87
+ },
88
+ Stage: {
89
+ padding: "3x",
90
+ fill: "#surface",
91
+ },
92
+ Frame: {
93
+ display: "block",
94
+ width: "100%",
95
+ height: "min 18rem",
96
+ border: "0",
97
+ fill: "#surface",
98
+ },
99
+ Code: {
100
+ border: "1bw top #border",
101
+ },
102
+ Summary: {
103
+ padding: "1.5x 2x",
104
+ preset: "small",
105
+ fill: "#surface-2",
106
+ cursor: "pointer",
107
+ },
108
+ Pre: {
109
+ margin: "0",
110
+ radius: "0",
111
+ },
112
+ },
113
+ elements: previewElements,
114
+ }),
115
+ );
116
+
117
+ export const StepsRoot = customizeComponent(
118
+ "Steps",
119
+ { as: "ol" },
120
+ tasty({
121
+ as: "ol",
122
+ styles: {
123
+ display: "grid",
124
+ gap: "2x",
125
+ padding: "4x left",
126
+ border: "1bw left #border",
127
+
128
+ Item: {
129
+ $: "> li",
130
+ padding: "1x left",
131
+ },
132
+ Marker: {
133
+ $: "> li::marker",
134
+ color: "#accent-text",
135
+ preset: "bold",
136
+ },
137
+ },
138
+ }),
139
+ );
140
+
141
+ export const TabsRoot = customizeComponent(
142
+ "Tabs",
143
+ { as: "div" },
144
+ tasty({
145
+ as: "div",
146
+ styles: {
147
+ display: "flex",
148
+ flow: "row wrap",
149
+ gap: "1x",
150
+ padding: "1x",
151
+ border: true,
152
+ radius: "1cr",
153
+ fill: "#surface-2",
154
+ },
155
+ }),
156
+ );
@@ -0,0 +1,75 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ import {
3
+ configureComponentStyles,
4
+ resolveLegacyAnatomyStyles,
5
+ resolveComponentStyleOverride,
6
+ resolveComponentStyles,
7
+ } from "./component-styles.js";
8
+
9
+ const defaults = {
10
+ color: "#text",
11
+ padding: "2x",
12
+ Label: { color: "#text-soft", fontWeight: 500 },
13
+ };
14
+
15
+ afterEach(() => configureComponentStyles(undefined));
16
+
17
+ describe("component style configuration", () => {
18
+ it("extends defaults with a plain Tasty style object", () => {
19
+ configureComponentStyles({
20
+ Card: { padding: "3x", Label: { color: "#accent-text" } },
21
+ });
22
+
23
+ expect(resolveComponentStyles("Card", defaults)).toEqual({
24
+ color: "#text",
25
+ padding: "3x",
26
+ Label: { color: "#accent-text", fontWeight: 500 },
27
+ });
28
+ });
29
+
30
+ it("supports explicit extension and complete replacement", () => {
31
+ configureComponentStyles({
32
+ Card: { mode: "extend", styles: { radius: "1cr" } },
33
+ });
34
+ expect(resolveComponentStyles("Card", defaults)).toMatchObject({
35
+ color: "#text",
36
+ radius: "1cr",
37
+ });
38
+
39
+ configureComponentStyles({
40
+ Card: { mode: "replace", styles: { display: "grid" } },
41
+ });
42
+ expect(resolveComponentStyles("Card", defaults)).toEqual({
43
+ display: "grid",
44
+ });
45
+ });
46
+
47
+ it("normalizes configured component overrides for Tasty composition", () => {
48
+ expect(resolveComponentStyleOverride("Card")).toBeUndefined();
49
+
50
+ configureComponentStyles({ Card: { padding: "3x" } });
51
+ expect(resolveComponentStyleOverride("Card")).toEqual({
52
+ mode: "extend",
53
+ styles: { padding: "3x" },
54
+ });
55
+
56
+ configureComponentStyles({
57
+ Card: { mode: "replace", styles: { display: "grid" } },
58
+ });
59
+ expect(resolveComponentStyleOverride("Card")).toEqual({
60
+ mode: "replace",
61
+ styles: { display: "grid" },
62
+ });
63
+ });
64
+
65
+ it("keeps custom anatomy styles on the compatibility bridge", () => {
66
+ expect(
67
+ resolveLegacyAnatomyStyles({
68
+ ThemeSelect: { padding: "1x" },
69
+ ProductBadge: { color: "#accent-text" },
70
+ }),
71
+ ).toEqual({
72
+ '[data-tasty-anatomy="ProductBadge"]': { color: "#accent-text" },
73
+ });
74
+ });
75
+ });
@@ -0,0 +1,79 @@
1
+ import { mergeStyles, type Styles } from "@tenphi/tasty/core";
2
+ import {
3
+ COOKBOOK_COMPONENT_NAMES,
4
+ type ComponentStyleConfig,
5
+ type ComponentStylesConfig,
6
+ type CookbookComponentName,
7
+ } from "@tenphi/docs";
8
+
9
+ // Astro can load the integration and renderer through separate module graphs.
10
+ // Keep their component configuration on the shared process global.
11
+ const sharedConfiguration = globalThis as typeof globalThis & {
12
+ __tenphiCookbookComponentStyles?: ComponentStylesConfig;
13
+ };
14
+ const cookbookComponentNames = new Set<string>(COOKBOOK_COMPONENT_NAMES);
15
+
16
+ export function configureComponentStyles(
17
+ styles: ComponentStylesConfig | undefined,
18
+ ): void {
19
+ sharedConfiguration.__tenphiCookbookComponentStyles = styles ?? {};
20
+ }
21
+
22
+ export function resolveComponentStyles(
23
+ name: CookbookComponentName,
24
+ defaults: Styles,
25
+ ): Styles {
26
+ const configured = sharedConfiguration.__tenphiCookbookComponentStyles?.[
27
+ name
28
+ ] as ComponentStyleConfig | undefined;
29
+ if (!configured) return defaults;
30
+ if (isModeConfig(configured)) {
31
+ const styles = configured.styles as Styles;
32
+ return configured.mode === "replace"
33
+ ? styles
34
+ : mergeStyles(defaults, styles);
35
+ }
36
+ return mergeStyles(defaults, configured as Styles);
37
+ }
38
+
39
+ export function resolveComponentStyleOverride(
40
+ name: CookbookComponentName,
41
+ ): { mode: "extend" | "replace"; styles: Styles } | undefined {
42
+ const configured = sharedConfiguration.__tenphiCookbookComponentStyles?.[
43
+ name
44
+ ] as ComponentStyleConfig | undefined;
45
+ if (!configured) return undefined;
46
+ if (isModeConfig(configured)) {
47
+ return { mode: configured.mode, styles: configured.styles as Styles };
48
+ }
49
+ return { mode: "extend", styles: configured as Styles };
50
+ }
51
+
52
+ /** Preserve custom anatomy names from the pre-component style API. */
53
+ export function resolveLegacyAnatomyStyles(
54
+ styles: ComponentStylesConfig | undefined,
55
+ ): Record<string, Styles> | undefined {
56
+ if (!styles) return undefined;
57
+ const entries = Object.entries(styles)
58
+ .filter(
59
+ (entry): entry is [string, ComponentStyleConfig] =>
60
+ !cookbookComponentNames.has(entry[0]) && entry[1] !== undefined,
61
+ )
62
+ .map(([name, value]) => [
63
+ `[data-tasty-anatomy="${name}"]`,
64
+ isModeConfig(value) ? value.styles : value,
65
+ ]);
66
+ return entries.length
67
+ ? (Object.fromEntries(entries) as Record<string, Styles>)
68
+ : undefined;
69
+ }
70
+
71
+ function isModeConfig(
72
+ value: ComponentStyleConfig,
73
+ ): value is Extract<ComponentStyleConfig, { mode: string }> {
74
+ return (
75
+ "mode" in value &&
76
+ (value.mode === "extend" || value.mode === "replace") &&
77
+ "styles" in value
78
+ );
79
+ }
@@ -0,0 +1,10 @@
1
+ import { tasty } from "@tenphi/tasty";
2
+ import { resolveComponentStyleOverride } from "./component-styles.js";
3
+
4
+ export function customizeComponent(name, options, base) {
5
+ const override = resolveComponentStyleOverride(name);
6
+ if (!override) return base;
7
+ return override.mode === "replace"
8
+ ? tasty({ ...options, styles: override.styles })
9
+ : tasty(base, { styles: override.styles });
10
+ }
@@ -0,0 +1,11 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { svgIconUrl } from "./svg-icon.js";
3
+
4
+ describe("svgIconUrl", () => {
5
+ it("preserves case-sensitive SVG data when Tasty lowercases the style", () => {
6
+ const svg = '<svg viewBox="0 0 24 24"><path d="M1 2H3Z"/></svg>';
7
+ const [, payload] = svgIconUrl(svg).toLowerCase().split(",", 2);
8
+
9
+ expect(decodeURIComponent(payload ?? "")).toBe(svg);
10
+ });
11
+ });
@@ -0,0 +1,11 @@
1
+ /** Encode an SVG data URL that remains valid after Tasty lowercases styles. */
2
+ export function svgIconUrl(svg: string): string {
3
+ const encoded = encodeURIComponent(svg).replace(
4
+ /%[0-9A-F]{2}|[A-Z]/g,
5
+ (token) =>
6
+ token.startsWith("%")
7
+ ? token.toLowerCase()
8
+ : `%${token.charCodeAt(0).toString(16)}`,
9
+ );
10
+ return `data:image/svg+xml,${encoded}`;
11
+ }
@@ -0,0 +1,2 @@
1
+ export const cookbookStates: Record<`@${string}`, string>;
2
+ export function configureCookbookStates(): void;
@@ -0,0 +1,21 @@
1
+ import { configure } from "@tenphi/tasty";
2
+
3
+ export const cookbookStates = {
4
+ "@mobile": "@media(w < 50rem)",
5
+ "@desktop": "@media(w >= 50rem)",
6
+ "@small": "@media(w <= 40rem)",
7
+ "@shell-mobile": "@media(w <= 48rem)",
8
+ "@shell-desktop": "@media(w > 48rem)",
9
+ "@narrow-layout": "@media(w < 72rem)",
10
+ "@medium-layout": "@media(w >= 50rem) & @media(w < 72rem)",
11
+ "@reduced-motion": "@media(prefers-reduced-motion: reduce)",
12
+ };
13
+
14
+ let configured = false;
15
+
16
+ /** Configure aliases in the renderer's Tasty module before styles are parsed. */
17
+ export function configureCookbookStates() {
18
+ if (configured) return;
19
+ configure({ states: cookbookStates });
20
+ configured = true;
21
+ }
@@ -1,6 +1,7 @@
1
1
  import type { AstroComponentFactory } from "astro/runtime/server/index.js";
2
2
 
3
3
  export const Card: AstroComponentFactory;
4
+ export const Logo: AstroComponentFactory;
4
5
  export const Preview: AstroComponentFactory;
5
6
  export const Steps: AstroComponentFactory;
6
7
  export const Tabs: AstroComponentFactory;
@@ -1,5 +1,6 @@
1
1
  import Card from "./components/Card.astro";
2
+ import Logo from "./components/Logo.astro";
2
3
  import Preview from "./components/Preview.astro";
3
4
  import Steps from "./components/Steps.astro";
4
5
  import Tabs from "./components/Tabs.astro";
5
- export { Card, Preview, Steps, Tabs };
6
+ export { Card, Logo, Preview, Steps, Tabs };
@@ -1,5 +1,6 @@
1
1
  import Card from "./components/Card.astro";
2
+ import Logo from "./components/Logo.astro";
2
3
  import Preview from "./components/Preview.astro";
3
4
  import Steps from "./components/Steps.astro";
4
5
  import Tabs from "./components/Tabs.astro";
5
- export { Card, Preview, Steps, Tabs };
6
+ export { Card, Logo, Preview, Steps, Tabs };
package/dist/index.d.ts CHANGED
@@ -22,7 +22,6 @@ declare function tastyStarlight(config: Parameters<typeof starlight>[0]): AstroI
22
22
  //#endregion
23
23
  //#region src/theme/index.d.ts
24
24
  interface ResolvedDocsTheme {
25
- css: string;
26
25
  colors: {
27
26
  surface: Record<string, string>;
28
27
  surface2: Record<string, string>;
@@ -33,6 +32,7 @@ interface ResolvedDocsTheme {
33
32
  accentSurface: Record<string, string>;
34
33
  accentSurfaceText: Record<string, string>;
35
34
  focus: Record<string, string>;
35
+ shadow: Record<string, string>;
36
36
  };
37
37
  tokens: ThemeTokens;
38
38
  presets: Record<string, TypographyPreset>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/starlight-runtime.d.ts","../src/integration.ts","../src/theme/index.ts","../src/theme/defaults.ts"],"mappings":";;;;UAEiB;EACf;EACA;EACA;EACA,mBAAmB;EACnB;GACC;;iBAGqB,UAAU,SAAS,mBAAmB;;;UCqB7C;EACf,SAAS;EACT;;iBAGsB,SACtB,UAAS,kBACR;iBAsZa,eACd,QAAQ,kBAAkB,gBACzB;;;UC7ac;EACf;EACA;IACE,SAAS;IACT,UAAU;IACV,UAAU;IACV,MAAM;IACN,UAAU;IACV,YAAY;IACZ,eAAe;IACf,mBAAmB;IACnB,OAAO;;EAET,QAAQ;EACR,SAAS,eAAe;EACxB;IACE;IACA;IACA;IACA;;EAEF,aAAa;;iBAGC,iBAAiB,QAAO,cAAmB;;;cCpC9C;;;;;;;;;;;;cAkBA,4BAA4B,eAAe"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/starlight-runtime.d.ts","../src/integration.ts","../src/theme/index.ts","../src/theme/defaults.ts"],"mappings":";;;;UAEiB;EACf;EACA;EACA;EACA,mBAAmB;EACnB;GACC;;iBAGqB,UAAU,SAAS,mBAAmB;;;UCoC7C;EACf,SAAS;EACT;;iBAGsB,SACtB,UAAS,kBACR;iBAsaa,eACd,QAAQ,kBAAkB,gBACzB;;;UC5cc;EACf;IACE,SAAS;IACT,UAAU;IACV,UAAU;IACV,MAAM;IACN,UAAU;IACV,YAAY;IACZ,eAAe;IACf,mBAAmB;IACnB,OAAO;IACP,QAAQ;;EAEV,QAAQ;EACR,SAAS,eAAe;EACxB;IACE;IACA;IACA;IACA;;EAEF,aAAa;;iBAGC,iBAAiB,QAAO,cAAmB;;;cCpC9C;;;;;;;;;;;;cAkBA,4BAA4B,eAAe"}